From 54bef663e7d9f8ddec9e5434ca1162b09662bcab Mon Sep 17 00:00:00 2001 From: "Richard G. Curtis" Date: Tue, 5 Oct 2010 21:12:52 +0000 Subject: [PATCH] OPENJPA-1801: Refactor cache statistics. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1004818 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/datacache/AbstractDataCache.java | 39 +-- .../openjpa/datacache/CacheStatistics.java | 175 +---------- .../datacache/CacheStatisticsImpl.java | 190 ++++++++++++ .../openjpa/datacache/CacheStatisticsSPI.java | 70 +++++ .../datacache/DataCacheStoreManager.java | 89 +++++- .../AbstractDataCacheInstrument.java | 40 --- .../instrumentation/DataCacheInstrument.java | 12 +- .../persistence/datacache/TestStatistics.java | 274 +++++++++++++++--- 8 files changed, 588 insertions(+), 301 deletions(-) create mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatisticsImpl.java create mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatisticsSPI.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractDataCache.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractDataCache.java index 3fbf5f77c..508bb04de 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractDataCache.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/AbstractDataCache.java @@ -34,12 +34,16 @@ import org.apache.commons.lang.StringUtils; import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.event.RemoteCommitEvent; import org.apache.openjpa.event.RemoteCommitListener; +import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.lib.conf.Configurable; import org.apache.openjpa.lib.conf.Configuration; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager; import org.apache.openjpa.util.GeneralException; +import org.apache.openjpa.util.InternalException; +import org.apache.openjpa.util.OpenJPAId; + import serp.util.Strings; /** @@ -54,7 +58,7 @@ import serp.util.Strings; public abstract class AbstractDataCache extends AbstractConcurrentEventManager implements DataCache, Configurable { - protected CacheStatistics.Default stats = new CacheStatistics.Default(); + protected CacheStatisticsSPI _stats = new CacheStatisticsImpl(); private static final BitSet EMPTY_BITSET = new BitSet(0); @@ -86,11 +90,11 @@ public abstract class AbstractDataCache extends AbstractConcurrentEventManager } public void setEnableStatistics(boolean enable){ if(enable == true){ - stats.enable(); + _stats.enable(); } } public void getEnableStatistics(){ - stats.isEnabled(); + _stats.isEnabled(); } public String getEvictionSchedule() { @@ -157,9 +161,6 @@ public abstract class AbstractDataCache extends AbstractConcurrentEventManager public boolean contains(Object key) { DataCachePCData o = getInternal(key); - if (stats.isEnabled()) { - stats.newGet(o == null ? null : o.getType(), o != null); - } if (o != null && o.isTimedOut()) { o = null; removeInternal(key); @@ -195,9 +196,7 @@ public abstract class AbstractDataCache extends AbstractConcurrentEventManager else log.trace(s_loc.get("cache-hit", key)); } - if (stats.isEnabled()) { - stats.newGet((o == null) ? null : o.getType(), o != null); - } + return o; } @@ -213,9 +212,6 @@ public abstract class AbstractDataCache extends AbstractConcurrentEventManager } public DataCachePCData put(DataCachePCData data) { - if (stats.isEnabled()) { - stats.newPut(data.getType()); - } DataCachePCData o = putInternal(data.getId(), data); if (log.isTraceEnabled()) log.trace(s_loc.get("cache-put", data.getId())); @@ -224,18 +220,12 @@ public abstract class AbstractDataCache extends AbstractConcurrentEventManager public void update(DataCachePCData data) { if (recacheUpdates()) { - if (stats.isEnabled()) { - stats.newPut(data.getType()); - } putInternal(data.getId(), data); } } public DataCachePCData remove(Object key) { DataCachePCData o = removeInternal(key); - if (stats.isEnabled()) { - stats.newEvict(o == null ? null : o.getType()); - } if (o != null && o.isTimedOut()) o = null; if (log.isTraceEnabled()) { @@ -418,9 +408,6 @@ public abstract class AbstractDataCache extends AbstractConcurrentEventManager */ protected void putAllInternal(Collection pcs) { for (DataCachePCData pc : pcs) { - if (stats.isEnabled()) { - stats.newPut(pc.getType()); - } putInternal(pc.getId(), pc); } } @@ -492,9 +479,9 @@ public abstract class AbstractDataCache extends AbstractConcurrentEventManager public boolean isPartitioned() { return false; } - - public CacheStatistics getStatistics() { - return stats; + + public CacheStatistics getStatistics() { + return _stats; } // ---------- Configurable implementation ---------- @@ -550,4 +537,8 @@ public abstract class AbstractDataCache extends AbstractConcurrentEventManager _excludedTypes = StringUtils.isEmpty(types) ? null : new HashSet(Arrays.asList(Strings.split(types, ";", 0))); } + + public DataCache selectCache(OpenJPAStateManager sm) { + return this; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatistics.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatistics.java index a8292231f..c0ee8b782 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatistics.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatistics.java @@ -24,6 +24,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +import org.apache.openjpa.util.OpenJPAId; + /** * Counts number of read/write requests and hit ratio for a cache in total and * per-class basis. @@ -35,11 +37,9 @@ import java.util.Set; * is registered under generic java.lang.Object. * * @since 1.3.0 - * - * @author Pinaki Poddar - * */ public interface CacheStatistics extends Serializable { + /** * Gets number of total read requests since last reset. */ @@ -104,26 +104,6 @@ public interface CacheStatistics extends Serializable { */ public long getTotalWriteCount(Class c); - /** - * Gets number of total evictions since last reset. - */ - public long getEvictionCount(); - - /** - * Gets number of total evictions for the given class since last reset. - */ - public long getEvictionCount(Class c); - - /** - * Gets number of total evictions in cache since start. - */ - public long getTotalEvictionCount(); - - /** - * Gets number of total evictions for the given class since start. - */ - public long getTotalEvictionCount(Class c); - /** * Gets the time of last reset. */ @@ -149,154 +129,5 @@ public interface CacheStatistics extends Serializable { * @return */ public Set> classNames(); - - /** - * A default implementation. - * - */ - public static class Default implements CacheStatistics { - private static final int ARRAY_SIZE = 4; - private long[] astat = new long[ARRAY_SIZE]; - private long[] stat = new long[ARRAY_SIZE]; - private Map, long[]> stats = new HashMap, long[]>(); - private Map, long[]> astats = new HashMap, long[]>(); - private Date start = new Date(); - private Date since = new Date(); - private boolean enabled = false; - - private static final int READ = 0; - private static final int HIT = 1; - private static final int WRITE = 2; - private static final int EVICT = 3; - - public long getReadCount() { - return stat[READ]; - } - - public long getHitCount() { - return stat[HIT]; - } - - public long getWriteCount() { - return stat[WRITE]; - } - - public long getEvictionCount() { - return stat[EVICT]; - } - - public long getTotalReadCount() { - return astat[READ]; - } - - public long getTotalHitCount() { - return astat[HIT]; - } - - public long getTotalWriteCount() { - return astat[WRITE]; - } - - public long getTotalEvictionCount() { - return astat[EVICT]; - } - - 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 getEvictionCount(Class c) { - return getCount(stats, c, EVICT); - } - - 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); - } - - public long getTotalEvictionCount(Class c) { - return getCount(astats, c, EVICT); - } - - private long getCount(Map, long[]> target, Class c, int index) { - long[] row = target.get(c); - return (row == null) ? 0 : row[index]; - } - - public Date since() { - return since; - } - - public Date start() { - return start; - } - - public void reset() { - stat = new long[ARRAY_SIZE]; - stats.clear(); - since = new Date(); - } - - public boolean isEnabled() { - return enabled; - } - void enable(){ - enabled = true; - } - void disable() { - enabled = false; - } - 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); - } - - void newEvict(Class cls) { - cls = (cls == null) ? Object.class : cls; - addSample(cls, EVICT); - } - - private void addSample(Class c, int index) { - stat[index]++; - astat[index]++; - addSample(stats, c, index); - addSample(astats, c, index); - } - - private void addSample(Map, long[]> target, Class c, int index) { - long[] row = target.get(c); - if (row == null) { - row = new long[ARRAY_SIZE]; - } - row[index]++; - target.put(c, row); - } - public Set> classNames() { - return astats.keySet(); - } - } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatisticsImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatisticsImpl.java new file mode 100644 index 000000000..89e719548 --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatisticsImpl.java @@ -0,0 +1,190 @@ +/* + * 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.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.openjpa.util.OpenJPAId; + +/** + * The default CacheStatistics(SPI) implementation. + */ +public class CacheStatisticsImpl implements CacheStatisticsSPI { + private static final long serialVersionUID = 9014495759588003166L; + private static final int ARRAY_SIZE = 3; + private long[] astat = new long[ARRAY_SIZE]; + private long[] stat = new long[ARRAY_SIZE]; + private Map, long[]> stats = new HashMap, long[]>(); + private Map, long[]> astats = new HashMap, long[]>(); + private Date start = new Date(); + private Date since = new Date(); + private boolean enabled = false; + + 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); + } + + public Date since() { + return since; + } + + public Date start() { + return start; + } + + public void reset() { + stat = new long[ARRAY_SIZE]; + stats.clear(); + since = new Date(); + } + + public boolean isEnabled() { + return enabled; + } + + public Set> classNames() { + return astats.keySet(); + } + + /** + * SPI implementation + */ + public void enable() { + enabled = true; + } + + public void disable() { + enabled = false; + } + + public void newGet(Class cls, boolean hit) { + if (!enabled) { + return; + } + if (cls == null) { + throw new RuntimeException("Snap! newGet will null cls Name"); + } + cls = (cls == null) ? Object.class : cls; + addSample(cls, READ); + if (hit) { + addSample(cls, HIT); + } + } + + public void newGet(Object oid, boolean hit) { + if (!enabled) { + return; + } + if (oid instanceof OpenJPAId) { + newGet(((OpenJPAId) oid).getType(), hit); + } + } + + public void newPut(Class cls) { + if (!enabled) { + return; + } + cls = (cls == null) ? Object.class : cls; + addSample(cls, WRITE); + } + + public void newPut(Object oid) { + if (!enabled) { + return; + } + if (oid instanceof OpenJPAId) { + newPut(((OpenJPAId) oid).getType()); + } + } + + /** + * Private worker methods. + */ + private void addSample(Class c, int index) { + stat[index]++; + astat[index]++; + addSample(stats, c, index); + addSample(astats, c, index); + } + + private void addSample(Map, long[]> target, Class c, int index) { + long[] row = target.get(c); + if (row == null) { + row = new long[ARRAY_SIZE]; + } + row[index]++; + target.put(c, row); + } + + private long getCount(Map, long[]> target, Class c, int index) { + long[] row = target.get(c); + return (row == null) ? 0 : row[index]; + } +} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatisticsSPI.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatisticsSPI.java new file mode 100644 index 000000000..1eac31abc --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/CacheStatisticsSPI.java @@ -0,0 +1,70 @@ +/* + * 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; + +/** + * The provider extensions to the CacheStatistics interface. + */ +public interface CacheStatisticsSPI extends CacheStatistics { + /** + * Record a new cache get. + * + * @param cls + * - The class describing the type that is contained in the cache. + * @param hit + * - true for a cache hit, false otherwise + */ + public void newGet(Class cls, boolean hit); + + /** + * Record a new cache get. + * + * @param oid + * - The cache key. + * @param hit + * - true for a cache hit, false otherwise + */ + public void newGet(Object oid, boolean hit); + + /** + * Record a new cache put. + * + * @param cls + * - The class describing the type that is contained in the cache. + */ + public void newPut(Class cls); + + /** + * Record a new cache put. + * + * @param oid + * - The cache key. + */ + public void newPut(Object oid); + + /** + * Enable statistics collection. + */ + public void enable(); + + /** + * Disable statistics collection. + */ + public void disable(); +} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheStoreManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheStoreManager.java index 1c7843e89..86369c77e 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheStoreManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/DataCacheStoreManager.java @@ -147,6 +147,10 @@ public class DataCacheStoreManager data = newPCData(sm); data.store(sm); mods.additions.add(new PCDataHolder(data, sm)); + CacheStatistics stats = cache.getStatistics(); + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newPut(sm.getMetaData().getDescribedType()); + } } } @@ -182,6 +186,10 @@ public class DataCacheStoreManager data.store(sm, fields); mods.existingUpdates.add(new PCDataHolder(data, sm)); } + CacheStatistics stats = cache.getStatistics(); + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newPut(sm.getMetaData().getDescribedType()); + } } } @@ -272,9 +280,22 @@ public class DataCacheStoreManager } public boolean exists(OpenJPAStateManager sm, Object edata) { - DataCache cache = _mgr.selectCache(sm); - if (cache != null && !isLocking(null) && cache.contains(sm.getObjectId())) + DataCache cache = _mgr.selectCache(sm); + CacheStatistics stats = cache.getStatistics(); + if (cache != null && !isLocking(null) && cache.contains(sm.getObjectId())){ + if (stats.isEnabled()) { + // delay this call ONLY if stats collection is enabled + Class cls = sm.getMetaData().getDescribedType(); + ((CacheStatisticsSPI)stats).newGet(cls, false); + } return true; + } + // If isLocking(null)==true && cache.contains(..) == true... probably shouldn't count? + if (stats.isEnabled()) { + // delay this call ONLY if stats collection is enabled + Class cls = sm.getMetaData().getDescribedType(); + ((CacheStatisticsSPI)stats).newGet(cls, false); + } return super.exists(sm, edata); } @@ -301,7 +322,11 @@ public class DataCacheStoreManager public boolean syncVersion(OpenJPAStateManager sm, Object edata) { DataCache cache = _mgr.selectCache(sm); FetchConfiguration fc = sm.getContext().getFetchConfiguration(); + CacheStatistics stats = cache.getStatistics(); if (cache == null || sm.isEmbedded() || fc.getCacheRetrieveMode() == DataCacheRetrieveMode.BYPASS) { + if(stats.isEnabled()){ + ((CacheStatisticsSPI)stats).newGet(sm.getMetaData().getDescribedType(), false); + } return super.syncVersion(sm, edata); } @@ -313,6 +338,9 @@ public class DataCacheStoreManager // if we have a cached version update from there if (version != null) { + if(stats.isEnabled()){ + ((CacheStatisticsSPI)stats).newGet(sm.getMetaData().getDescribedType(), true); + } if (!version.equals(sm.getVersion())) { sm.setVersion(version); return false; @@ -320,6 +348,9 @@ public class DataCacheStoreManager return true; } + if(stats.isEnabled()){ + ((CacheStatisticsSPI)stats).newGet(sm.getMetaData().getDescribedType(), false); + } // use data store version return super.syncVersion(sm, edata); } @@ -329,18 +360,29 @@ public class DataCacheStoreManager if (cache == null) { return super.initialize(sm, state, fetch, edata); } + Class cls = sm.getMetaData().getDescribedType(); DataCachePCData data = cache.get(sm.getObjectId()); + CacheStatistics stats = cache.getStatistics(); boolean fromDatabase = false; boolean alreadyCached = data != null; if (sm.isEmbedded() || fetch.getCacheRetrieveMode() == DataCacheRetrieveMode.BYPASS || fetch.getCacheStoreMode() == DataCacheStoreMode.REFRESH) { + // stats -- Skipped reading from the cache, noop fromDatabase = super.initialize(sm, state, fetch, edata); } else { - if (alreadyCached && !isLocking(fetch)) { - sm.initialize(data.getType(), state); + if (alreadyCached && !isLocking(fetch)) { + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newGet(cls, true); + } + sm.initialize(cls, state); data.load(sm, fetch, edata); } else { + if (!alreadyCached) { + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newGet(cls, false); + } + } fromDatabase = super.initialize(sm, state, fetch, edata); } } @@ -349,6 +391,9 @@ public class DataCacheStoreManager && ((fetch.getCacheStoreMode() == DataCacheStoreMode.USE && !alreadyCached) || (fetch.getCacheStoreMode() == DataCacheStoreMode.REFRESH)); if (updateCache) { + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newPut(cls); + } cacheStateManager(cache, sm, data); } return fromDatabase || alreadyCached; @@ -389,11 +434,17 @@ public class DataCacheStoreManager if (cache == null || sm.isEmbedded() || bypass(fetch, StoreManager.FORCE_LOAD_NONE)) return super.load(sm, fields, fetch, lockLevel, edata); + CacheStatistics stats = cache.getStatistics(); + Class cls = sm.getMetaData().getDescribedType(); DataCachePCData data = cache.get(sm.getObjectId()); if (lockLevel == LockLevels.LOCK_NONE && !isLocking(fetch) && data != null) data.load(sm, fields, fetch, edata); - if (fields.length() == 0) + if (fields.length() == 0){ + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newGet(cls, true); + } return true; + } // load from store manager; clone the set of still-unloaded fields // so that if the store manager decides to modify it it won't affect us @@ -452,15 +503,21 @@ public class DataCacheStoreManager for (OpenJPAStateManager sm : smList) { data = dataMap.get(sm.getObjectId()); - + CacheStatistics stats = cache.getStatistics(); if (sm.getManagedInstance() == null) { if (data != null) { //### the 'data.type' access here probably needs //### to be addressed for bug 511 + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newGet(sm.getMetaData().getDescribedType(), true); + } sm.initialize(data.getType(), state); data.load(sm, fetch, edata); } else { unloaded = addUnloaded(sm, null, unloaded); + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newGet(sm.getMetaData().getDescribedType(), false); + } } } else if (load != FORCE_LOAD_NONE || sm.getPCState() == PCState.HOLLOW) { @@ -469,10 +526,22 @@ public class DataCacheStoreManager // load unloaded fields fields = sm.getUnloaded(fetch); data.load(sm, fields, fetch, edata); - if (fields.length() > 0) + if (fields.length() > 0){ unloaded = addUnloaded(sm, fields, unloaded); - } else + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newGet(sm.getMetaData().getDescribedType(), false); + } + }else{ + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newGet(sm.getMetaData().getDescribedType(), true); + } + } + } else{ unloaded = addUnloaded(sm, null, unloaded); + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newGet(sm.getMetaData().getDescribedType(), false); + } + } } } } @@ -517,6 +586,10 @@ public class DataCacheStoreManager cache.put(data); else cache.update(data); + CacheStatistics stats = cache.getStatistics(); + if (stats.isEnabled()) { + ((CacheStatisticsSPI)stats).newPut(sm.getMetaData().getDescribedType()); + } } finally { cache.writeUnlock(); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractDataCacheInstrument.java b/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractDataCacheInstrument.java index a05823151..f718192f9 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractDataCacheInstrument.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/AbstractDataCacheInstrument.java @@ -218,46 +218,6 @@ public abstract class AbstractDataCacheInstrument extends AbstractInstrument return NO_STATS; } - public long getEvictionCount() { - CacheStatistics stats = getStatistics(); - if (stats != null) - return stats.getEvictionCount(); - return NO_STATS; - } - - public long getEvictionCount(String className) - throws ClassNotFoundException { - Class clazz = Class.forName(className); - return getEvictionCount(clazz); - } - - public long getEvictionCount(Class c) { - CacheStatistics stats = getStatistics(); - if (stats != null) - return stats.getEvictionCount(c); - return NO_STATS; - } - - public long getTotalEvictionCount() { - CacheStatistics stats = getStatistics(); - if (stats != null) - return stats.getTotalEvictionCount(); - return NO_STATS; - } - - public long getTotalEvictionCount(String className) - throws ClassNotFoundException { - Class clazz = Class.forName(className); - return getTotalEvictionCount(clazz); - } - - public long getTotalEvictionCount(Class c) { - CacheStatistics stats = getStatistics(); - if (stats != null) - return stats.getTotalEvictionCount(c); - return NO_STATS; - } - @SuppressWarnings("unchecked") public Set classNames() { CacheStatistics stats = getStatistics(); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/DataCacheInstrument.java b/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/DataCacheInstrument.java index f4314e81e..02cd9de2d 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/DataCacheInstrument.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/instrumentation/DataCacheInstrument.java @@ -60,17 +60,7 @@ public interface DataCacheInstrument { */ public long getTotalWriteCount(String className) throws ClassNotFoundException; - - /** - * Gets the number of cache evictions from the last reset. - */ - public long getEvictionCount(); - - /** - * Gets the total number of cache evictions since cache start. - */ - public long getTotalEvictionCount(); - + /** * Returns the name of the cache */ diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestStatistics.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestStatistics.java index f45afafeb..d086ae2ac 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestStatistics.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestStatistics.java @@ -18,129 +18,311 @@ */ package org.apache.openjpa.persistence.datacache; -import java.util.Arrays; +import java.util.List; import javax.persistence.EntityManager; +import junit.framework.AssertionFailedError; + import org.apache.openjpa.datacache.CacheStatistics; import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; +import org.apache.openjpa.persistence.OpenJPAPersistence; import org.apache.openjpa.persistence.StoreCache; import org.apache.openjpa.persistence.StoreCacheImpl; import org.apache.openjpa.persistence.test.SingleEMFTestCase; /** * Tests statistics of data cache operation. - * - * @author Pinaki Poddar - * + * */ public class TestStatistics extends SingleEMFTestCase { private static final boolean L2Cached = true; private static final boolean L1Cached = true; - private static CachedPerson person; + private static final Class cls = CachedEntityStatistics.class; + + Object[] p = + new Object[] { CLEAR_TABLES, CachedEntityStatistics.class + ,"openjpa.DataCache", "true(EnableStatistics=true)","openjpa.QueryCache", "true", +// "openjpa.ConnectionFactoryProperties", "PrintParameters=True", "openjpa.Log","SQL=trace", + }; + private EntityManager em; private StoreCache cache; CacheStatistics stats; + public void setUp() { - super.setUp(CLEAR_TABLES, CachedPerson.class, - "openjpa.DataCache", "true(EnableStatistics=true)", - "openjpa.QueryCache", "true", - "openjpa.RemoteCommitProvider", "sjvm"); + super.setUp(p); cache = emf.getStoreCache(); assertNotNull(cache); stats = cache.getStatistics(); assertNotNull(stats); em = emf.createEntityManager(); - - person = createData(); + stats.reset(); em.clear(); } - + + public void tearDown() throws Exception { + + } + /** * Test that the CacheStatistics is disabled by default. */ public void testDefaultSettings() { - Object[] props = {"openjpa.DataCache", "true", "openjpa.RemoteCommitProvider", "sjvm"}; + Object[] props = { "openjpa.DataCache", "true", "openjpa.RemoteCommitProvider", "sjvm" }; OpenJPAEntityManagerFactory emf1 = createNamedEMF("second-persistence-unit", props); - + assertFalse(emf1.getStoreCache().getStatistics().isEnabled()); } - + /** * Finding an entity from a clean should hit the L2 cache. */ - public void testFind() { + public void testSimpleFind() { + int hit = 0, eviction = 0, read = 0, write = 0; + CachedEntityStatistics person = createData(false, false); + em.clear(); + cache.getStatistics().reset(); assertTrue(cache.getStatistics().isEnabled()); Object pid = person.getId(); + // Note -- the StoreCache interface doesn't calculate statistics assertCached(person, pid, !L1Cached, L2Cached); - - long[] before = snapshot(); - CachedPerson p = em.find(CachedPerson.class, pid); - long[] after = snapshot(); - assertDelta(before, after, 1, 1, 0); // READ:1 HIT:1, WRITE:0 + CachedEntityStatistics p = em.find(CachedEntityStatistics.class, pid); + read++; + hit++; + + assertion(cls, hit, read, write, stats); + + em.find(CachedEntityStatistics.class, -1); + read++; + assertCached(p, pid, L1Cached, L2Cached); + } + + public void testFind() { + int hit = 0, evict = 0, read = 0, write = 0; + CachedEntityStatistics person = createData(true, true); + em.clear(); + cache.evictAll(); + cache.getStatistics().reset(); + + // Make sure cache is enabled and empty + assertTrue(cache.getStatistics().isEnabled()); + assertion(cls, hit, read, write, stats); + + Object pid = person.getId(); + + // Should have 3 reads and 3 writes because of pid and it's eager relationship + CachedEntityStatistics p = em.find(CachedEntityStatistics.class, pid); + read++; + read++; + read++; + write++; + write++; + write++; + assertion(cls, hit, read, write, stats); + + em.clear(); + em.find(CachedEntityStatistics.class, person.getEagerList().toArray(new CachedEntityStatistics[0])[0].getId()); + read++; + hit++; + em.clear(); + + // Should have two reads and two hits + person = em.find(CachedEntityStatistics.class, pid); + read++; + read++; + read++; + hit++; + hit++; + hit++; + assertion(cls, hit, read, write, stats); + em.clear(); + + // Evict 1 eager field data from cache + cache.evict(CachedEntityStatistics.class, person.getEagerList().toArray(new CachedEntityStatistics[0])[0] + .getId()); + evict++; + p = em.find(CachedEntityStatistics.class, pid); + read++; + read++; + read++; + hit++; + hit++; + write++; + + assertion(cls, hit, read, write, stats); + + // Test lazy field -- should be a cache miss + assertEquals(1, p.getLazyList().size()); + read++; + write++; + assertion(cls, hit, read, write, stats); + em.clear(); + + em.find(CachedEntityStatistics.class, p.getLazyList().toArray(new CachedEntityStatistics[0])[0].getId()); + read++; + hit++; + assertion(cls, hit, read, write, stats); } - + public void testMultipleUnits() { - String[] props = {"openjpa.DataCache", "true", "openjpa.RemoteCommitProvider", "sjvm"}; + String[] props = { "openjpa.DataCache", "true", "openjpa.RemoteCommitProvider", "sjvm" }; OpenJPAEntityManagerFactory emf1 = createNamedEMF("test", props); OpenJPAEntityManagerFactory emf2 = createNamedEMF("empty-pu", props); assertNotSame(emf1, emf2); assertNotSame(emf1.getStoreCache(), emf2.getStoreCache()); assertNotSame(emf1.getStoreCache().getStatistics(), emf2.getStoreCache().getStatistics()); - assertNotSame(((StoreCacheImpl)emf1.getStoreCache()).getDelegate(), - ((StoreCacheImpl)emf2.getStoreCache()).getDelegate()); - + assertNotSame(((StoreCacheImpl) emf1.getStoreCache()).getDelegate(), ((StoreCacheImpl) emf2.getStoreCache()) + .getDelegate()); + } - - CachedPerson createData() { + + public void testPersist() { + int hit = 0, evict = 0, read = 0, write = 0; + + em = emf.createEntityManager(); + // test single em.getTransaction().begin(); - CachedPerson p = new CachedPerson(); - p.setId((int)System.currentTimeMillis()); + em.persist(new CachedEntityStatistics()); + write++; + em.getTransaction().commit(); + + assertion(cls, hit, read, write, stats); + + // test multiple + CachedEntityStatistics root = new CachedEntityStatistics(); + root.addEager(new CachedEntityStatistics()); + root.addEager(new CachedEntityStatistics()); + root.addLazy(new CachedEntityStatistics()); + root.addLazy(new CachedEntityStatistics()); + write += 5; + em.getTransaction().begin(); + em.persist(root); + em.getTransaction().commit(); + assertion(cls, hit, read, write, stats); + + } + + public void testRefresh() { + int hit = 0, evict = 0, read = 0, write = 0; + CachedEntityStatistics e = new CachedEntityStatistics(); + em = emf.createEntityManager(); + // test single + em.getTransaction().begin(); + em.persist(e); + write++; + em.getTransaction().commit(); + assertion(cls, hit, read, write, stats); + + em.refresh(e); + read++; + assertion(cls, hit, read, write, stats); + em.clear(); + + } + + public void testMerge() { + int hit = 0, evict = 0, read = 0, write = 0; + CachedEntityStatistics e = new CachedEntityStatistics(); + em = emf.createEntityManager(); + // test single + em.getTransaction().begin(); + em.persist(e); + write++; + em.getTransaction().commit(); + assertion(cls, hit, read, write, stats); + em.clear(); + cache.evictAll(); + + em.getTransaction().begin(); + em.merge(e); + + em.getTransaction().commit(); + // TODO -- BROKEN + // DataCacheStoreManager.flush(...) doesn't account for some of this traffic. + // read++; + assertion(cls, hit, read, write, stats); + + } + + CachedEntityStatistics createData(boolean lazy, boolean eager) { + List eagerList = null; + List lazyList = null; + + em.getTransaction().begin(); + CachedEntityStatistics p = new CachedEntityStatistics(); + if (lazy) { + p.addLazy(new CachedEntityStatistics()); + } + if (eager) { + p.addEager(new CachedEntityStatistics()); + p.addEager(new CachedEntityStatistics()); + } em.persist(p); + em.getTransaction().commit(); return p; } - + /** * Get {hit,read,write} count for the cache across all instances. */ long[] snapshot() { - return new long[]{stats.getReadCount(), stats.getHitCount(), stats.getWriteCount()}; + return new long[] { stats.getReadCount(), stats.getHitCount(), stats.getWriteCount() }; } - + /** * Get {hit,read,write} count for the cache across given class extent. */ long[] snapshot(Class cls) { - return new long[]{stats.getReadCount(cls), stats.getHitCount(cls), stats.getWriteCount(cls)}; + return new long[] { stats.getReadCount(cls), stats.getHitCount(cls), stats.getWriteCount(cls) }; } - + + /** + * Assert that the passed in hit/eviction/read/write match those values collected by stats. + */ + private static final void assertion(Class cls, int hit, int read, int write, CacheStatistics stats) { + if (cls == null) { + throw new RuntimeException("invalid assertion. Null class"); + } else { + try { + assertEquals("Hit count doesn't match", hit, stats.getHitCount(cls)); + assertEquals("Read count doesn't match", read, stats.getReadCount(cls)); + assertEquals("Write count doesn't match", write, stats.getWriteCount(cls)); + } catch (AssertionFailedError t) { + System.out.println("hit : " + stats.getHitCount(cls) + " read: " + stats.getReadCount(cls) + " write: " + + stats.getWriteCount(cls)); + throw t; + } + } + + } + void assertDelta(long[] before, long[] after, long readDelta, long hitDelta, long writeDelta) { - assertEquals("READ count mismatch", readDelta, after[0] - before[0]); - assertEquals("HIT count mismatch", hitDelta, after[1] - before[1]); + assertEquals("READ count mismatch", readDelta, after[0] - before[0]); + assertEquals("HIT count mismatch", hitDelta, after[1] - before[1]); assertEquals("WRITE count mismatch", writeDelta, after[2] - before[2]); } - - + void assertCached(Object o, Object oid, boolean l1, boolean l2) { boolean l1a = em.contains(o); boolean l2a = cache.contains(o.getClass(), oid); if (l1 != l1a) { - fail("Expected " + (l1 ? "":"not") + " to find instance " + - o.getClass().getSimpleName()+":"+oid + " in L1 cache"); + fail("Expected " + (l1 ? "" : "not") + " to find instance " + o.getClass().getSimpleName() + ":" + oid + + " in L1 cache"); } if (l2 != l2a) { - fail("Expected " + (l2 ? "":"not") + " to find instance " + - o.getClass().getSimpleName()+":"+oid + " in L2 cache"); + fail("Expected " + (l2 ? "" : "not") + " to find instance " + o.getClass().getSimpleName() + ":" + oid + + " in L2 cache"); } } - + void print(String msg, CacheStatistics stats) { - System.err.println(msg + stats + " H:" + stats.getHitCount() + " R:" + stats.getReadCount() + " W:" + - stats.getWriteCount()); + System.err.println(msg + stats + " H:" + stats.getHitCount() + " R:" + stats.getReadCount() + " W:" + + stats.getWriteCount()); } }