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:
Pinaki Poddar 2008-08-08 22:39:39 +00:00
parent 2a5964a540
commit 0d3812fcb5
9 changed files with 512 additions and 15 deletions

View File

@ -46,11 +46,14 @@ import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager;
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;
@ -352,15 +366,16 @@ public abstract class AbstractDataCache
* given oid.
*/
protected abstract DataCachePCData putInternal(Object oid,
DataCachePCData pc);
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);
}
}
@ -414,6 +429,10 @@ public abstract class AbstractDataCache
* Unpin an object from the cache.
*/
protected abstract boolean unpinInternal(Object oid);
public CacheStatistics getStatistics() {
return stats;
}
// ---------- Configurable implementation ----------
@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}

View File

@ -345,4 +345,8 @@ public class DelegatingDataCache
throw translate(re);
}
}
public CacheStatistics getStatistics() {
return (_cache == null) ? null : _cache.getStatistics();
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
/**
@ -96,6 +97,14 @@ public interface StoreCache {
* Clear the cache.
*/
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

View File

@ -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;
@ -129,6 +130,10 @@ public class StoreCacheImpl
public void evictAll() {
_cache.clear();
}
public CacheStatistics getStatistics() {
return (_cache == null) ? null : _cache.getStatistics();
}
/**
* Return metadata for the given class, throwing the proper exception