diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/PersistentCacheStatsMBean.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/PersistentCacheStatsMBean.java new file mode 100644 index 0000000..a0c9345 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/PersistentCacheStatsMBean.java @@ -0,0 +1,51 @@ +package org.apache.jackrabbit.oak.api.jmx; +/* + * 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. + */ + +import aQute.bnd.annotation.ProviderType; + +import javax.management.openmbean.CompositeData; + +@ProviderType +public interface PersistentCacheStatsMBean extends CacheStatsMBean { + String TYPE = "PersistentCacheStats"; + + CompositeData getRequestRateHistory(); + + CompositeData getHitRateHistory(); + + CompositeData getLoadRateHistory(); + + CompositeData getLoadExceptionRateHistory(); + + CompositeData getHitPercentageHistory(); + + CompositeData getPutRateHistory(); + + CompositeData getInvalidateOneRateHistory(); + + CompositeData getInvalidateAllRateHistory(); + + CompositeData getBroadcastRecvRateHistory(); + + CompositeData getUsedSpaceHistory(); + + String cacheInfoAsString(); + +} + + diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java index b8d5dd7..7f13405 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java @@ -37,6 +37,7 @@ import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean; import org.apache.jackrabbit.oak.api.jmx.ConsolidatedCacheStatsMBean; +import org.apache.jackrabbit.oak.api.jmx.PersistentCacheStatsMBean; import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard; import org.apache.jackrabbit.oak.spi.whiteboard.Registration; import org.apache.jackrabbit.oak.spi.whiteboard.Tracker; @@ -51,6 +52,7 @@ import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerM public class ConsolidatedCacheStats implements ConsolidatedCacheStatsMBean { private Tracker cacheStats; + private Tracker persistentCacheStats; private Registration mbeanReg; @@ -64,6 +66,9 @@ public class ConsolidatedCacheStats implements ConsolidatedCacheStatsMBean { for(CacheStatsMBean stats : cacheStats.getServices()){ tds.put(new CacheStatsData(stats).toCompositeData()); } + for(CacheStatsMBean stats : persistentCacheStats.getServices()){ + tds.put(new CacheStatsData(stats).toCompositeData()); + } } catch (OpenDataException e) { throw new IllegalStateException(e); } @@ -74,6 +79,7 @@ public class ConsolidatedCacheStats implements ConsolidatedCacheStatsMBean { private void activate(BundleContext context){ Whiteboard wb = new OsgiWhiteboard(context); cacheStats = wb.track(CacheStatsMBean.class); + persistentCacheStats = wb.track(PersistentCacheStatsMBean.class); mbeanReg = registerMBean(wb, ConsolidatedCacheStatsMBean.class, this, @@ -90,6 +96,10 @@ public class ConsolidatedCacheStats implements ConsolidatedCacheStatsMBean { if(cacheStats != null){ cacheStats.stop(); } + + if(persistentCacheStats != null) { + persistentCacheStats.stop(); + } } private static class CacheStatsData { diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java index 94b982c..3146a37 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java @@ -17,15 +17,12 @@ package org.apache.jackrabbit.oak.plugins.document; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.cache.RemovalCause.COLLECTED; -import static com.google.common.cache.RemovalCause.EXPIRED; -import static com.google.common.cache.RemovalCause.SIZE; import java.io.InputStream; -import java.lang.ref.Reference; import java.net.UnknownHostException; -import java.util.EnumSet; +import java.util.EnumMap; import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Executor; @@ -71,6 +68,7 @@ import org.apache.jackrabbit.oak.plugins.document.mongo.MongoVersionGCSupport; import org.apache.jackrabbit.oak.plugins.document.persistentCache.CacheType; import org.apache.jackrabbit.oak.plugins.document.persistentCache.EvictionListener; import org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCache; +import org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCacheStats; import org.apache.jackrabbit.oak.plugins.document.rdb.RDBBlobStore; import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore; import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions; @@ -534,6 +532,8 @@ public class DocumentMK { private BlobStoreStats blobStoreStats; private CacheStats blobStoreCacheStats; private DocumentStoreStatsCollector documentStoreStatsCollector; + private Map persistentCacheStats = + new EnumMap(CacheType.class); public Builder() { } @@ -895,6 +895,9 @@ public class DocumentMK { return this; } + public StatisticsProvider getStatisticsProvider() { + return this.statisticsProvider; + } public DocumentStoreStatsCollector getDocumentStoreStatsCollector() { if (documentStoreStatsCollector == null) { documentStoreStatsCollector = new DocumentStoreStats(statisticsProvider); @@ -907,6 +910,11 @@ public class DocumentMK { return this; } + @Nonnull + public Map getPersistenceCacheStats() { + return persistentCacheStats; + } + @CheckForNull public BlobStoreStats getBlobStoreStats() { return blobStoreStats; @@ -1033,10 +1041,14 @@ public class DocumentMK { if (docNodeStore != null) { docNodeStore.setPersistentCache(p); } - cache = p.wrap(docNodeStore, docStore, cache, cacheType); + cache = p.wrap(docNodeStore, docStore, cache, cacheType, statisticsProvider); if (cache instanceof EvictionListener) { listeners.add((EvictionListener) cache); } + PersistentCacheStats stats = PersistentCache.getPersistentCacheStats(cache); + if (stats != null) { + persistentCacheStats.put(cacheType, stats); + } } return cache; } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java index 0321ea0..ab17c0e 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java @@ -60,6 +60,7 @@ import org.apache.jackrabbit.commons.SimpleValueFactory; import org.apache.jackrabbit.oak.api.Descriptors; import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean; import org.apache.jackrabbit.oak.api.jmx.CheckpointMBean; +import org.apache.jackrabbit.oak.api.jmx.PersistentCacheStatsMBean; import org.apache.jackrabbit.oak.cache.CacheStats; import org.apache.jackrabbit.oak.commons.PropertiesUtil; import org.apache.jackrabbit.oak.osgi.ObserverTracker; @@ -70,6 +71,8 @@ import org.apache.jackrabbit.oak.plugins.blob.BlobGarbageCollector; import org.apache.jackrabbit.oak.plugins.blob.BlobStoreStats; import org.apache.jackrabbit.oak.plugins.blob.SharedDataStore; import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils; +import org.apache.jackrabbit.oak.plugins.document.persistentCache.CacheType; +import org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCacheStats; import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection; import org.apache.jackrabbit.oak.plugins.identifier.ClusterRepositoryInfo; import org.apache.jackrabbit.oak.spi.blob.BlobStore; @@ -687,6 +690,19 @@ public class DocumentNodeStoreService { ); } + // register persistent cache stats + Map persistenceCacheStats = mkBuilder.getPersistenceCacheStats(); + for (PersistentCacheStats pcs: persistenceCacheStats.values()) { + registrations.add( + registerMBean(whiteboard, + PersistentCacheStatsMBean.class, + pcs, + PersistentCacheStatsMBean.TYPE, + pcs.getName()) + ); + } + + final long versionGcMaxAgeInSecs = toLong(prop(PROP_VER_GC_MAX_AGE), DEFAULT_VER_GC_MAX_AGE); final long blobGcMaxAgeInSecs = toLong(prop(PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE); diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java index 0b14e3a..ccc6d76 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java @@ -35,6 +35,8 @@ import org.apache.jackrabbit.oak.plugins.document.DocumentStore; import org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCache.GenerationCache; import org.apache.jackrabbit.oak.plugins.document.persistentCache.async.CacheActionDispatcher; import org.apache.jackrabbit.oak.plugins.document.persistentCache.async.CacheWriteQueue; +import org.apache.jackrabbit.oak.stats.StatisticsProvider; +import org.apache.jackrabbit.oak.stats.TimerStats; import org.h2.mvstore.MVMap; import org.h2.mvstore.WriteBuffer; import org.h2.mvstore.type.DataType; @@ -51,6 +53,7 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< private static final Set EVICTION_CAUSES = ImmutableSet.of(COLLECTED, EXPIRED, SIZE); private final PersistentCache cache; + private final PersistentCacheStats stats; private final Cache memCache; private final MultiGenerationMap map; private final CacheType type; @@ -64,7 +67,8 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< DocumentNodeStore docNodeStore, DocumentStore docStore, CacheType type, - CacheActionDispatcher dispatcher) { + CacheActionDispatcher dispatcher, + StatisticsProvider statisticsProvider) { this.cache = cache; this.memCache = memCache; this.type = type; @@ -73,6 +77,7 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< keyType = new KeyDataType(type); valueType = new ValueDataType(docNodeStore, docStore, type); this.writerQueue = new CacheWriteQueue(dispatcher, cache, map); + this.stats = new PersistentCacheStats(type, statisticsProvider); } @Override @@ -89,12 +94,14 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< map.addReadMap(generation, m); if (!readOnly) { map.setWriteMap(m); + stats.addWriteGeneration(generation); } } @Override public void removeGeneration(int generation) { map.removeReadMap(generation); + stats.removeReadGeneration(generation); } private V readIfPresent(K key) { @@ -102,11 +109,18 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< return null; } cache.switchGenerationIfNeeded(); + TimerStats.Context ctx = stats.startReadTimer(); V v = map.get(key); + ctx.stop(); return v; } private void broadcast(final K key, final V value) { + long memory = 0L; + memory += (key == null ? 0L: keyType.getMemory(key)); + memory += (value == null ? 0L: valueType.getMemory(value)); + stats.markBytesWritten(memory); + cache.broadcast(type, new Function() { @Override @Nullable @@ -131,9 +145,12 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< if (value != null) { return value; } + stats.markRequest(); + value = readIfPresent((K) key); if (value != null) { memCache.put((K) key, value); + stats.markHit(); } return value; } @@ -142,12 +159,24 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< public V get(K key, Callable valueLoader) throws ExecutionException { + + // Get stats covered in getIfPresent V value = getIfPresent(key); if (value != null) { return value; } + + // Track entry load time + TimerStats.Context ctx = stats.startLoaderTimer(); + try { value = memCache.get(key, valueLoader); + ctx.stop(); broadcast(key, value); + } + catch (ExecutionException e) { + stats.markException(); + throw e; + } return value; } @@ -161,6 +190,7 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< public void put(K key, V value) { memCache.put(key, value); broadcast(key, value); + stats.markPut(); } @SuppressWarnings("unchecked") @@ -169,6 +199,7 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< memCache.invalidate(key); writerQueue.addInvalidate(singleton((K) key)); broadcast((K) key, null); + stats.markInvalidateOne(); } @Override @@ -185,6 +216,7 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< public void invalidateAll() { memCache.invalidateAll(); map.clear(); + stats.markInvalidateAll(); } @Override @@ -219,6 +251,7 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< value = (V) valueType.read(buff); memCache.put(key, value); } + stats.markRecvBroadcast(); } /** @@ -232,4 +265,8 @@ class NodeCache implements Cache, GenerationCache, EvictionListener< } } + public PersistentCacheStats getPersistentCacheStats() { + return stats; + } + } \ No newline at end of file diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java index 1086b68..4533fc8 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java @@ -34,6 +34,7 @@ import org.apache.jackrabbit.oak.plugins.document.persistentCache.broadcast.InMe import org.apache.jackrabbit.oak.plugins.document.persistentCache.broadcast.TCPBroadcaster; import org.apache.jackrabbit.oak.plugins.document.persistentCache.broadcast.UDPBroadcaster; import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore; +import org.apache.jackrabbit.oak.stats.StatisticsProvider; import org.h2.mvstore.FileStore; import org.h2.mvstore.MVMap; import org.h2.mvstore.MVMap.Builder; @@ -381,6 +382,14 @@ public class PersistentCache implements Broadcaster.Listener { DocumentNodeStore docNodeStore, DocumentStore docStore, Cache base, CacheType type) { + return wrap(docNodeStore, docStore, base, type, StatisticsProvider.NOOP); + } + + public synchronized Cache wrap( + DocumentNodeStore docNodeStore, + DocumentStore docStore, + Cache base, CacheType type, + StatisticsProvider statisticsProvider) { boolean wrap; switch (type) { case NODE: @@ -410,7 +419,8 @@ public class PersistentCache implements Broadcaster.Listener { } if (wrap) { NodeCache c = new NodeCache(this, - base, docNodeStore, docStore, type, writeDispatcher); + base, docNodeStore, docStore, + type, writeDispatcher, statisticsProvider); initGenerationCache(c); return c; } @@ -530,6 +540,15 @@ public class PersistentCache implements Broadcaster.Listener { buff.position(end); } + public static PersistentCacheStats getPersistentCacheStats(Cache cache) { + if (cache instanceof NodeCache) { + return ((NodeCache) cache).getPersistentCacheStats(); + } + else { + return null; + } + } + private void receiveMessage(ByteBuffer buff) { CacheType type = CacheType.VALUES[buff.get()]; GenerationCache cache = caches.get(type); diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCacheStats.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCacheStats.java new file mode 100644 index 0000000..06e0333 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCacheStats.java @@ -0,0 +1,520 @@ +/* + * 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.jackrabbit.oak.plugins.document.persistentCache; + +import com.google.common.base.Objects; +import org.apache.jackrabbit.api.stats.TimeSeries; +import org.apache.jackrabbit.oak.api.jmx.PersistentCacheStatsMBean; +import org.apache.jackrabbit.oak.commons.IOUtils; +import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean; +import org.apache.jackrabbit.oak.stats.CounterStats; +import org.apache.jackrabbit.oak.stats.MeterStats; +import org.apache.jackrabbit.oak.stats.StatisticsProvider; +import org.apache.jackrabbit.oak.stats.StatsOptions; +import org.apache.jackrabbit.oak.stats.TimerStats; +import org.apache.jackrabbit.stats.TimeSeriesStatsUtil; + +import javax.management.openmbean.CompositeData; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Persistence Cache Statistics. + */ +public class PersistentCacheStats extends AnnotatedStandardMBean implements PersistentCacheStatsMBean { + + private static final Boolean ENABLE_READ_TIMER; + private static final Boolean ENABLE_LOAD_TIMER; + static { + String enableReadTimer = System.getProperty("PersistentCacheStats.readTimer", "true"); + String enableLoadTimer = System.getProperty("PersistentCacheStats.loadTimer", "false"); + ENABLE_READ_TIMER = Boolean.parseBoolean(enableReadTimer); + ENABLE_LOAD_TIMER = Boolean.parseBoolean(enableLoadTimer); + } + + private static final String HITS = "HITS"; + private static final String REQUESTS = "REQUESTS"; + private static final String LOAD_TIMER = "LOAD_TIMER"; + private static final String LOAD_EXCEPTIONS = "LOAD_EXCEPTIONS"; + private static final String PUT_ONE = "CACHE_PUT"; + private static final String BROADCAST_RECV = "BROADCAST_RECV"; + private static final String INVALIDATE_ONE = "INVALIDATE_ONE"; + private static final String INVALIDATE_ALL = "INVALIDATE_ALL"; + private static final String READ_TIMER = "READ_TIMER"; + private static final String USED_DISK_SPACE = "USED_SPACE_BYTES"; + + private final StatisticsProvider statisticsProvider; + private final String cacheName; + + private final MeterStats hitMeter; + private final TimeSeries hitRateHistory; + + private final MeterStats requestMeter; + private final TimeSeries requestRateHistory; + + private final MeterStats loadExceptionMeter; + private final TimeSeries loadExceptionRateHistory; + + private final TimerStats loadTimer; + private final TimeSeries loadRateHistory; + + private final MeterStats putMeter; + private final TimeSeries putRateHistory; + + private final MeterStats broadcastRecvMeter; + private final TimeSeries broadcastRecvRateHistory; + + private final MeterStats invalidateOneMeter; + private final TimeSeries invalidateOneRateHistory; + + private final MeterStats invalidateAllMeter; + private final TimeSeries invalidateAllRateHistory; + + private final TimerStats readTimer; + + private final CounterStats usedSpaceByteCounter; + private final TimeSeries usedSpaceByteCounterHistory; + + private final UsedSpaceTracker diskStats; + + private final TimeSeries hitPercentageHistory; + + + public PersistentCacheStats(CacheType cacheType, StatisticsProvider provider) { + super(PersistentCacheStatsMBean.class); + + if (provider == null) { + statisticsProvider = StatisticsProvider.NOOP; + } else { + statisticsProvider = provider; + } + + // Configure cache name + cacheName = "PersistentCache.NodeCache." + cacheType.name().toLowerCase(); + + // Fetch stats and time series + String statName; + + statName = getStatName(HITS, cacheName); + hitMeter = statisticsProvider.getMeter(statName, StatsOptions.DEFAULT); + hitRateHistory = getTimeSeries(statName); + + statName = getStatName(REQUESTS, cacheName); + requestMeter = statisticsProvider.getMeter(statName, StatsOptions.DEFAULT); + requestRateHistory = getTimeSeries(statName); + hitPercentageHistory = new PercentageTimeSeries(hitRateHistory, requestRateHistory); + + statName = getStatName(LOAD_TIMER, cacheName); + loadRateHistory = new DifferenceTimeSeries(requestRateHistory, hitRateHistory); + if (ENABLE_LOAD_TIMER) { + loadTimer = statisticsProvider.getTimer(statName, StatsOptions.METRICS_ONLY); + } + else { + loadTimer = StatisticsProvider.NOOP.getTimer(statName, StatsOptions.METRICS_ONLY); + } + + statName = getStatName(LOAD_EXCEPTIONS, cacheName); + loadExceptionMeter = statisticsProvider.getMeter(statName, StatsOptions.DEFAULT); + loadExceptionRateHistory = getTimeSeries(statName); + + statName = getStatName(PUT_ONE, cacheName); + putMeter = statisticsProvider.getMeter(statName, StatsOptions.DEFAULT); + putRateHistory = getTimeSeries(statName); + + statName = getStatName(BROADCAST_RECV, cacheName); + broadcastRecvMeter = statisticsProvider.getMeter(statName, StatsOptions.DEFAULT); + broadcastRecvRateHistory = getTimeSeries(statName); + + statName = getStatName(INVALIDATE_ONE, cacheName); + invalidateOneMeter = statisticsProvider.getMeter(statName, StatsOptions.DEFAULT); + invalidateOneRateHistory = getTimeSeries(statName); + + statName = getStatName(INVALIDATE_ALL, cacheName); + invalidateAllMeter = statisticsProvider.getMeter(statName, StatsOptions.DEFAULT); + invalidateAllRateHistory = getTimeSeries(statName); + + statName = getStatName(USED_DISK_SPACE, cacheName); + usedSpaceByteCounter = statisticsProvider.getCounterStats(statName, StatsOptions.DEFAULT); + usedSpaceByteCounterHistory = getTimeSeries(statName, false); + + statName = getStatName(READ_TIMER, cacheName); + if (ENABLE_READ_TIMER) { + readTimer = statisticsProvider.getTimer(statName, StatsOptions.METRICS_ONLY); + } + else { + readTimer = StatisticsProvider.NOOP.getTimer(statName, StatsOptions.METRICS_ONLY); + } + + diskStats = new UsedSpaceTracker(usedSpaceByteCounter); + } + + //~--------------------------------------< stats update methods + + public void markHit() { + hitMeter.mark(); + } + + public void markRequest() { + requestMeter.mark(); + } + + public void markException() { + loadExceptionMeter.mark(); + } + + public void markPut() { + putMeter.mark(); + } + + public void markRecvBroadcast() { + broadcastRecvMeter.mark(); + } + + public void markInvalidateOne() { + invalidateOneMeter.mark(); + } + + public void markInvalidateAll() { + invalidateAllMeter.mark(); + } + + public TimerStats.Context startReadTimer() { + return this.readTimer.time(); + } + + public TimerStats.Context startLoaderTimer() { + return this.loadTimer.time(); + } + + // Update disk space + + public void addWriteGeneration(int generation) { + diskStats.addWriteGeneration(generation); + } + + public void removeReadGeneration(int generation) { + diskStats.removeReadGeneration(generation); + } + + public void markBytesWritten(long numBytes) { + diskStats.markBytesWritten(numBytes); + } + + //~--------------------------------------< diskspace usage helper + + static class UsedSpaceTracker { + private final CounterStats byteCounter; + + private final Map generationByteCounters; + + private AtomicLong currentGenCounter; + + UsedSpaceTracker(CounterStats usageCounter) { + this.byteCounter = usageCounter; + this.generationByteCounters = new ConcurrentHashMap(); + this.currentGenCounter = new AtomicLong(); + } + + void addWriteGeneration(int generation) { + currentGenCounter = new AtomicLong(0L); + generationByteCounters.put(generation, currentGenCounter); + } + + void removeReadGeneration(int generation) { + AtomicLong genCounter = generationByteCounters.remove(generation); + byteCounter.dec(genCounter == null ? 0L : genCounter.get()); + } + + void markBytesWritten(long bytes) { + currentGenCounter.addAndGet(bytes); + byteCounter.inc(bytes); + } + } + + //~--------------------------------------< CacheStatsMbean + + @Override + public String getName() { + return cacheName; + } + + @Override + public long getRequestCount() { + return requestMeter.getCount(); + } + + @Override + public long getHitCount() { + return hitMeter.getCount(); + } + + @Override + public double getHitRate() { + long hitCount = hitMeter.getCount(); + long requestCount = requestMeter.getCount(); + return (requestCount == 0L ? 0L : (double)hitCount/requestCount); + } + + @Override + public long getMissCount() { + return requestMeter.getCount() - hitMeter.getCount(); + } + + @Override + public double getMissRate() { + long missCount = getMissCount(); + long requestCount = requestMeter.getCount(); + return (requestCount == 0L ? 0L : (double)missCount/requestCount); + } + + @Override + public long getLoadCount() { + return getMissCount(); + } + + @Override + public long getLoadSuccessCount() { + return getLoadCount() - getLoadExceptionCount(); + } + + @Override + public long getLoadExceptionCount() { + return loadExceptionMeter.getCount(); + } + + @Override + public double getLoadExceptionRate() { + long exceptionCount = loadExceptionMeter.getCount(); + long loadCount = loadTimer.getCount(); + return (loadCount == 0L ? 0L : (double)exceptionCount/loadCount); + } + + @Override + public long estimateCurrentWeight() { + return usedSpaceByteCounter.getCount(); + } + + + @Override + public CompositeData getRequestRateHistory() { + return TimeSeriesStatsUtil.asCompositeData(requestRateHistory, "Persistent cache requests"); + } + + @Override + public CompositeData getHitRateHistory() { + return TimeSeriesStatsUtil.asCompositeData(hitRateHistory, "Persistent cache hits"); + } + + @Override + public CompositeData getLoadRateHistory() { + return TimeSeriesStatsUtil.asCompositeData(loadRateHistory, "Persistent cache loads/misses"); + } + + @Override + public CompositeData getLoadExceptionRateHistory() { + return TimeSeriesStatsUtil.asCompositeData(loadExceptionRateHistory, "Persistent cache load exceptions"); + } + + @Override + public CompositeData getHitPercentageHistory() { + return TimeSeriesStatsUtil.asCompositeData(hitPercentageHistory, "Persistent cache hit percentage"); + } + + @Override + public CompositeData getPutRateHistory() { + return TimeSeriesStatsUtil.asCompositeData(putRateHistory, "Persistent cache manual put entry"); + } + + @Override + public CompositeData getInvalidateOneRateHistory() { + return TimeSeriesStatsUtil.asCompositeData(invalidateOneRateHistory, "Persistent cache invalidate one entry"); + } + + @Override + public CompositeData getInvalidateAllRateHistory() { + return TimeSeriesStatsUtil.asCompositeData(invalidateAllRateHistory, "Persistent cache invalidate all entries"); + } + + @Override + public CompositeData getBroadcastRecvRateHistory() { + return TimeSeriesStatsUtil.asCompositeData(broadcastRecvRateHistory, "Persistent cache entries received from broadcast"); + } + + @Override + public CompositeData getUsedSpaceHistory() { + return TimeSeriesStatsUtil.asCompositeData(usedSpaceByteCounterHistory, "Persistent cache estimated size (bytes)"); + } + + @Override + public String cacheInfoAsString() { + return Objects.toStringHelper("PersistentCacheStats") + .add("requestCount", getRequestCount()) + .add("hitCount", getHitCount()) + .add("hitRate", String.format("%1.2f", getHitRate())) + .add("missCount", getMissCount()) + .add("missRate", String.format("%1.2f", getMissRate())) + .add("loadCount", getLoadCount()) + .add("loadSuccessCount", getLoadSuccessCount()) + .add("loadExceptionCount", getLoadExceptionCount()) + .add("totalWeight", IOUtils.humanReadableByteCount(estimateCurrentWeight())) + .toString(); + } + + //~--------------------------------------< CacheStatsMbean - stats that are not (yet) available + @Override + public long getTotalLoadTime() { + return 0; + } + + @Override + public double getAverageLoadPenalty() { + return 0; + } + + @Override + public long getEvictionCount() { + return 0; + } + + @Override + public long getElementCount() { + return 0; + } + + @Override + public long getMaxTotalWeight() { + return Long.MAX_VALUE; + } + + @Override + public void resetStats() { + } + + + //~--------------------------------------< private helpers + + private static String getStatName(String meter, String cacheName) { + return cacheName + "." + meter; + } + + private TimeSeries getTimeSeries(String name) { + return statisticsProvider.getStats().getTimeSeries(name, true); + } + + private TimeSeries getTimeSeries(String name, boolean resetValues) { + return statisticsProvider.getStats().getTimeSeries(name, resetValues); + } + + + /** + * TimeSeries that computes the hit ratio in percentages. ( hit/total * 100 ) + */ + private static class PercentageTimeSeries implements TimeSeries { + + private TimeSeries hit, total; + + PercentageTimeSeries(TimeSeries hit, TimeSeries total) { + this.hit = hit; + this.total = total; + } + + @Override + public long[] getValuePerSecond() { + return percentage(hit.getValuePerSecond(), total.getValuePerSecond()); + } + + @Override + public long[] getValuePerMinute() { + return percentage(hit.getValuePerMinute(), total.getValuePerMinute()); + } + + @Override + public long[] getValuePerHour() { + return percentage(hit.getValuePerHour(), total.getValuePerHour()); + } + + @Override + public long[] getValuePerWeek() { + return percentage(hit.getValuePerWeek(), total.getValuePerWeek()); + } + + @Override + public long getMissingValue() { + return 0; + } + + private static long[] percentage(long[] a, long[] b) { + long[] result = new long[a.length]; + for (int i=0; i