Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up
Find file
Copy path
cassandra/src/java/org/apache/cassandra/metrics/TableMetrics.java /
Find file
Copy path
Fetching contributors…
| /* | |
| * 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 cells 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 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); | |
| } | |
| 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()); | |
| allTableMetrics.get(entry.getKey()).remove(Metrics.getMetrics().get(name.getMetricName())); | |
| 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; | |
| } | |
| /** | |
| * 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 | |
| } | |
| } |