blob: a02539a1829e182434cf40ff45493d2b92fe8af5 [file] [log] [blame]
/*
* 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.cassandra.metrics;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Maps;
import com.codahale.metrics.*;
import com.codahale.metrics.Timer;
import org.apache.cassandra.config.Schema;
import org.apache.cassandra.config.SchemaConstants;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.Memtable;
import org.apache.cassandra.db.lifecycle.SSTableSet;
import org.apache.cassandra.index.SecondaryIndexManager;
import org.apache.cassandra.io.compress.CompressionMetadata;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.sstable.metadata.MetadataCollector;
import org.apache.cassandra.utils.EstimatedHistogram;
import org.apache.cassandra.utils.TopKSampler;
import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
/**
* Metrics for {@link ColumnFamilyStore}.
*/
public class TableMetrics
{
public static final long[] EMPTY = new long[0];
/** Total amount of data stored in the memtable that resides on-heap, including column related overhead and partitions overwritten. */
public final Gauge<Long> memtableOnHeapSize;
/** Total amount of data stored in the memtable that resides off-heap, including column related overhead and partitions overwritten. */
public final Gauge<Long> memtableOffHeapSize;
/** Total amount of live data stored in the memtable, excluding any data structure overhead */
public final Gauge<Long> memtableLiveDataSize;
/** Total amount of data stored in the memtables (2i and pending flush memtables included) that resides on-heap. */
public final Gauge<Long> allMemtablesOnHeapSize;
/** Total amount of data stored in the memtables (2i and pending flush memtables included) that resides off-heap. */
public final Gauge<Long> allMemtablesOffHeapSize;
/** Total amount of live data stored in the memtables (2i and pending flush memtables included) that resides off-heap, excluding any data structure overhead */
public final Gauge<Long> allMemtablesLiveDataSize;
/** Total number of columns present in the memtable. */
public final Gauge<Long> memtableColumnsCount;
/** Number of times flush has resulted in the memtable being switched out. */
public final Counter memtableSwitchCount;
/** Current compression ratio for all SSTables */
public final Gauge<Double> compressionRatio;
/** Histogram of estimated partition size (in bytes). */
public final Gauge<long[]> estimatedPartitionSizeHistogram;
/** Approximate number of keys in table. */
public final Gauge<Long> estimatedPartitionCount;
/** Histogram of estimated number of columns. */
public final Gauge<long[]> estimatedColumnCountHistogram;
/** Histogram of the number of sstable data files accessed per read */
public final TableHistogram sstablesPerReadHistogram;
/** (Local) read metrics */
public final LatencyMetrics readLatency;
/** (Local) range slice metrics */
public final LatencyMetrics rangeLatency;
/** (Local) write metrics */
public final LatencyMetrics writeLatency;
/** Estimated number of tasks pending for this table */
public final Counter pendingFlushes;
/** Total number of bytes flushed since server [re]start */
public final Counter bytesFlushed;
/** Total number of bytes written by compaction since server [re]start */
public final Counter compactionBytesWritten;
/** Estimate of number of pending compactios for this table */
public final Gauge<Integer> pendingCompactions;
/** Number of SSTables on disk for this CF */
public final Gauge<Integer> liveSSTableCount;
/** Disk space used by SSTables belonging to this table */
public final Counter liveDiskSpaceUsed;
/** Total disk space used by SSTables belonging to this table, including obsolete ones waiting to be GC'd */
public final Counter totalDiskSpaceUsed;
/** Size of the smallest compacted partition */
public final Gauge<Long> minPartitionSize;
/** Size of the largest compacted partition */
public final Gauge<Long> maxPartitionSize;
/** Size of the smallest compacted partition */
public final Gauge<Long> meanPartitionSize;
/** Number of false positives in bloom filter */
public final Gauge<Long> bloomFilterFalsePositives;
/** Number of false positives in bloom filter from last read */
public final Gauge<Long> recentBloomFilterFalsePositives;
/** False positive ratio of bloom filter */
public final Gauge<Double> bloomFilterFalseRatio;
/** False positive ratio of bloom filter from last read */
public final Gauge<Double> recentBloomFilterFalseRatio;
/** Disk space used by bloom filter */
public final Gauge<Long> bloomFilterDiskSpaceUsed;
/** Off heap memory used by bloom filter */
public final Gauge<Long> bloomFilterOffHeapMemoryUsed;
/** Off heap memory used by index summary */
public final Gauge<Long> indexSummaryOffHeapMemoryUsed;
/** Off heap memory used by compression meta data*/
public final Gauge<Long> compressionMetadataOffHeapMemoryUsed;
/** Key cache hit rate for this CF */
public final Gauge<Double> keyCacheHitRate;
/** Tombstones scanned in queries on this CF */
public final TableHistogram tombstoneScannedHistogram;
/** Live rows scanned in queries on this CF */
public final TableHistogram liveScannedHistogram;
/** Column update time delta on this CF */
public final TableHistogram colUpdateTimeDeltaHistogram;
/** time taken acquiring the partition lock for materialized view updates for this table */
public final TableTimer viewLockAcquireTime;
/** time taken during the local read of a materialized view update */
public final TableTimer viewReadTime;
/** Disk space used by snapshot files which */
public final Gauge<Long> trueSnapshotsSize;
/** Row cache hits, but result out of range */
public final Counter rowCacheHitOutOfRange;
/** Number of row cache hits */
public final Counter rowCacheHit;
/** Number of row cache misses */
public final Counter rowCacheMiss;
/** CAS Prepare metrics */
public final LatencyMetrics casPrepare;
/** CAS Propose metrics */
public final LatencyMetrics casPropose;
/** CAS Commit metrics */
public final LatencyMetrics casCommit;
/** percent of the data that is repaired */
public final Gauge<Double> percentRepaired;
public final Timer coordinatorReadLatency;
public final Timer coordinatorScanLatency;
/** Time spent waiting for free memtable space, either on- or off-heap */
public final Histogram waitingOnFreeMemtableSpace;
/** Dropped Mutations Count */
public final Counter droppedMutations;
private final MetricNameFactory factory;
private final MetricNameFactory aliasFactory;
private static final MetricNameFactory globalFactory = new AllTableMetricNameFactory("Table");
private static final MetricNameFactory globalAliasFactory = new AllTableMetricNameFactory("ColumnFamily");
public final Counter speculativeRetries;
public final static LatencyMetrics globalReadLatency = new LatencyMetrics(globalFactory, globalAliasFactory, "Read");
public final static LatencyMetrics globalWriteLatency = new LatencyMetrics(globalFactory, globalAliasFactory, "Write");
public final static LatencyMetrics globalRangeLatency = new LatencyMetrics(globalFactory, globalAliasFactory, "Range");
public final static Gauge<Double> globalPercentRepaired = Metrics.register(globalFactory.createMetricName("PercentRepaired"),
new Gauge<Double>()
{
public Double getValue()
{
double repaired = 0;
double total = 0;
for (String keyspace : Schema.instance.getNonSystemKeyspaces())
{
Keyspace k = Schema.instance.getKeyspaceInstance(keyspace);
if (SchemaConstants.DISTRIBUTED_KEYSPACE_NAME.equals(k.getName()))
continue;
if (k.getReplicationStrategy().getReplicationFactor() < 2)
continue;
for (ColumnFamilyStore cf : k.getColumnFamilyStores())
{
if (!SecondaryIndexManager.isIndexColumnFamily(cf.name))
{
for (SSTableReader sstable : cf.getSSTables(SSTableSet.CANONICAL))
{
if (sstable.isRepaired())
{
repaired += sstable.uncompressedLength();
}
total += sstable.uncompressedLength();
}
}
}
}
return total > 0 ? (repaired / total) * 100 : 100.0;
}
});
public final Meter readRepairRequests;
public final Meter shortReadProtectionRequests;
public final Meter replicaSideFilteringProtectionRequests;
public final Map<Sampler, TopKSampler<ByteBuffer>> samplers;
/**
* stores metrics that will be rolled into a single global metric
*/
public final static ConcurrentMap<String, Set<Metric>> allTableMetrics = Maps.newConcurrentMap();
/**
* Stores all metric names created that can be used when unregistering, optionally mapped to an alias name.
*/
public final static Map<String, String> all = Maps.newHashMap();
private interface GetHistogram
{
EstimatedHistogram getHistogram(SSTableReader reader);
}
private static long[] combineHistograms(Iterable<SSTableReader> sstables, GetHistogram getHistogram)
{
Iterator<SSTableReader> iterator = sstables.iterator();
if (!iterator.hasNext())
{
return EMPTY;
}
long[] firstBucket = getHistogram.getHistogram(iterator.next()).getBuckets(false);
long[] values = new long[firstBucket.length];
System.arraycopy(firstBucket, 0, values, 0, values.length);
while (iterator.hasNext())
{
long[] nextBucket = getHistogram.getHistogram(iterator.next()).getBuckets(false);
if (nextBucket.length > values.length)
{
long[] newValues = new long[nextBucket.length];
System.arraycopy(firstBucket, 0, newValues, 0, firstBucket.length);
for (int i = 0; i < newValues.length; i++)
{
newValues[i] += nextBucket[i];
}
values = newValues;
}
else
{
for (int i = 0; i < values.length; i++)
{
values[i] += nextBucket[i];
}
}
}
return values;
}
/**
* Creates metrics for given {@link ColumnFamilyStore}.
*
* @param cfs ColumnFamilyStore to measure metrics
*/
public TableMetrics(final ColumnFamilyStore cfs)
{
factory = new TableMetricNameFactory(cfs, "Table");
aliasFactory = new TableMetricNameFactory(cfs, "ColumnFamily");
samplers = Maps.newHashMap();
for (Sampler sampler : Sampler.values())
{
samplers.put(sampler, new TopKSampler<>());
}
memtableColumnsCount = createTableGauge("MemtableColumnsCount", new Gauge<Long>()
{
public Long getValue()
{
return cfs.getTracker().getView().getCurrentMemtable().getOperations();
}
});
memtableOnHeapSize = createTableGauge("MemtableOnHeapSize", new Gauge<Long>()
{
public Long getValue()
{
return cfs.getTracker().getView().getCurrentMemtable().getAllocator().onHeap().owns();
}
});
memtableOffHeapSize = createTableGauge("MemtableOffHeapSize", new Gauge<Long>()
{
public Long getValue()
{
return cfs.getTracker().getView().getCurrentMemtable().getAllocator().offHeap().owns();
}
});
memtableLiveDataSize = createTableGauge("MemtableLiveDataSize", new Gauge<Long>()
{
public Long getValue()
{
return cfs.getTracker().getView().getCurrentMemtable().getLiveDataSize();
}
});
allMemtablesOnHeapSize = createTableGauge("AllMemtablesHeapSize", new Gauge<Long>()
{
public Long getValue()
{
long size = 0;
for (ColumnFamilyStore cfs2 : cfs.concatWithIndexes())
size += cfs2.getTracker().getView().getCurrentMemtable().getAllocator().onHeap().owns();
return size;
}
});
allMemtablesOffHeapSize = createTableGauge("AllMemtablesOffHeapSize", new Gauge<Long>()
{
public Long getValue()
{
long size = 0;
for (ColumnFamilyStore cfs2 : cfs.concatWithIndexes())
size += cfs2.getTracker().getView().getCurrentMemtable().getAllocator().offHeap().owns();
return size;
}
});
allMemtablesLiveDataSize = createTableGauge("AllMemtablesLiveDataSize", new Gauge<Long>()
{
public Long getValue()
{
long size = 0;
for (ColumnFamilyStore cfs2 : cfs.concatWithIndexes())
size += cfs2.getTracker().getView().getCurrentMemtable().getLiveDataSize();
return size;
}
});
memtableSwitchCount = createTableCounter("MemtableSwitchCount");
estimatedPartitionSizeHistogram = Metrics.register(factory.createMetricName("EstimatedPartitionSizeHistogram"),
aliasFactory.createMetricName("EstimatedRowSizeHistogram"),
new Gauge<long[]>()
{
public long[] getValue()
{
return combineHistograms(cfs.getSSTables(SSTableSet.CANONICAL), new GetHistogram()
{
public EstimatedHistogram getHistogram(SSTableReader reader)
{
return reader.getEstimatedPartitionSize();
}
});
}
});
estimatedPartitionCount = Metrics.register(factory.createMetricName("EstimatedPartitionCount"),
aliasFactory.createMetricName("EstimatedRowCount"),
new Gauge<Long>()
{
public Long getValue()
{
long memtablePartitions = 0;
for (Memtable memtable : cfs.getTracker().getView().getAllMemtables())
memtablePartitions += memtable.partitionCount();
return SSTableReader.getApproximateKeyCount(cfs.getSSTables(SSTableSet.CANONICAL)) + memtablePartitions;
}
});
estimatedColumnCountHistogram = Metrics.register(factory.createMetricName("EstimatedColumnCountHistogram"),
aliasFactory.createMetricName("EstimatedColumnCountHistogram"),
new Gauge<long[]>()
{
public long[] getValue()
{
return combineHistograms(cfs.getSSTables(SSTableSet.CANONICAL), new GetHistogram()
{
public EstimatedHistogram getHistogram(SSTableReader reader)
{
return reader.getEstimatedColumnCount();
}
});
}
});
sstablesPerReadHistogram = createTableHistogram("SSTablesPerReadHistogram", cfs.keyspace.metric.sstablesPerReadHistogram, true);
compressionRatio = createTableGauge("CompressionRatio", new Gauge<Double>()
{
public Double getValue()
{
return computeCompressionRatio(cfs.getSSTables(SSTableSet.CANONICAL));
}
}, new Gauge<Double>() // global gauge
{
public Double getValue()
{
List<SSTableReader> sstables = new ArrayList<>();
Keyspace.all().forEach(ks -> sstables.addAll(ks.getAllSSTables(SSTableSet.CANONICAL)));
return computeCompressionRatio(sstables);
}
});
percentRepaired = createTableGauge("PercentRepaired", new Gauge<Double>()
{
public Double getValue()
{
double repaired = 0;
double total = 0;
for (SSTableReader sstable : cfs.getSSTables(SSTableSet.CANONICAL))
{
if (sstable.isRepaired())
{
repaired += sstable.uncompressedLength();
}
total += sstable.uncompressedLength();
}
return total > 0 ? (repaired / total) * 100 : 100.0;
}
});
readLatency = new LatencyMetrics(factory, "Read", cfs.keyspace.metric.readLatency, globalReadLatency);
writeLatency = new LatencyMetrics(factory, "Write", cfs.keyspace.metric.writeLatency, globalWriteLatency);
rangeLatency = new LatencyMetrics(factory, "Range", cfs.keyspace.metric.rangeLatency, globalRangeLatency);
pendingFlushes = createTableCounter("PendingFlushes");
bytesFlushed = createTableCounter("BytesFlushed");
compactionBytesWritten = createTableCounter("CompactionBytesWritten");
pendingCompactions = createTableGauge("PendingCompactions", new Gauge<Integer>()
{
public Integer getValue()
{
return cfs.getCompactionStrategyManager().getEstimatedRemainingTasks();
}
});
liveSSTableCount = createTableGauge("LiveSSTableCount", new Gauge<Integer>()
{
public Integer getValue()
{
return cfs.getTracker().getView().liveSSTables().size();
}
});
liveDiskSpaceUsed = createTableCounter("LiveDiskSpaceUsed");
totalDiskSpaceUsed = createTableCounter("TotalDiskSpaceUsed");
minPartitionSize = createTableGauge("MinPartitionSize", "MinRowSize", new Gauge<Long>()
{
public Long getValue()
{
long min = 0;
for (SSTableReader sstable : cfs.getSSTables(SSTableSet.CANONICAL))
{
if (min == 0 || sstable.getEstimatedPartitionSize().min() < min)
min = sstable.getEstimatedPartitionSize().min();
}
return min;
}
}, new Gauge<Long>() // global gauge
{
public Long getValue()
{
long min = Long.MAX_VALUE;
for (Metric cfGauge : allTableMetrics.get("MinPartitionSize"))
{
min = Math.min(min, ((Gauge<? extends Number>) cfGauge).getValue().longValue());
}
return min;
}
});
maxPartitionSize = createTableGauge("MaxPartitionSize", "MaxRowSize", new Gauge<Long>()
{
public Long getValue()
{
long max = 0;
for (SSTableReader sstable : cfs.getSSTables(SSTableSet.CANONICAL))
{
if (sstable.getEstimatedPartitionSize().max() > max)
max = sstable.getEstimatedPartitionSize().max();
}
return max;
}
}, new Gauge<Long>() // global gauge
{
public Long getValue()
{
long max = 0;
for (Metric cfGauge : allTableMetrics.get("MaxPartitionSize"))
{
max = Math.max(max, ((Gauge<? extends Number>) cfGauge).getValue().longValue());
}
return max;
}
});
meanPartitionSize = createTableGauge("MeanPartitionSize", "MeanRowSize", new Gauge<Long>()
{
public Long getValue()
{
long sum = 0;
long count = 0;
for (SSTableReader sstable : cfs.getSSTables(SSTableSet.CANONICAL))
{
long n = sstable.getEstimatedPartitionSize().count();
sum += sstable.getEstimatedPartitionSize().mean() * n;
count += n;
}
return count > 0 ? sum / count : 0;
}
}, new Gauge<Long>() // global gauge
{
public Long getValue()
{
long sum = 0;
long count = 0;
for (Keyspace keyspace : Keyspace.all())
{
for (SSTableReader sstable : keyspace.getAllSSTables(SSTableSet.CANONICAL))
{
long n = sstable.getEstimatedPartitionSize().count();
sum += sstable.getEstimatedPartitionSize().mean() * n;
count += n;
}
}
return count > 0 ? sum / count : 0;
}
});
bloomFilterFalsePositives = createTableGauge("BloomFilterFalsePositives", new Gauge<Long>()
{
public Long getValue()
{
long count = 0L;
for (SSTableReader sstable: cfs.getSSTables(SSTableSet.LIVE))
count += sstable.getBloomFilterFalsePositiveCount();
return count;
}
});
recentBloomFilterFalsePositives = createTableGauge("RecentBloomFilterFalsePositives", new Gauge<Long>()
{
public Long getValue()
{
long count = 0L;
for (SSTableReader sstable : cfs.getSSTables(SSTableSet.LIVE))
count += sstable.getRecentBloomFilterFalsePositiveCount();
return count;
}
});
bloomFilterFalseRatio = createTableGauge("BloomFilterFalseRatio", new Gauge<Double>()
{
public Double getValue()
{
long falseCount = 0L;
long trueCount = 0L;
for (SSTableReader sstable : cfs.getSSTables(SSTableSet.LIVE))
{
falseCount += sstable.getBloomFilterFalsePositiveCount();
trueCount += sstable.getBloomFilterTruePositiveCount();
}
if (falseCount == 0L && trueCount == 0L)
return 0d;
return (double) falseCount / (trueCount + falseCount);
}
}, new Gauge<Double>() // global gauge
{
public Double getValue()
{
long falseCount = 0L;
long trueCount = 0L;
for (Keyspace keyspace : Keyspace.all())
{
for (SSTableReader sstable : keyspace.getAllSSTables(SSTableSet.LIVE))
{
falseCount += sstable.getBloomFilterFalsePositiveCount();
trueCount += sstable.getBloomFilterTruePositiveCount();
}
}
if (falseCount == 0L && trueCount == 0L)
return 0d;
return (double) falseCount / (trueCount + falseCount);
}
});
recentBloomFilterFalseRatio = createTableGauge("RecentBloomFilterFalseRatio", new Gauge<Double>()
{
public Double getValue()
{
long falseCount = 0L;
long trueCount = 0L;
for (SSTableReader sstable: cfs.getSSTables(SSTableSet.LIVE))
{
falseCount += sstable.getRecentBloomFilterFalsePositiveCount();
trueCount += sstable.getRecentBloomFilterTruePositiveCount();
}
if (falseCount == 0L && trueCount == 0L)
return 0d;
return (double) falseCount / (trueCount + falseCount);
}
}, new Gauge<Double>() // global gauge
{
public Double getValue()
{
long falseCount = 0L;
long trueCount = 0L;
for (Keyspace keyspace : Keyspace.all())
{
for (SSTableReader sstable : keyspace.getAllSSTables(SSTableSet.LIVE))
{
falseCount += sstable.getRecentBloomFilterFalsePositiveCount();
trueCount += sstable.getRecentBloomFilterTruePositiveCount();
}
}
if (falseCount == 0L && trueCount == 0L)
return 0d;
return (double) falseCount / (trueCount + falseCount);
}
});
bloomFilterDiskSpaceUsed = createTableGauge("BloomFilterDiskSpaceUsed", new Gauge<Long>()
{
public Long getValue()
{
long total = 0;
for (SSTableReader sst : cfs.getSSTables(SSTableSet.CANONICAL))
total += sst.getBloomFilterSerializedSize();
return total;
}
});
bloomFilterOffHeapMemoryUsed = createTableGauge("BloomFilterOffHeapMemoryUsed", new Gauge<Long>()
{
public Long getValue()
{
long total = 0;
for (SSTableReader sst : cfs.getSSTables(SSTableSet.LIVE))
total += sst.getBloomFilterOffHeapSize();
return total;
}
});
indexSummaryOffHeapMemoryUsed = createTableGauge("IndexSummaryOffHeapMemoryUsed", new Gauge<Long>()
{
public Long getValue()
{
long total = 0;
for (SSTableReader sst : cfs.getSSTables(SSTableSet.LIVE))
total += sst.getIndexSummaryOffHeapSize();
return total;
}
});
compressionMetadataOffHeapMemoryUsed = createTableGauge("CompressionMetadataOffHeapMemoryUsed", new Gauge<Long>()
{
public Long getValue()
{
long total = 0;
for (SSTableReader sst : cfs.getSSTables(SSTableSet.LIVE))
total += sst.getCompressionMetadataOffHeapSize();
return total;
}
});
speculativeRetries = createTableCounter("SpeculativeRetries");
keyCacheHitRate = Metrics.register(factory.createMetricName("KeyCacheHitRate"),
aliasFactory.createMetricName("KeyCacheHitRate"),
new RatioGauge()
{
@Override
public Ratio getRatio()
{
return Ratio.of(getNumerator(), getDenominator());
}
protected double getNumerator()
{
long hits = 0L;
for (SSTableReader sstable : cfs.getSSTables(SSTableSet.LIVE))
hits += sstable.getKeyCacheHit();
return hits;
}
protected double getDenominator()
{
long requests = 0L;
for (SSTableReader sstable : cfs.getSSTables(SSTableSet.LIVE))
requests += sstable.getKeyCacheRequest();
return Math.max(requests, 1); // to avoid NaN.
}
});
tombstoneScannedHistogram = createTableHistogram("TombstoneScannedHistogram", cfs.keyspace.metric.tombstoneScannedHistogram, false);
liveScannedHistogram = createTableHistogram("LiveScannedHistogram", cfs.keyspace.metric.liveScannedHistogram, false);
colUpdateTimeDeltaHistogram = createTableHistogram("ColUpdateTimeDeltaHistogram", cfs.keyspace.metric.colUpdateTimeDeltaHistogram, false);
coordinatorReadLatency = Metrics.timer(factory.createMetricName("CoordinatorReadLatency"));
coordinatorScanLatency = Metrics.timer(factory.createMetricName("CoordinatorScanLatency"));
waitingOnFreeMemtableSpace = Metrics.histogram(factory.createMetricName("WaitingOnFreeMemtableSpace"), false);
// We do not want to capture view mutation specific metrics for a view
// They only makes sense to capture on the base table
if (cfs.metadata.isView())
{
viewLockAcquireTime = null;
viewReadTime = null;
}
else
{
viewLockAcquireTime = createTableTimer("ViewLockAcquireTime", cfs.keyspace.metric.viewLockAcquireTime);
viewReadTime = createTableTimer("ViewReadTime", cfs.keyspace.metric.viewReadTime);
}
trueSnapshotsSize = createTableGauge("SnapshotsSize", new Gauge<Long>()
{
public Long getValue()
{
return cfs.trueSnapshotsSize();
}
});
rowCacheHitOutOfRange = createTableCounter("RowCacheHitOutOfRange");
rowCacheHit = createTableCounter("RowCacheHit");
rowCacheMiss = createTableCounter("RowCacheMiss");
droppedMutations = createTableCounter("DroppedMutations");
casPrepare = new LatencyMetrics(factory, "CasPrepare", cfs.keyspace.metric.casPrepare);
casPropose = new LatencyMetrics(factory, "CasPropose", cfs.keyspace.metric.casPropose);
casCommit = new LatencyMetrics(factory, "CasCommit", cfs.keyspace.metric.casCommit);
readRepairRequests = createTableMeter("ReadRepairRequests");
shortReadProtectionRequests = createTableMeter("ShortReadProtectionRequests");
replicaSideFilteringProtectionRequests = createTableMeter("ReplicaSideFilteringProtectionRequests");
}
public void updateSSTableIterated(int count)
{
sstablesPerReadHistogram.update(count);
}
/**
* Release all associated metrics.
*/
public void release()
{
for(Map.Entry<String, String> entry : all.entrySet())
{
CassandraMetricsRegistry.MetricName name = factory.createMetricName(entry.getKey());
CassandraMetricsRegistry.MetricName alias = aliasFactory.createMetricName(entry.getValue());
final Metric metric = Metrics.getMetrics().get(name.getMetricName());
if (metric != null)
{ // Metric will be null if it's a view metric we are releasing. Views have null for ViewLockAcquireTime and ViewLockReadTime
allTableMetrics.get(entry.getKey()).remove(metric);
Metrics.remove(name, alias);
}
}
readLatency.release();
writeLatency.release();
rangeLatency.release();
Metrics.remove(factory.createMetricName("EstimatedPartitionSizeHistogram"), aliasFactory.createMetricName("EstimatedRowSizeHistogram"));
Metrics.remove(factory.createMetricName("EstimatedPartitionCount"), aliasFactory.createMetricName("EstimatedRowCount"));
Metrics.remove(factory.createMetricName("EstimatedColumnCountHistogram"), aliasFactory.createMetricName("EstimatedColumnCountHistogram"));
Metrics.remove(factory.createMetricName("KeyCacheHitRate"), aliasFactory.createMetricName("KeyCacheHitRate"));
Metrics.remove(factory.createMetricName("CoordinatorReadLatency"), aliasFactory.createMetricName("CoordinatorReadLatency"));
Metrics.remove(factory.createMetricName("CoordinatorScanLatency"), aliasFactory.createMetricName("CoordinatorScanLatency"));
Metrics.remove(factory.createMetricName("WaitingOnFreeMemtableSpace"), aliasFactory.createMetricName("WaitingOnFreeMemtableSpace"));
}
/**
* Create a gauge that will be part of a merged version of all column families. The global gauge
* will merge each CF gauge by adding their values
*/
protected <T extends Number> Gauge<T> createTableGauge(final String name, Gauge<T> gauge)
{
return createTableGauge(name, gauge, new Gauge<Long>()
{
public Long getValue()
{
long total = 0;
for (Metric cfGauge : allTableMetrics.get(name))
{
total = total + ((Gauge<? extends Number>) cfGauge).getValue().longValue();
}
return total;
}
});
}
/**
* Create a gauge that will be part of a merged version of all column families. The global gauge
* is defined as the globalGauge parameter
*/
protected <G,T> Gauge<T> createTableGauge(String name, Gauge<T> gauge, Gauge<G> globalGauge)
{
return createTableGauge(name, name, gauge, globalGauge);
}
protected <G,T> Gauge<T> createTableGauge(String name, String alias, Gauge<T> gauge, Gauge<G> globalGauge)
{
Gauge<T> cfGauge = Metrics.register(factory.createMetricName(name), aliasFactory.createMetricName(alias), gauge);
if (register(name, alias, cfGauge))
{
Metrics.register(globalFactory.createMetricName(name), globalAliasFactory.createMetricName(alias), globalGauge);
}
return cfGauge;
}
/**
* Creates a counter that will also have a global counter thats the sum of all counters across
* different column families
*/
protected Counter createTableCounter(final String name)
{
return createTableCounter(name, name);
}
protected Counter createTableCounter(final String name, final String alias)
{
Counter cfCounter = Metrics.counter(factory.createMetricName(name), aliasFactory.createMetricName(alias));
if (register(name, alias, cfCounter))
{
Metrics.register(globalFactory.createMetricName(name),
globalAliasFactory.createMetricName(alias),
new Gauge<Long>()
{
public Long getValue()
{
long total = 0;
for (Metric cfGauge : allTableMetrics.get(name))
{
total += ((Counter) cfGauge).getCount();
}
return total;
}
});
}
return cfCounter;
}
private Meter createTableMeter(final String name)
{
return createTableMeter(name, name);
}
private Meter createTableMeter(final String name, final String alias)
{
Meter tableMeter = Metrics.meter(factory.createMetricName(name), aliasFactory.createMetricName(alias));
register(name, alias, tableMeter);
return tableMeter;
}
/**
* Computes the compression ratio for the specified SSTables
*
* @param sstables the SSTables
* @return the compression ratio for the specified SSTables
*/
private static Double computeCompressionRatio(Iterable<SSTableReader> sstables)
{
double compressedLengthSum = 0;
double dataLengthSum = 0;
for (SSTableReader sstable : sstables)
{
if (sstable.compression)
{
// We should not have any sstable which are in an open early mode as the sstable were selected
// using SSTableSet.CANONICAL.
assert sstable.openReason != SSTableReader.OpenReason.EARLY;
CompressionMetadata compressionMetadata = sstable.getCompressionMetadata();
compressedLengthSum += compressionMetadata.compressedFileLength;
dataLengthSum += compressionMetadata.dataLength;
}
}
return dataLengthSum != 0 ? compressedLengthSum / dataLengthSum : MetadataCollector.NO_COMPRESSION_RATIO;
}
/**
* Create a histogram-like interface that will register both a CF, keyspace and global level
* histogram and forward any updates to both
*/
protected TableHistogram createTableHistogram(String name, Histogram keyspaceHistogram, boolean considerZeroes)
{
return createTableHistogram(name, name, keyspaceHistogram, considerZeroes);
}
protected TableHistogram createTableHistogram(String name, String alias, Histogram keyspaceHistogram, boolean considerZeroes)
{
Histogram cfHistogram = Metrics.histogram(factory.createMetricName(name), aliasFactory.createMetricName(alias), considerZeroes);
register(name, alias, cfHistogram);
return new TableHistogram(cfHistogram,
keyspaceHistogram,
Metrics.histogram(globalFactory.createMetricName(name),
globalAliasFactory.createMetricName(alias),
considerZeroes));
}
protected TableTimer createTableTimer(String name, Timer keyspaceTimer)
{
return createTableTimer(name, name, keyspaceTimer);
}
protected TableTimer createTableTimer(String name, String alias, Timer keyspaceTimer)
{
Timer cfTimer = Metrics.timer(factory.createMetricName(name), aliasFactory.createMetricName(alias));
register(name, alias, cfTimer);
return new TableTimer(cfTimer,
keyspaceTimer,
Metrics.timer(globalFactory.createMetricName(name),
globalAliasFactory.createMetricName(alias)));
}
/**
* Registers a metric to be removed when unloading CF.
* @return true if first time metric with that name has been registered
*/
private boolean register(String name, String alias, Metric metric)
{
boolean ret = allTableMetrics.putIfAbsent(name, ConcurrentHashMap.newKeySet()) == null;
allTableMetrics.get(name).add(metric);
all.put(name, alias);
return ret;
}
public static class TableHistogram
{
public final Histogram[] all;
public final Histogram cf;
private TableHistogram(Histogram cf, Histogram keyspace, Histogram global)
{
this.cf = cf;
this.all = new Histogram[]{cf, keyspace, global};
}
public void update(long i)
{
for(Histogram histo : all)
{
histo.update(i);
}
}
}
public static class TableTimer
{
public final Timer[] all;
public final Timer cf;
private TableTimer(Timer cf, Timer keyspace, Timer global)
{
this.cf = cf;
this.all = new Timer[]{cf, keyspace, global};
}
public void update(long i, TimeUnit unit)
{
for(Timer timer : all)
{
timer.update(i, unit);
}
}
}
static class TableMetricNameFactory implements MetricNameFactory
{
private final String keyspaceName;
private final String tableName;
private final boolean isIndex;
private final String type;
TableMetricNameFactory(ColumnFamilyStore cfs, String type)
{
this.keyspaceName = cfs.keyspace.getName();
this.tableName = cfs.name;
this.isIndex = cfs.isIndex();
this.type = type;
}
public CassandraMetricsRegistry.MetricName createMetricName(String metricName)
{
String groupName = TableMetrics.class.getPackage().getName();
String type = isIndex ? "Index" + this.type : this.type;
StringBuilder mbeanName = new StringBuilder();
mbeanName.append(groupName).append(":");
mbeanName.append("type=").append(type);
mbeanName.append(",keyspace=").append(keyspaceName);
mbeanName.append(",scope=").append(tableName);
mbeanName.append(",name=").append(metricName);
return new CassandraMetricsRegistry.MetricName(groupName, type, metricName, keyspaceName + "." + tableName, mbeanName.toString());
}
}
static class AllTableMetricNameFactory implements MetricNameFactory
{
private final String type;
public AllTableMetricNameFactory(String type)
{
this.type = type;
}
public CassandraMetricsRegistry.MetricName createMetricName(String metricName)
{
String groupName = TableMetrics.class.getPackage().getName();
StringBuilder mbeanName = new StringBuilder();
mbeanName.append(groupName).append(":");
mbeanName.append("type=").append(type);
mbeanName.append(",name=").append(metricName);
return new CassandraMetricsRegistry.MetricName(groupName, type, metricName, "all", mbeanName.toString());
}
}
public enum Sampler
{
READS, WRITES
}
}