mirror of https://github.com/apache/openjpa.git
OPENJPA-687: Add read/write/hit statitics to DataCache/StoreCache
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@684132 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
2a5964a540
commit
0d3812fcb5
|
@ -47,11 +47,14 @@ public abstract class AbstractDataCache
|
|||
extends AbstractConcurrentEventManager
|
||||
implements DataCache, Configurable {
|
||||
|
||||
protected CacheStatistics.Default stats = new CacheStatistics.Default();
|
||||
|
||||
private static final BitSet EMPTY_BITSET = new BitSet(0);
|
||||
|
||||
private static final Localizer s_loc =
|
||||
Localizer.forPackage(AbstractDataCache.class);
|
||||
|
||||
|
||||
/**
|
||||
* The configuration set by the system.
|
||||
*/
|
||||
|
@ -152,19 +155,30 @@ public abstract class AbstractDataCache
|
|||
if (log.isTraceEnabled())
|
||||
log.trace(s_loc.get("cache-timeout", key));
|
||||
}
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
if (o == null)
|
||||
log.trace(s_loc.get("cache-miss", key));
|
||||
else
|
||||
log.trace(s_loc.get("cache-hit", key));
|
||||
}
|
||||
|
||||
stats.newGet((o == null) ? null : o.getType(), o != null);
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the objects for the given key List.
|
||||
*/
|
||||
public Map getAll(List keys) {
|
||||
Map resultMap = new HashMap(keys.size());
|
||||
for(Object key : keys)
|
||||
resultMap.put(key, get(key));
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
public DataCachePCData put(DataCachePCData data) {
|
||||
DataCachePCData o = putInternal(data.getId(), data);
|
||||
stats.newPut((o == null) ? null : o.getType());
|
||||
if (log.isTraceEnabled())
|
||||
log.trace(s_loc.get("cache-put", data.getId()));
|
||||
return (o == null || o.isTimedOut()) ? null : o;
|
||||
|
@ -355,12 +369,13 @@ public abstract class AbstractDataCache
|
|||
DataCachePCData pc);
|
||||
|
||||
/**
|
||||
* All all of the given objects to the cache.
|
||||
* Add all of the given objects to the cache.
|
||||
*/
|
||||
protected void putAllInternal(Collection pcs) {
|
||||
DataCachePCData pc;
|
||||
for (Iterator iter = pcs.iterator(); iter.hasNext();) {
|
||||
pc = (DataCachePCData) iter.next();
|
||||
stats.newPut(pc.getType());
|
||||
putInternal(pc.getId(), pc);
|
||||
}
|
||||
}
|
||||
|
@ -415,6 +430,10 @@ public abstract class AbstractDataCache
|
|||
*/
|
||||
protected abstract boolean unpinInternal(Object oid);
|
||||
|
||||
public CacheStatistics getStatistics() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
// ---------- Configurable implementation ----------
|
||||
|
||||
public void setConfiguration(Configuration conf) {
|
||||
|
@ -442,14 +461,4 @@ public abstract class AbstractDataCache
|
|||
log.warn(s_loc.get("exp-listener-ex"), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the objects for the given key List.
|
||||
*/
|
||||
public Map getAll(List keys) {
|
||||
Map resultMap = new HashMap(keys.size());
|
||||
for(int i=0; i<keys.size(); i++)
|
||||
resultMap.put(keys.get(i), get(keys.get(i)));
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openjpa.datacache;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Counts number of read/write requests and hit ratio for a cache in total and
|
||||
* per-class basis.
|
||||
*
|
||||
* All methods with Class as input argument treats null as
|
||||
* <code>java.lang.Object</code>. All per-class statistics depends on
|
||||
* determining the runtime type of the instance being cached. If it is not
|
||||
* possible to determine the runtime type from the given context, the statistics
|
||||
* is registered under generic <code>java.lang.Object</code>.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public interface CacheStatistics extends Serializable {
|
||||
/**
|
||||
* Gets number of total read requests since last reset.
|
||||
*/
|
||||
public long getReadCount();
|
||||
|
||||
/**
|
||||
* Gets number of total read requests that has been found in cache since
|
||||
* last reset.
|
||||
*/
|
||||
public long getHitCount();
|
||||
|
||||
/**
|
||||
* Gets number of total write requests since last reset.
|
||||
*/
|
||||
public long getWriteCount();
|
||||
|
||||
/**
|
||||
* Gets number of total read requests since start.
|
||||
*/
|
||||
public long getTotalReadCount();
|
||||
|
||||
/**
|
||||
* Gets number of total read requests that has been found in cache since
|
||||
* start.
|
||||
*/
|
||||
public long getTotalHitCount();
|
||||
|
||||
/**
|
||||
* Gets number of total write requests since start.
|
||||
*/
|
||||
public long getTotalWriteCount();
|
||||
|
||||
/**
|
||||
* Gets number of total read requests for the given class since last reset.
|
||||
*/
|
||||
public long getReadCount(Class c);
|
||||
|
||||
/**
|
||||
* Gets number of total read requests that has been found in cache for the
|
||||
* given class since last reset.
|
||||
*/
|
||||
public long getHitCount(Class c);
|
||||
|
||||
/**
|
||||
* Gets number of total write requests for the given class since last reset.
|
||||
*/
|
||||
public long getWriteCount(Class c);
|
||||
|
||||
/**
|
||||
* Gets number of total read requests for the given class since start.
|
||||
*/
|
||||
public long getTotalReadCount(Class c);
|
||||
|
||||
/**
|
||||
* Gets number of total read requests that has been found in cache for the
|
||||
* given class since start.
|
||||
*/
|
||||
public long getTotalHitCount(Class c);
|
||||
|
||||
/**
|
||||
* Gets number of total write requests for the given class since start.
|
||||
*/
|
||||
public long getTotalWriteCount(Class c);
|
||||
|
||||
/**
|
||||
* Gets the time of last reset.
|
||||
*/
|
||||
public Date since();
|
||||
|
||||
/**
|
||||
* Gets the time of start.
|
||||
*/
|
||||
public Date start();
|
||||
|
||||
/**
|
||||
* Clears all accumulated statistics.
|
||||
*/
|
||||
public void reset();
|
||||
|
||||
/**
|
||||
* A default implementation.
|
||||
*
|
||||
*/
|
||||
public static class Default implements CacheStatistics {
|
||||
private long[] astat = new long[3];
|
||||
private long[] stat = new long[3];
|
||||
private Map<Class, long[]> stats = new HashMap<Class, long[]>();
|
||||
private Map<Class, long[]> astats = new HashMap<Class, long[]>();
|
||||
private Date start = new Date();
|
||||
private Date since = new Date();
|
||||
|
||||
private static final int READ = 0;
|
||||
private static final int HIT = 1;
|
||||
private static final int WRITE = 2;
|
||||
|
||||
public long getReadCount() {
|
||||
return stat[READ];
|
||||
}
|
||||
|
||||
public long getHitCount() {
|
||||
return stat[HIT];
|
||||
}
|
||||
|
||||
public long getWriteCount() {
|
||||
return stat[WRITE];
|
||||
}
|
||||
|
||||
public long getTotalReadCount() {
|
||||
return astat[READ];
|
||||
}
|
||||
|
||||
public long getTotalHitCount() {
|
||||
return astat[HIT];
|
||||
}
|
||||
|
||||
public long getTotalWriteCount() {
|
||||
return astat[WRITE];
|
||||
}
|
||||
|
||||
public long getReadCount(Class c) {
|
||||
return getCount(stats, c, READ);
|
||||
}
|
||||
|
||||
public long getHitCount(Class c) {
|
||||
return getCount(stats, c, HIT);
|
||||
}
|
||||
|
||||
public long getWriteCount(Class c) {
|
||||
return getCount(stats, c, WRITE);
|
||||
}
|
||||
|
||||
public long getTotalReadCount(Class c) {
|
||||
return getCount(astats, c, READ);
|
||||
}
|
||||
|
||||
public long getTotalHitCount(Class c) {
|
||||
return getCount(astats, c, HIT);
|
||||
}
|
||||
|
||||
public long getTotalWriteCount(Class c) {
|
||||
return getCount(astats, c, WRITE);
|
||||
}
|
||||
|
||||
private long getCount(Map<Class, long[]> target, Class c, int index) {
|
||||
long[] row = target.get(c);
|
||||
return (row == null) ? 0 : row[WRITE];
|
||||
}
|
||||
|
||||
public Date since() {
|
||||
return since;
|
||||
}
|
||||
|
||||
public Date start() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
stat = new long[3];
|
||||
stats.clear();
|
||||
since = new Date();
|
||||
}
|
||||
|
||||
void newGet(Class cls, boolean hit) {
|
||||
cls = (cls == null) ? Object.class : cls;
|
||||
addSample(cls, READ);
|
||||
if (hit) {
|
||||
addSample(cls, HIT);
|
||||
}
|
||||
}
|
||||
|
||||
void newPut(Class cls) {
|
||||
cls = (cls == null) ? Object.class : cls;
|
||||
addSample(cls, WRITE);
|
||||
}
|
||||
|
||||
private void addSample(Class c, int index) {
|
||||
stat[index]++;
|
||||
astat[index]++;
|
||||
addSample(stats, c, index);
|
||||
addSample(astats, c, index);
|
||||
}
|
||||
|
||||
private void addSample(Map<Class, long[]> target, Class c, int index) {
|
||||
long[] row = target.get(c);
|
||||
if (row == null) {
|
||||
row = new long[3];
|
||||
}
|
||||
row[index]++;
|
||||
target.put(c, row);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -264,4 +264,9 @@ public interface DataCache
|
|||
* returns objects from the caches for a given list of keys
|
||||
*/
|
||||
public Map getAll(List keys);
|
||||
|
||||
/**
|
||||
* Returns number of read/write request and cache hit ratio data.
|
||||
*/
|
||||
public CacheStatistics getStatistics();
|
||||
}
|
||||
|
|
|
@ -345,4 +345,8 @@ public class DelegatingDataCache
|
|||
throw translate(re);
|
||||
}
|
||||
}
|
||||
|
||||
public CacheStatistics getStatistics() {
|
||||
return (_cache == null) ? null : _cache.getStatistics();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openjpa.persistence.datacache.stats;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.apache.openjpa.datacache.CacheStatistics;
|
||||
import org.apache.openjpa.datacache.DataCache;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.persistence.EntityManagerImpl;
|
||||
import org.apache.openjpa.persistence.jdbc.query.domain.Customer;
|
||||
import org.apache.openjpa.persistence.jdbc.query.domain.Order;
|
||||
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||
|
||||
public class TestStatistics extends SingleEMFTestCase {
|
||||
private CacheStatistics stats;
|
||||
|
||||
public void setUp() {
|
||||
super.setUp(CLEAR_TABLES, Customer.class, Order.class,
|
||||
"openjpa.DataCache", "true", "openjpa.RemoteCommitProvider",
|
||||
"sjvm", "openjpa.Log", "DefaultLevel=WARN");
|
||||
startCaching(Customer.class);
|
||||
startCaching(Order.class);
|
||||
assertTrue(((EntityManagerImpl) emf.createEntityManager()).getBroker()
|
||||
.getPopulateDataCache());
|
||||
stats = emf.getStoreCache().getStatistics();
|
||||
assertNotNull(stats);
|
||||
}
|
||||
|
||||
void startCaching(Class cls) {
|
||||
ClassMetaData meta = emf.getConfiguration()
|
||||
.getMetaDataRepositoryInstance().getMetaData(cls, null, true);
|
||||
meta.setDataCacheName(DataCache.NAME_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that statistics captures correct data under perfect caching
|
||||
* condition.
|
||||
*/
|
||||
public void testPerfectCache() {
|
||||
print(stats);
|
||||
// populate a bunch of customer and order
|
||||
int nCustomer = 20;
|
||||
int nOrder = 10;
|
||||
EntityManager em = emf.createEntityManager();
|
||||
em.getTransaction().begin();
|
||||
for (int j = 0; j < nCustomer; j++) {
|
||||
Customer customer = new Customer();
|
||||
customer.setName("Customer-" + j);
|
||||
for (int k = 0; k < nOrder; k++) {
|
||||
Order order = new Order();
|
||||
order.setAmount(100);
|
||||
customer.addOrder(order);
|
||||
em.persist(order);
|
||||
}
|
||||
em.persist(customer);
|
||||
}
|
||||
em.getTransaction().commit();
|
||||
em.clear();
|
||||
|
||||
// print(stats);
|
||||
|
||||
em.getTransaction().begin();
|
||||
Query query = em.createQuery("select c from Customer c");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
em.clear();
|
||||
stats.reset();
|
||||
List<Customer> result = query.getResultList();
|
||||
for (Customer c : result) {
|
||||
c.getOrders();
|
||||
}
|
||||
// print(stats);
|
||||
assertEquals(stats.getReadCount(), stats.getHitCount());
|
||||
assertEquals(0, stats.getWriteCount());
|
||||
}
|
||||
}
|
||||
|
||||
void assertStatistics(CacheStatistics stats, long[] expected) {
|
||||
assertEquals(expected[0], stats.getReadCount());
|
||||
assertEquals(expected[1], stats.getHitCount());
|
||||
assertEquals(expected[2], stats.getWriteCount());
|
||||
}
|
||||
|
||||
void print(CacheStatistics stats) {
|
||||
System.err
|
||||
.print("r:" + stats.getTotalReadCount() + " w:"
|
||||
+ stats.getTotalWriteCount() + " h:"
|
||||
+ stats.getTotalHitCount());
|
||||
System.err.println(" since last reset r:" + stats.getReadCount()
|
||||
+ " w:" + stats.getWriteCount() + " h:" + stats.getHitCount());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openjpa.persistence.jdbc.query.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
@Entity
|
||||
public class Customer {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy="customer")
|
||||
private Collection<Order> orders;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Collection<Order> getOrders() {
|
||||
return orders;
|
||||
}
|
||||
|
||||
public void addOrder(Order order) {
|
||||
if (orders == null)
|
||||
orders = new ArrayList<Order>();
|
||||
this.orders.add(order);
|
||||
order.setCustomer(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openjpa.persistence.jdbc.query.domain;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name="ORDERS")
|
||||
public class Order {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
private int amount;
|
||||
|
||||
@ManyToOne
|
||||
private Customer customer;
|
||||
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ package org.apache.openjpa.persistence;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.openjpa.datacache.CacheStatistics;
|
||||
import org.apache.openjpa.datacache.DataCache;
|
||||
|
||||
/**
|
||||
|
@ -97,6 +98,14 @@ public interface StoreCache {
|
|||
*/
|
||||
public void evictAll();
|
||||
|
||||
/**
|
||||
* Gets the number of read/write/hit on this receiver in total and per
|
||||
* class basis.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public CacheStatistics getStatistics();
|
||||
|
||||
/**
|
||||
* @deprecated cast to {@link StoreCacheImpl} instead. This
|
||||
* method pierces the published-API boundary, as does the SPI cast.
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.Arrays;
|
|||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.openjpa.datacache.CacheStatistics;
|
||||
import org.apache.openjpa.datacache.DataCache;
|
||||
import org.apache.openjpa.datacache.DelegatingDataCache;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
|
@ -130,6 +131,10 @@ public class StoreCacheImpl
|
|||
_cache.clear();
|
||||
}
|
||||
|
||||
public CacheStatistics getStatistics() {
|
||||
return (_cache == null) ? null : _cache.getStatistics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return metadata for the given class, throwing the proper exception
|
||||
* if not persistent.
|
||||
|
|
Loading…
Reference in New Issue