/* * @(#) DataStatsAspect.java * Created Nov 4, 2011 by oleg * (C) ONE, SIA */ package odkl.cassandra.stat; import java.io.IOException; import java.util.HashMap; import java.util.Map.Entry; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Table; import org.apache.cassandra.io.CompactionIterator; import org.apache.cassandra.utils.FBUtilities; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import one.log.util.LoggerUtil; /** * This has background process which periodically collects data from internals of cassandra * * @author Oleg Anastasyev<oa@hq.one.lv> * */ @Aspect public class DataStatsAspect extends SystemArchitectureAspect implements Runnable { private final Log log = LogFactory.getLog(getClass()); private static final int MB = 1024 * 1024; /** * Statistic values are logged for each CF * */ enum Msg { // mbytes of data in CF LoadMBytes, // number of completed compactions, total compacted (read) Mbytes Compactions, CompactedMBytes, CompactedRows, // RowCacheHitRate, RowCacheSize, KeyCacheHitRate, KeyCacheSize, - these are not implemented at the moment // mean and max size of the row RowSize }; private ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); private ScheduledFuture<?> execFuture = null; /** * Currently running compaction information. * If == null no compaction is currently running */ private ColumnFamilyStore compactingStore = null; private CompactionIterator compactingIterator = null; private long compactionStartedMillis; @After("cassandraStart()") public void start() { execFuture = exec.scheduleWithFixedDelay(this, 1, 1, TimeUnit.MINUTES); log.info("Statistics collection daemon started"); } @Before("cassandraStop()") public void stop() { if (execFuture!=null) { execFuture.cancel(true); execFuture = null; log.info("Statistics collection daemon stopped"); } } @After("compactionStartedPointcut(cfs,ci)") public void compactionStart(ColumnFamilyStore cfs,CompactionIterator ci) { compactingStore = cfs; compactingIterator = ci; compactionStartedMillis = System.currentTimeMillis(); } @After("compactionCompletedPointcut()") public void compactionCompleted() { if (compactingStore==null) return; if (compactingIterator.getBytesRead()==0) return; // this was just check for possible compaction String clusterName = DatabaseDescriptor.getClusterName(); String serverName = FBUtilities.getLocalAddress().getHostAddress(); String cfNameForLogging = cfNameForLogging(compactingStore.getTable().name, compactingStore); long compactionTimeNanos = (System.currentTimeMillis() - compactionStartedMillis) * 1000000; LoggerUtil.operationsSuccess(OP_LOGGER_NAME, compactionTimeNanos, 1, "COMPACTION", clusterName, serverName, cfNameForLogging); LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.CompactedMBytes.name(), clusterName, serverName, cfNameForLogging, compactingIterator.getBytesRead() / MB); LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.CompactedRows.name(), clusterName, serverName, cfNameForLogging, compactingIterator.getRow()); compactingStore = null; compactingIterator = null; compactionStartedMillis = 0; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { try { String clusterName = DatabaseDescriptor.getClusterName(); String serverName = FBUtilities.getLocalAddress().getHostAddress(); // Collect statistics for CassandraStat //streams pending, completed //(MAX seen in 5 minutes) // LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.TPStream.name(), clusterName, serverName,null,streamStageMBean.getPendingTasks(),streamStageMBean.getCompletedTasks()); // per CF statistics HashMap<String,long[]> loads = new HashMap<String, long[]>(); for (String table : DatabaseDescriptor.getNonSystemTables()) try { for (ColumnFamilyStore cf : Table.open(table).getColumnFamilyStores() ) { String columnFamilyName = cfNameForLogging(table, cf); long[] array = loads.get(columnFamilyName); if (array==null) loads.put(columnFamilyName,array=new long[5]); array[0]+=cf.getLiveDiskSpaceUsed(); array[1]+=cf.getMemtableDataSize(); array[2]+=cf.getMeanRowCompactedSize(); array[3]++; array[4] = Math.max(array[4],cf.getMaxRowCompactedSize()); // row caches //hits.label row cache hit rate (avg) and size (max seen) // JMXInstrumentedCacheMBean cache = caches.get(table+'.'+columnFamilyName+".row"); // if (cache.getSize()>0) // { // LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.RowCacheHitRate.name(), clusterName, serverName,columnFamilyName,cache.getRecentHitRate()*100); // LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.RowCacheSize.name(), clusterName, serverName,columnFamilyName,cache.getSize()); // } // key caches //hits.label key cache hit rate and size // cache = caches.get(table+'.'+columnFamilyName+".key"); // if (cache.getSize()>0) // { // LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.KeyCacheHitRate.name(), clusterName, serverName,columnFamilyName,cache.getRecentHitRate()*100); // LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.KeyCacheSize.name(), clusterName, serverName,columnFamilyName,cache.getSize()); // } } } catch (IOException e) { log.error("",e); } for (Entry<String, long[]> en : loads.entrySet()) { long[] array = en.getValue(); String columnFamilyName = en.getKey(); LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.LoadMBytes.name(), clusterName, serverName, columnFamilyName, array[0] / MB, array[1] / MB); LoggerUtil.operationData(MSG_LOGGER_NAME, Msg.RowSize.name(), clusterName, serverName, columnFamilyName, array[2] / array[3], array[4]); } } catch (Throwable e) { log.error("Cannot collect statistics data",e); } } private String cfNameForLogging(String table, ColumnFamilyStore cf) { String columnFamilyName = cf.getColumnFamilyName(); CFMetaData data = DatabaseDescriptor.getCFMetaData(table, columnFamilyName); if (data.domainSplit) columnFamilyName = data.domainCFName; return columnFamilyName; } }