You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2016/02/25 10:56:26 UTC
svn commit: r1732270 - in
/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak:
api/jmx/ cache/ plugins/document/ plugins/document/persistentCache/
Author: thomasm
Date: Thu Feb 25 09:56:25 2016
New Revision: 1732270
URL: http://svn.apache.org/viewvc?rev=1732270&view=rev
Log:
OAK-3850 Collect and expose Persistent Cache stats
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/PersistentCacheStatsMBean.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCacheStats.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/PersistentCacheStatsMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/PersistentCacheStatsMBean.java?rev=1732270&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/PersistentCacheStatsMBean.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/jmx/PersistentCacheStatsMBean.java Thu Feb 25 09:56:25 2016
@@ -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();
+
+}
+
+
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java?rev=1732270&r1=1732269&r2=1732270&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/cache/ConsolidatedCacheStats.java Thu Feb 25 09:56:25 2016
@@ -37,6 +37,7 @@ import org.apache.felix.scr.annotations.
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.
public class ConsolidatedCacheStats implements ConsolidatedCacheStatsMBean {
private Tracker<CacheStatsMBean> cacheStats;
+ private Tracker<PersistentCacheStatsMBean> persistentCacheStats;
private Registration mbeanReg;
@@ -64,6 +66,9 @@ public class ConsolidatedCacheStats impl
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 impl
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 impl
if(cacheStats != null){
cacheStats.stop();
}
+
+ if(persistentCacheStats != null) {
+ persistentCacheStats.stop();
+ }
}
private static class CacheStatsData {
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java?rev=1732270&r1=1732269&r2=1732270&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java Thu Feb 25 09:56:25 2016
@@ -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
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<CacheType, PersistentCacheStats> persistentCacheStats =
+ new EnumMap<CacheType, PersistentCacheStats>(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<CacheType, PersistentCacheStats> 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<K, V>) cache);
}
+ PersistentCacheStats stats = PersistentCache.getPersistentCacheStats(cache);
+ if (stats != null) {
+ persistentCacheStats.put(cacheType, stats);
+ }
}
return cache;
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java?rev=1732270&r1=1732269&r2=1732270&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java Thu Feb 25 09:56:25 2016
@@ -60,6 +60,7 @@ import org.apache.jackrabbit.commons.Sim
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
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<CacheType, PersistentCacheStats> 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);
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java?rev=1732270&r1=1732269&r2=1732270&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/NodeCache.java Thu Feb 25 09:56:25 2016
@@ -35,6 +35,8 @@ import org.apache.jackrabbit.oak.plugins
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<K, V> implements Cache<K
private static final Set<RemovalCause> EVICTION_CAUSES = ImmutableSet.of(COLLECTED, EXPIRED, SIZE);
private final PersistentCache cache;
+ private final PersistentCacheStats stats;
private final Cache<K, V> memCache;
private final MultiGenerationMap<K, V> map;
private final CacheType type;
@@ -64,7 +67,8 @@ class NodeCache<K, V> implements Cache<K
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<K, V> implements Cache<K
keyType = new KeyDataType(type);
valueType = new ValueDataType(docNodeStore, docStore, type);
this.writerQueue = new CacheWriteQueue<K, V>(dispatcher, cache, map);
+ this.stats = new PersistentCacheStats(type, statisticsProvider);
}
@Override
@@ -89,12 +94,14 @@ class NodeCache<K, V> implements Cache<K
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<K, V> implements Cache<K
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<WriteBuffer, Void>() {
@Override
@Nullable
@@ -131,9 +145,12 @@ class NodeCache<K, V> implements Cache<K
if (value != null) {
return value;
}
+ stats.markRequest();
+
value = readIfPresent((K) key);
if (value != null) {
memCache.put((K) key, value);
+ stats.markHit();
}
return value;
}
@@ -142,13 +159,24 @@ class NodeCache<K, V> implements Cache<K
public V get(K key,
Callable<? extends V> valueLoader)
throws ExecutionException {
+
+ // Get stats covered in getIfPresent
V value = getIfPresent(key);
if (value != null) {
return value;
}
- value = memCache.get(key, valueLoader);
- broadcast(key, value);
- return value;
+
+ // Track entry load time
+ TimerStats.Context ctx = stats.startLoaderTimer();
+ try {
+ value = memCache.get(key, valueLoader);
+ ctx.stop();
+ broadcast(key, value);
+ return value;
+ } catch (ExecutionException e) {
+ stats.markException();
+ throw e;
+ }
}
@Override
@@ -161,6 +189,7 @@ class NodeCache<K, V> implements Cache<K
public void put(K key, V value) {
memCache.put(key, value);
broadcast(key, value);
+ stats.markPut();
}
@SuppressWarnings("unchecked")
@@ -169,6 +198,7 @@ class NodeCache<K, V> implements Cache<K
memCache.invalidate(key);
writerQueue.addInvalidate(singleton((K) key));
broadcast((K) key, null);
+ stats.markInvalidateOne();
}
@Override
@@ -185,6 +215,7 @@ class NodeCache<K, V> implements Cache<K
public void invalidateAll() {
memCache.invalidateAll();
map.clear();
+ stats.markInvalidateAll();
}
@Override
@@ -219,6 +250,7 @@ class NodeCache<K, V> implements Cache<K
value = (V) valueType.read(buff);
memCache.put(key, value);
}
+ stats.markRecvBroadcast();
}
/**
@@ -232,4 +264,8 @@ class NodeCache<K, V> implements Cache<K
}
}
+ public PersistentCacheStats getPersistentCacheStats() {
+ return stats;
+ }
+
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java?rev=1732270&r1=1732269&r2=1732270&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCache.java Thu Feb 25 09:56:25 2016
@@ -34,6 +34,7 @@ import org.apache.jackrabbit.oak.plugins
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
DocumentNodeStore docNodeStore,
DocumentStore docStore,
Cache<K, V> base, CacheType type) {
+ return wrap(docNodeStore, docStore, base, type, StatisticsProvider.NOOP);
+ }
+
+ public synchronized <K, V> Cache<K, V> wrap(
+ DocumentNodeStore docNodeStore,
+ DocumentStore docStore,
+ Cache<K, V> base, CacheType type,
+ StatisticsProvider statisticsProvider) {
boolean wrap;
switch (type) {
case NODE:
@@ -410,7 +419,8 @@ public class PersistentCache implements
}
if (wrap) {
NodeCache<K, V> c = new NodeCache<K, V>(this,
- base, docNodeStore, docStore, type, writeDispatcher);
+ base, docNodeStore, docStore,
+ type, writeDispatcher, statisticsProvider);
initGenerationCache(c);
return c;
}
@@ -530,6 +540,15 @@ public class PersistentCache implements
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);
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCacheStats.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCacheStats.java?rev=1732270&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCacheStats.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/persistentCache/PersistentCacheStats.java Thu Feb 25 09:56:25 2016
@@ -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<Integer, AtomicLong> generationByteCounters;
+
+ private AtomicLong currentGenCounter;
+
+ UsedSpaceTracker(CounterStats usageCounter) {
+ this.byteCounter = usageCounter;
+ this.generationByteCounters = new ConcurrentHashMap<Integer, AtomicLong>();
+ 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<a.length; ++i) {
+ if (b[i] == 0) {
+ result[i] = 0;
+ }
+ else {
+ result[i] = (a[i] * 100) / b[i];
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * TimeSeries as a difference between two other TimeSeries
+ */
+ private static class DifferenceTimeSeries implements TimeSeries {
+
+ private TimeSeries tsA, tsB;
+
+ DifferenceTimeSeries(TimeSeries tsA, TimeSeries tsB) {
+ this.tsA = tsA;
+ this.tsB = tsB;
+ }
+
+ @Override
+ public long[] getValuePerSecond() {
+ return difference(tsA.getValuePerSecond(), tsB.getValuePerSecond());
+ }
+
+ @Override
+ public long[] getValuePerMinute() {
+ return difference(tsA.getValuePerMinute(), tsB.getValuePerMinute());
+ }
+
+ @Override
+ public long[] getValuePerHour() {
+ return difference(tsA.getValuePerHour(), tsB.getValuePerHour());
+ }
+
+ @Override
+ public long[] getValuePerWeek() {
+ return difference(tsA.getValuePerWeek(), tsB.getValuePerWeek());
+ }
+
+ @Override
+ public long getMissingValue() {
+ return 0;
+ }
+
+ private static long[] difference(long[] a, long[] b) {
+ long[] result = new long[a.length];
+ for (int i=0; i<a.length; ++i) {
+ result[i] = a[i] - b[i];
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file