package voldemort.store.bdb.stats; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicLong; import voldemort.VoldemortException; import voldemort.annotations.jmx.JmxGetter; import voldemort.annotations.jmx.JmxOperation; import voldemort.utils.CachedCallable; import voldemort.utils.Utils; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.DatabaseStats; import com.sleepycat.je.Environment; import com.sleepycat.je.EnvironmentConfig; import com.sleepycat.je.EnvironmentFailureException; import com.sleepycat.je.EnvironmentStats; import com.sleepycat.je.LockTimeoutException; import com.sleepycat.je.StatsConfig; public class BdbEnvironmentStats { // Don't fetch entry count/btree stats more than twice a day private final static long INVASIVE_STATS_TTL_MS = 12 * 3600 * 1000; private final Environment environment; private final Database database; private final CachedCallable<EnvironmentStats> fastStats; private final CachedCallable<SpaceUtilizationStats> fastSpaceStats; private final CachedCallable<Long> entryCount; private final CachedCallable<DatabaseStats> btreeStats; private final boolean exposeSpaceStats; private final AtomicLong numExceptions; private final AtomicLong numLockTimeoutExceptions; private final AtomicLong numEnvironmentFailureExceptions; public BdbEnvironmentStats(Environment environment, Database database, long ttlMs, boolean exposeSpaceUtil) { this.environment = environment; this.database = database; this.exposeSpaceStats = exposeSpaceUtil; Callable<EnvironmentStats> fastStatsCallable = new Callable<EnvironmentStats>() { public EnvironmentStats call() throws Exception { return getEnvironmentStats(true); } }; fastStats = new CachedCallable<EnvironmentStats>(fastStatsCallable, ttlMs); Callable<SpaceUtilizationStats> fastDbStatsCallable = new Callable<SpaceUtilizationStats>() { public SpaceUtilizationStats call() throws Exception { return getSpaceUtilizationStats(); } }; fastSpaceStats = new CachedCallable<SpaceUtilizationStats>(fastDbStatsCallable, ttlMs); Callable<Long> entryCountCallable = new Callable<Long>() { public Long call() throws Exception { return getEntryCountUncached(); } }; entryCount = new CachedCallable<Long>(entryCountCallable, INVASIVE_STATS_TTL_MS); Callable<DatabaseStats> btreeStatsCallable = new Callable<DatabaseStats>() { public DatabaseStats call() throws Exception { return getBtreeStatsUncached(); } }; btreeStats = new CachedCallable<DatabaseStats>(btreeStatsCallable, INVASIVE_STATS_TTL_MS); numExceptions = new AtomicLong(0); numLockTimeoutExceptions = new AtomicLong(0); numEnvironmentFailureExceptions = new AtomicLong(0); } private EnvironmentStats getEnvironmentStats(boolean fast) { StatsConfig config = new StatsConfig(); config.setFast(fast); return environment.getStats(config); } private SpaceUtilizationStats getSpaceUtilizationStats() { return new SpaceUtilizationStats(environment); } private SpaceUtilizationStats getFastSpaceUtilizationStats() { try { return fastSpaceStats.call(); } catch(Exception e) { throw new VoldemortException(e); } } private EnvironmentStats getFastStats() { try { return fastStats.call(); } catch(Exception e) { throw new VoldemortException(e); } } private Long getEntryCountUncached() { return database.count(); } public DatabaseStats getBtreeStatsUncached() throws Exception { // fast stats does not provide detailed Btree structure. // This is invasive and will affect performance. return database.getStats(new StatsConfig().setFast(false)); } public void reportException(DatabaseException de) { numExceptions.incrementAndGet(); if(de instanceof LockTimeoutException) { numLockTimeoutExceptions.incrementAndGet(); } else if(de instanceof EnvironmentFailureException) { numEnvironmentFailureExceptions.incrementAndGet(); } } @JmxGetter(name = "FastStatsAsString") public String getFastStatsAsString() { return getFastStats().toString(); } // 1. Caching @JmxGetter(name = "NumCacheMiss") public long getNumCacheMiss() { return getFastStats().getNCacheMiss(); } @JmxGetter(name = "NumNotResident") public long getNumNotResident() { return getFastStats().getNNotResident(); } @JmxGetter(name = "TotalCacheSize") public long getTotalCacheSize() { return getFastStats().getSharedCacheTotalBytes(); } @JmxGetter(name = "AllotedCacheSize") public long getAllotedCacheSize() { return getFastStats().getCacheTotalBytes(); } @JmxGetter(name = "EvictionPasses") public long getEvictedLNs() { return getFastStats().getNEvictPasses(); } @JmxGetter(name = "BINFetches") public long getBINFetches() { return getFastStats().getNBINsFetch(); } @JmxGetter(name = "BINFetchMisses") public long getBINFetchMisses() { return getFastStats().getNBINsFetchMiss(); } @JmxGetter(name = "INFetches") public long getINFetches() { return getFastStats().getNUpperINsFetch(); } @JmxGetter(name = "INFetchMisses") public long getINFetchMisses() { return getFastStats().getNUpperINsFetchMiss(); } @JmxGetter(name = "LNFetches") public long getLNFetches() { return getFastStats().getNLNsFetch(); } @JmxGetter(name = "LNFetchMisses") public long getLNFetchMisses() { return getFastStats().getNLNsFetchMiss(); } @JmxGetter(name = "CachedBINs") public long getCachedBINs() { return getFastStats().getNCachedBINs(); } @JmxGetter(name = "CachedINs") public long getCachedUpperINs() { return getFastStats().getNCachedUpperINs(); } @JmxGetter(name = "EvictedBINs") public long getEvictedBINs() { EnvironmentStats stats = getFastStats(); return stats.getNBINsEvictedCacheMode() + stats.getNBINsEvictedCritical() + stats.getNBINsEvictedDaemon() + stats.getNBINsEvictedManual(); } @JmxGetter(name = "EvictedINs") public long getEvictedINs() { EnvironmentStats stats = getFastStats(); return stats.getNUpperINsEvictedCacheMode() + stats.getNUpperINsEvictedCritical() + stats.getNUpperINsEvictedDaemon() + stats.getNUpperINsEvictedManual(); } // 2. IO @JmxGetter(name = "NumRandomWrites") public long getNumRandomWrites() { return getFastStats().getNRandomWrites(); } @JmxGetter(name = "NumRandomWriteBytes") public long getNumRandomWriteBytes() { return getFastStats().getNRandomWriteBytes(); } @JmxGetter(name = "NumRandomReads") public long getNumRandomReads() { return getFastStats().getNRandomReads(); } @JmxGetter(name = "NumRandomReadBytes") public long getNumRandomReadBytes() { return getFastStats().getNRandomReadBytes(); } @JmxGetter(name = "NumSequentialWrites") public long getNumSequentialWrites() { return getFastStats().getNSequentialWrites(); } @JmxGetter(name = "NumSequentialWriteBytes") public long getNumSequentialWriteBytes() { return getFastStats().getNSequentialWriteBytes(); } @JmxGetter(name = "NumSequentialReads") public long getNumSequentialReads() { return getFastStats().getNSequentialReads(); } @JmxGetter(name = "NumSequentialReadBytes") public long getNumSequentialReadBytes() { return getFastStats().getNSequentialReadBytes(); } @JmxGetter(name = "NumFSyncs") public long getNumFSyncs() { return getFastStats().getNFSyncs(); } // 3. Cleaning & Checkpointing @JmxGetter(name = "NumCleanerEntriesRead") public long getNumCleanerEntriesRead() { return getFastStats().getNCleanerEntriesRead(); } @JmxGetter(name = "FileDeletionBacklog") public long getFileDeletionBacklog() { return getFastStats().getFileDeletionBacklog(); } @JmxGetter(name = "FileDeletionBacklogBytes") public long getFileDeletionBacklogBytes() { String logFileMaxStr = environment.getConfig() .getConfigParam(EnvironmentConfig.LOG_FILE_MAX); long logFileMax = Long.parseLong(logFileMaxStr); return getFileDeletionBacklog() * logFileMax; } @JmxGetter(name = "CleanerBacklog") public long getCleanerBacklog() { return getFastStats().getCleanerBacklog(); } @JmxGetter(name = "NumCleanerRuns") public long getNumCleanerRuns() { return getFastStats().getNCleanerRuns(); } @JmxGetter(name = "NumCleanerDeletions") public long getNumCleanerDeletions() { return getFastStats().getNCleanerDeletions(); } @JmxGetter(name = "NumCheckpoints") public long getNumCheckpoints() { return getFastStats().getNCheckpoints(); } @JmxGetter(name = "TotalSpace") public long getTotalSpace() { if(this.exposeSpaceStats) return getFastSpaceUtilizationStats().getTotalSpaceUsed(); else return 0; } @JmxGetter(name = "TotalSpaceUtilized") public long getTotalSpaceUtilized() { if(this.exposeSpaceStats) return getFastSpaceUtilizationStats().getTotalSpaceUtilized(); else return 0; } @JmxGetter(name = "UtilizationSummary", description = "Displays the disk space utilization for an environment.") public String getUtilizationSummaryAsString() { return getFastSpaceUtilizationStats().getSummariesAsString(); } // 4. Latching/Locking @JmxGetter(name = "BtreeLatches") public long getBtreeLatches() { return getFastStats().getRelatchesRequired(); } @JmxGetter(name = "NumAcquiresWithContention") public long getNumAcquiresWithContention() { return getFastStats().getNAcquiresWithContention(); } @JmxGetter(name = "NumAcquiresNoWaiters") public long getNumAcquiresNoWaiters() { return getFastStats().getNAcquiresNoWaiters(); } // 5. Exceptions & general statistics @JmxGetter(name = "numExceptions") public long getNumExceptions() { return numExceptions.longValue(); } @JmxGetter(name = "numLockTimeoutExceptions") public long getNumLockTimeoutExceptions() { return numLockTimeoutExceptions.longValue(); } @JmxGetter(name = "numEnvironmentFailureExceptions") public long getNumEnvironmentFailureExceptions() { return numEnvironmentFailureExceptions.longValue(); } @JmxOperation(description = "Obtain the number of k-v entries in the store") public long getEntryCount() throws Exception { return entryCount.call(); } @JmxOperation(description = "Obtain statistics about the BTree Index for a store") public String getBtreeStats() throws Exception { return btreeStats.call().toString(); } // Compound statistics derived from raw statistics @JmxGetter(name = "NumWritesTotal") public long getNumWritesTotal() { return getNumRandomWrites() + getNumSequentialWrites(); } @JmxGetter(name = "NumWriteBytesTotal") public long getNumWriteBytesTotal() { return getNumSequentialWriteBytes() + getNumRandomWriteBytes(); } @JmxGetter(name = "NumReadsTotal") public long getNumReadsTotal() { return getNumRandomReads() + getNumSequentialReads(); } @JmxGetter(name = "NumReadBytesTotal") public long getNumReadBytesTotal() { return getNumRandomReadBytes() + getNumSequentialReadBytes(); } @JmxGetter(name = "PercentRandomWrites") public double getPercentRandomWrites() { return Utils.safeGetPercentage(getNumRandomWrites(), getNumWritesTotal()); } @JmxGetter(name = "PercentageRandomWriteBytes") public double getPercentageRandomWriteBytes() { return Utils.safeGetPercentage(getNumRandomWriteBytes(), getNumRandomWriteBytes() + getNumSequentialWriteBytes()); } @JmxGetter(name = "PercentageRandomReads") public double getPercentageRandomReads() { return Utils.safeGetPercentage(getNumRandomReads(), getNumReadsTotal()); } @JmxGetter(name = "PercentageRandomReadBytes") public double getPercentageRandomReadBytes() { return Utils.safeGetPercentage(getNumRandomWriteBytes(), getNumRandomReadBytes() + getNumSequentialReadBytes()); } @JmxGetter(name = "PercentageReads") public double getPercentageReads() { return Utils.safeGetPercentage(getNumReadsTotal(), getNumReadsTotal() + getNumWritesTotal()); } @JmxGetter(name = "PercentageReadBytes") public double getPercentageReadBytes() { return Utils.safeGetPercentage(getNumReadBytesTotal(), getNumWriteBytesTotal() + getNumReadBytesTotal()); } @JmxGetter(name = "PercentageCacheHits") public double getPercentageCacheHits() { return 1.0d - getPercentageCacheMisses(); } @JmxGetter(name = "PercentageCacheMisses") public double getPercentageCacheMisses() { return Utils.safeGetPercentage(getNumCacheMiss(), getNumReadsTotal() + getNumWritesTotal()); } @JmxGetter(name = "PercentageContended") public double getPercentageContended() { return Utils.safeGetPercentage(getNumAcquiresWithContention(), getNumAcquiresWithContention() + getNumAcquiresNoWaiters()); } @JmxGetter(name = "PercentageUtilization") public double getPercentageUtilization() { return Utils.safeGetPercentage(getTotalSpaceUtilized(), getTotalSpace()); } @JmxGetter(name = "PercentageBINMiss") public double getPercentageBINMiss() { return Utils.safeGetPercentage(getBINFetchMisses(), getBINFetches()); } @JmxGetter(name = "PercentageINMiss") public double getPercentageINMiss() { return Utils.safeGetPercentage(getINFetchMisses(), getINFetches()); } @JmxGetter(name = "PercentageLNMiss") public double getPercentageLNMiss() { return Utils.safeGetPercentage(getLNFetchMisses(), getLNFetches()); } }