You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by dj...@apache.org on 2020/06/23 17:24:08 UTC

[cassandra] branch trunk updated: Several mbeans are not unregistered when dropping a keyspace and table

This is an automated email from the ASF dual-hosted git repository.

djoshi pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 1731e2f  Several mbeans are not unregistered when dropping a keyspace and table
1731e2f is described below

commit 1731e2fe84c1da2a6f4b0d1f73b8fd76c88b3acd
Author: Alex <st...@apache.org>
AuthorDate: Mon Apr 15 19:19:05 2019 +0200

    Several mbeans are not unregistered when dropping a keyspace and table
    
    Patch By Alex Deparvu; Reviewed by Caleb Rackliffe, Chris Lohfink and Dinesh Joshi for CASSANDRA-14888
---
 CHANGES.txt                                        |   1 +
 .../apache/cassandra/metrics/KeyspaceMetrics.java  | 275 ++++++++-------------
 .../org/apache/cassandra/metrics/TableMetrics.java | 193 ++++++++-------
 .../cassandra/metrics/KeyspaceMetricsTest.java     |  74 ++++++
 .../apache/cassandra/metrics/TableMetricsTest.java |  45 +++-
 5 files changed, 332 insertions(+), 256 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 1212030..4fa1567 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.0-alpha5
+ * Several mbeans are not unregistered when dropping a keyspace and table (CASSANDRA-14888)
  * Update defaults for server and client TLS settings (CASSANDRA-15262)
  * Differentiate follower/initator in StreamMessageHeader (CASSANDRA-15665)
  * Add a startup check to detect if LZ4 uses java rather than native implementation (CASSANDRA-15884)
diff --git a/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java b/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java
index 9c45dc0..4af26c0 100644
--- a/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java
+++ b/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java
@@ -18,6 +18,7 @@
 package org.apache.cassandra.metrics;
 
 import java.util.Set;
+import java.util.function.ToLongFunction;
 
 import com.codahale.metrics.Counter;
 import com.codahale.metrics.Gauge;
@@ -26,8 +27,9 @@ import com.codahale.metrics.Meter;
 import com.codahale.metrics.Timer;
 import org.apache.cassandra.db.ColumnFamilyStore;
 import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.metrics.CassandraMetricsRegistry.MetricName;
+import org.apache.cassandra.metrics.TableMetrics.ReleasableMetric;
 
-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
 import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
@@ -155,7 +157,7 @@ public class KeyspaceMetrics
     private Keyspace keyspace;
 
     /** set containing names of all the metrics stored here, for releasing later */
-    private Set<String> allMetrics = Sets.newHashSet();
+    private Set<ReleasableMetric> allMetrics = Sets.newHashSet();
 
     /**
      * Creates metrics for given {@link ColumnFamilyStore}.
@@ -166,137 +168,53 @@ public class KeyspaceMetrics
     {
         factory = new KeyspaceMetricNameFactory(ks);
         keyspace = ks;
-        memtableColumnsCount = createKeyspaceGauge("MemtableColumnsCount", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.memtableColumnsCount.getValue();
-            }
-        });
-        memtableLiveDataSize = createKeyspaceGauge("MemtableLiveDataSize", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.memtableLiveDataSize.getValue();
-            }
-        });
-        memtableOnHeapDataSize = createKeyspaceGauge("MemtableOnHeapDataSize", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.memtableOnHeapSize.getValue();
-            }
-        });
-        memtableOffHeapDataSize = createKeyspaceGauge("MemtableOffHeapDataSize", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.memtableOffHeapSize.getValue();
-            }
-        });
-        allMemtablesLiveDataSize = createKeyspaceGauge("AllMemtablesLiveDataSize", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.allMemtablesLiveDataSize.getValue();
-            }
-        });
-        allMemtablesOnHeapDataSize = createKeyspaceGauge("AllMemtablesOnHeapDataSize", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.allMemtablesOnHeapSize.getValue();
-            }
-        });
-        allMemtablesOffHeapDataSize = createKeyspaceGauge("AllMemtablesOffHeapDataSize", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.allMemtablesOffHeapSize.getValue();
-            }
-        });
-        memtableSwitchCount = createKeyspaceGauge("MemtableSwitchCount", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.memtableSwitchCount.getCount();
-            }
-        });
-        pendingCompactions = createKeyspaceGauge("PendingCompactions", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return (long) metric.pendingCompactions.getValue();
-            }
-        });
-        pendingFlushes = createKeyspaceGauge("PendingFlushes", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return (long) metric.pendingFlushes.getCount();
-            }
-        });
-        liveDiskSpaceUsed = createKeyspaceGauge("LiveDiskSpaceUsed", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.liveDiskSpaceUsed.getCount();
-            }
-        });
-        totalDiskSpaceUsed = createKeyspaceGauge("TotalDiskSpaceUsed", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.totalDiskSpaceUsed.getCount();
-            }
-        });
-        bloomFilterDiskSpaceUsed = createKeyspaceGauge("BloomFilterDiskSpaceUsed", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.bloomFilterDiskSpaceUsed.getValue();
-            }
-        });
-        bloomFilterOffHeapMemoryUsed = createKeyspaceGauge("BloomFilterOffHeapMemoryUsed", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.bloomFilterOffHeapMemoryUsed.getValue();
-            }
-        });
-        indexSummaryOffHeapMemoryUsed = createKeyspaceGauge("IndexSummaryOffHeapMemoryUsed", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.indexSummaryOffHeapMemoryUsed.getValue();
-            }
-        });
-        compressionMetadataOffHeapMemoryUsed = createKeyspaceGauge("CompressionMetadataOffHeapMemoryUsed", new MetricValue()
-        {
-            public Long getValue(TableMetrics metric)
-            {
-                return metric.compressionMetadataOffHeapMemoryUsed.getValue();
-            }
-        });
+        memtableColumnsCount = createKeyspaceGauge("MemtableColumnsCount",
+                metric -> metric.memtableColumnsCount.getValue());
+        memtableLiveDataSize = createKeyspaceGauge("MemtableLiveDataSize",
+                metric -> metric.memtableLiveDataSize.getValue());
+        memtableOnHeapDataSize = createKeyspaceGauge("MemtableOnHeapDataSize",
+                metric -> metric.memtableOnHeapSize.getValue());
+        memtableOffHeapDataSize = createKeyspaceGauge("MemtableOffHeapDataSize",
+                metric -> metric.memtableOffHeapSize.getValue());
+        allMemtablesLiveDataSize = createKeyspaceGauge("AllMemtablesLiveDataSize",
+                metric -> metric.allMemtablesLiveDataSize.getValue());
+        allMemtablesOnHeapDataSize = createKeyspaceGauge("AllMemtablesOnHeapDataSize",
+                metric -> metric.allMemtablesOnHeapSize.getValue());
+        allMemtablesOffHeapDataSize = createKeyspaceGauge("AllMemtablesOffHeapDataSize",
+                metric -> metric.allMemtablesOffHeapSize.getValue());
+        memtableSwitchCount = createKeyspaceGauge("MemtableSwitchCount",
+                metric -> metric.memtableSwitchCount.getCount());
+        pendingCompactions = createKeyspaceGauge("PendingCompactions", metric -> metric.pendingCompactions.getValue());
+        pendingFlushes = createKeyspaceGauge("PendingFlushes", metric -> metric.pendingFlushes.getCount());
+        liveDiskSpaceUsed = createKeyspaceGauge("LiveDiskSpaceUsed", metric -> metric.liveDiskSpaceUsed.getCount());
+        totalDiskSpaceUsed = createKeyspaceGauge("TotalDiskSpaceUsed", metric -> metric.totalDiskSpaceUsed.getCount());
+        bloomFilterDiskSpaceUsed = createKeyspaceGauge("BloomFilterDiskSpaceUsed",
+                metric -> metric.bloomFilterDiskSpaceUsed.getValue());
+        bloomFilterOffHeapMemoryUsed = createKeyspaceGauge("BloomFilterOffHeapMemoryUsed",
+                metric -> metric.bloomFilterOffHeapMemoryUsed.getValue());
+        indexSummaryOffHeapMemoryUsed = createKeyspaceGauge("IndexSummaryOffHeapMemoryUsed",
+                metric -> metric.indexSummaryOffHeapMemoryUsed.getValue());
+        compressionMetadataOffHeapMemoryUsed = createKeyspaceGauge("CompressionMetadataOffHeapMemoryUsed",
+                metric -> metric.compressionMetadataOffHeapMemoryUsed.getValue());
+
         // latency metrics for TableMetrics to update
-        readLatency = new LatencyMetrics(factory, "Read");
-        writeLatency = new LatencyMetrics(factory, "Write");
-        rangeLatency = new LatencyMetrics(factory, "Range");
+        readLatency = createLatencyMetrics("Read");
+        writeLatency = createLatencyMetrics("Write");
+        rangeLatency = createLatencyMetrics("Range");
+
         // create histograms for TableMetrics to replicate updates to
-        sstablesPerReadHistogram = Metrics.histogram(factory.createMetricName("SSTablesPerReadHistogram"), true);
-        tombstoneScannedHistogram = Metrics.histogram(factory.createMetricName("TombstoneScannedHistogram"), false);
-        liveScannedHistogram = Metrics.histogram(factory.createMetricName("LiveScannedHistogram"), false);
-        colUpdateTimeDeltaHistogram = Metrics.histogram(factory.createMetricName("ColUpdateTimeDeltaHistogram"), false);
-        viewLockAcquireTime =  Metrics.timer(factory.createMetricName("ViewLockAcquireTime"));
-        viewReadTime = Metrics.timer(factory.createMetricName("ViewReadTime"));
-        // add manually since histograms do not use createKeyspaceGauge method
-        allMetrics.addAll(Lists.newArrayList("SSTablesPerReadHistogram", "TombstoneScannedHistogram", "LiveScannedHistogram"));
+        sstablesPerReadHistogram = createKeyspaceHistogram("SSTablesPerReadHistogram", true);
+        tombstoneScannedHistogram = createKeyspaceHistogram("TombstoneScannedHistogram", false);
+        liveScannedHistogram = createKeyspaceHistogram("LiveScannedHistogram", false);
+        colUpdateTimeDeltaHistogram = createKeyspaceHistogram("ColUpdateTimeDeltaHistogram", false);
+        viewLockAcquireTime = createKeyspaceTimer("ViewLockAcquireTime");
+        viewReadTime = createKeyspaceTimer("ViewReadTime");
 
-        casPrepare = new LatencyMetrics(factory, "CasPrepare");
-        casPropose = new LatencyMetrics(factory, "CasPropose");
-        casCommit = new LatencyMetrics(factory, "CasCommit");
-        writeFailedIdealCL = Metrics.counter(factory.createMetricName("WriteFailedIdealCL"));
-        idealCLWriteLatency = new LatencyMetrics(factory, "IdealCLWrite");
+        casPrepare = createLatencyMetrics("CasPrepare");
+        casPropose = createLatencyMetrics("CasPropose");
+        casCommit = createLatencyMetrics("CasCommit");
+        writeFailedIdealCL = createKeyspaceCounter("WriteFailedIdealCL");
+        idealCLWriteLatency = createLatencyMetrics("IdealCLWrite");
 
         speculativeRetries = createKeyspaceCounter("SpeculativeRetries", metric -> metric.speculativeRetries.getCount());
         speculativeFailedRetries = createKeyspaceCounter("SpeculativeFailedRetries", metric -> metric.speculativeFailedRetries.getCount());
@@ -304,19 +222,19 @@ public class KeyspaceMetrics
         additionalWrites = createKeyspaceCounter("AdditionalWrites", metric -> metric.additionalWrites.getCount());
         repairsStarted = createKeyspaceCounter("RepairJobsStarted", metric -> metric.repairsStarted.getCount());
         repairsCompleted = createKeyspaceCounter("RepairJobsCompleted", metric -> metric.repairsCompleted.getCount());
-        repairTime = Metrics.timer(factory.createMetricName("RepairTime"));
-        repairPrepareTime = Metrics.timer(factory.createMetricName("RepairPrepareTime"));
-        anticompactionTime = Metrics.timer(factory.createMetricName("AntiCompactionTime"));
-        validationTime = Metrics.timer(factory.createMetricName("ValidationTime"));
-        repairSyncTime = Metrics.timer(factory.createMetricName("RepairSyncTime"));
-        partitionsValidated = Metrics.histogram(factory.createMetricName("PartitionsValidated"), false);
-        bytesValidated = Metrics.histogram(factory.createMetricName("BytesValidated"), false);
+        repairTime =createKeyspaceTimer("RepairTime");
+        repairPrepareTime = createKeyspaceTimer("RepairPrepareTime");
+        anticompactionTime = createKeyspaceTimer("AntiCompactionTime");
+        validationTime = createKeyspaceTimer("ValidationTime");
+        repairSyncTime = createKeyspaceTimer("RepairSyncTime");
+        partitionsValidated = createKeyspaceHistogram("PartitionsValidated", false);
+        bytesValidated = createKeyspaceHistogram("BytesValidated", false);
 
-        confirmedRepairedInconsistencies = Metrics.meter(factory.createMetricName("RepairedDataInconsistenciesConfirmed"));
-        unconfirmedRepairedInconsistencies = Metrics.meter(factory.createMetricName("RepairedDataInconsistenciesUnconfirmed"));
+        confirmedRepairedInconsistencies = createKeyspaceMeter("RepairedDataInconsistenciesConfirmed");
+        unconfirmedRepairedInconsistencies = createKeyspaceMeter("RepairedDataInconsistenciesUnconfirmed");
 
-        repairedDataTrackingOverreadRows = Metrics.histogram(factory.createMetricName("RepairedOverreadRows"), false);
-        repairedDataTrackingOverreadTime = Metrics.timer(factory.createMetricName("RepairedOverreadTime"));
+        repairedDataTrackingOverreadRows = createKeyspaceHistogram("RepairedOverreadRows", false);
+        repairedDataTrackingOverreadTime = createKeyspaceTimer("RepairedOverreadTime");
     }
 
     /**
@@ -324,28 +242,10 @@ public class KeyspaceMetrics
      */
     public void release()
     {
-        for(String name : allMetrics)
+        for (ReleasableMetric metric : allMetrics)
         {
-            Metrics.remove(factory.createMetricName(name));
+            metric.release();
         }
-        // latency metrics contain multiple metrics internally and need to be released manually
-        readLatency.release();
-        writeLatency.release();
-        rangeLatency.release();
-        idealCLWriteLatency.release();
-    }
-
-    /**
-     * Represents a column family metric value.
-     */
-    private interface MetricValue
-    {
-        /**
-         * get value of a metric
-         * @param metric of a column family in this keyspace
-         * @return current value of a metric
-         */
-        public Long getValue(TableMetrics metric);
     }
 
     /**
@@ -354,9 +254,9 @@ public class KeyspaceMetrics
      * @param extractor
      * @return Gauge&gt;Long> that computes sum of MetricValue.getValue()
      */
-    private Gauge<Long> createKeyspaceGauge(String name, final MetricValue extractor)
+    private Gauge<Long> createKeyspaceGauge(String name, final ToLongFunction<TableMetrics> extractor)
     {
-        allMetrics.add(name);
+        allMetrics.add(() -> releaseMetric(name));
         return Metrics.register(factory.createMetricName(name), new Gauge<Long>()
         {
             public Long getValue()
@@ -364,7 +264,7 @@ public class KeyspaceMetrics
                 long sum = 0;
                 for (ColumnFamilyStore cf : keyspace.getColumnFamilyStores())
                 {
-                    sum += extractor.getValue(cf.metric);
+                    sum += extractor.applyAsLong(cf.metric);
                 }
                 return sum;
             }
@@ -377,9 +277,9 @@ public class KeyspaceMetrics
      * @param extractor
      * @return Counter that computes sum of MetricValue.getValue()
      */
-    private Counter createKeyspaceCounter(String name, final MetricValue extractor)
+    private Counter createKeyspaceCounter(String name, final ToLongFunction<TableMetrics> extractor)
     {
-        allMetrics.add(name);
+        allMetrics.add(() -> releaseMetric(name));
         return Metrics.register(factory.createMetricName(name), new Counter()
         {
             @Override
@@ -388,13 +288,49 @@ public class KeyspaceMetrics
                 long sum = 0;
                 for (ColumnFamilyStore cf : keyspace.getColumnFamilyStores())
                 {
-                    sum += extractor.getValue(cf.metric);
+                    sum += extractor.applyAsLong(cf.metric);
                 }
                 return sum;
             }
         });
     }
 
+    protected Counter createKeyspaceCounter(String name)
+    {
+        allMetrics.add(() -> releaseMetric(name));
+        return Metrics.counter(factory.createMetricName(name));
+    }
+
+    protected Histogram createKeyspaceHistogram(String name, boolean considerZeroes)
+    {
+        allMetrics.add(() -> releaseMetric(name));
+        return Metrics.histogram(factory.createMetricName(name), considerZeroes);
+    }
+
+    protected Timer createKeyspaceTimer(String name)
+    {
+        allMetrics.add(() -> releaseMetric(name));
+        return Metrics.timer(factory.createMetricName(name));
+    }
+
+    protected Meter createKeyspaceMeter(String name)
+    {
+        allMetrics.add(() -> releaseMetric(name));
+        return Metrics.meter(factory.createMetricName(name));
+    }
+
+    private LatencyMetrics createLatencyMetrics(String name)
+    {
+        LatencyMetrics metric = new LatencyMetrics(factory, name);
+        allMetrics.add(() -> metric.release());
+        return metric;
+    }
+
+    private void releaseMetric(String name)
+    {
+        Metrics.remove(factory.createMetricName(name));
+    }
+
     static class KeyspaceMetricNameFactory implements MetricNameFactory
     {
         private final String keyspaceName;
@@ -404,7 +340,8 @@ public class KeyspaceMetrics
             this.keyspaceName = ks.getName();
         }
 
-        public CassandraMetricsRegistry.MetricName createMetricName(String metricName)
+        @Override
+        public MetricName createMetricName(String metricName)
         {
             String groupName = TableMetrics.class.getPackage().getName();
 
@@ -414,7 +351,7 @@ public class KeyspaceMetrics
             mbeanName.append(",keyspace=").append(keyspaceName);
             mbeanName.append(",name=").append(metricName);
 
-            return new CassandraMetricsRegistry.MetricName(groupName, "keyspace", metricName, keyspaceName, mbeanName.toString());
+            return new MetricName(groupName, "keyspace", metricName, keyspaceName, mbeanName.toString());
         }
     }
 }
diff --git a/src/java/org/apache/cassandra/metrics/TableMetrics.java b/src/java/org/apache/cassandra/metrics/TableMetrics.java
index 449b6c0..bfb261d 100644
--- a/src/java/org/apache/cassandra/metrics/TableMetrics.java
+++ b/src/java/org/apache/cassandra/metrics/TableMetrics.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
 import java.util.EnumMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -33,7 +32,7 @@ import java.util.function.Predicate;
 
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
-
+import com.google.common.collect.Sets;
 import com.codahale.metrics.Timer;
 
 import org.apache.cassandra.db.ColumnFamilyStore;
@@ -338,9 +337,9 @@ public class TableMetrics
     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.
+     * Stores all metrics created that can be used when unregistering
      */
-    public final static Map<String, String> all = Maps.newHashMap();
+    public final static Set<ReleasableMetric> all = Sets.newHashSet();
 
     private interface GetHistogram
     {
@@ -494,52 +493,45 @@ public class TableMetrics
             }
         });
         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();
-                                                           try(ColumnFamilyStore.RefViewFragment refViewFragment = cfs.selectAndReference(View.selectFunction(SSTableSet.CANONICAL)))
-                                                           {
-                                                               return SSTableReader.getApproximateKeyCount(refViewFragment.sstables) + 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.getEstimatedCellPerPartitionCount();
-                                                                     }
-                                                                 });
+        estimatedPartitionSizeHistogram = createTableGauge("EstimatedPartitionSizeHistogram", "EstimatedRowSizeHistogram", new Gauge<long[]>()
+        {
+            public long[] getValue()
+            {
+                return combineHistograms(cfs.getSSTables(SSTableSet.CANONICAL), new GetHistogram()
+                {
+                    public EstimatedHistogram getHistogram(SSTableReader reader)
+                    {
+                        return reader.getEstimatedPartitionSize();
+                    }
+                });
             }
-        });
+        }, null);
+        estimatedPartitionCount = createTableGauge("EstimatedPartitionCount", "EstimatedRowCount", new Gauge<Long>()
+        {
+            public Long getValue()
+            {
+                long memtablePartitions = 0;
+                for (Memtable memtable : cfs.getTracker().getView().getAllMemtables())
+                   memtablePartitions += memtable.partitionCount();
+                try(ColumnFamilyStore.RefViewFragment refViewFragment = cfs.selectAndReference(View.selectFunction(SSTableSet.CANONICAL)))
+                {
+                    return SSTableReader.getApproximateKeyCount(refViewFragment.sstables) + memtablePartitions;
+                }
+            }
+        }, null);
+        estimatedColumnCountHistogram = createTableGauge("EstimatedColumnCountHistogram", "EstimatedColumnCountHistogram", new Gauge<long[]>()
+        {
+            public long[] getValue()
+            {
+                return combineHistograms(cfs.getSSTables(SSTableSet.CANONICAL), new GetHistogram()
+                {
+                    public EstimatedHistogram getHistogram(SSTableReader reader)
+                    {
+                        return reader.getEstimatedCellPerPartitionCount();
+                    }
+                });
+            }
+        }, null);
         sstablesPerReadHistogram = createTableHistogram("SSTablesPerReadHistogram", cfs.keyspace.metric.sstablesPerReadHistogram, true);
         compressionRatio = createTableGauge("CompressionRatio", new Gauge<Double>()
         {
@@ -613,9 +605,9 @@ public class TableMetrics
             }
         });
 
-        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);
+        readLatency = createLatencyMetrics("Read", cfs.keyspace.metric.readLatency, globalReadLatency);
+        writeLatency = createLatencyMetrics("Write", cfs.keyspace.metric.writeLatency, globalWriteLatency);
+        rangeLatency = createLatencyMetrics("Range", cfs.keyspace.metric.rangeLatency, globalRangeLatency);
         pendingFlushes = createTableCounter("PendingFlushes");
         bytesFlushed = createTableCounter("BytesFlushed");
 
@@ -863,9 +855,7 @@ public class TableMetrics
         additionalWrites = createTableCounter("AdditionalWrites");
         additionalWriteLatencyNanos = createTableGauge("AdditionalWriteLatencyNanos", () -> cfs.additionalWriteLatencyNanos);
 
-        keyCacheHitRate = Metrics.register(factory.createMetricName("KeyCacheHitRate"),
-                                           aliasFactory.createMetricName("KeyCacheHitRate"),
-                                           new RatioGauge()
+        keyCacheHitRate = createTableGauge("KeyCacheHitRate", "KeyCacheHitRate", new RatioGauge()
         {
             @Override
             public Ratio getRatio()
@@ -888,14 +878,14 @@ public class TableMetrics
                     requests += sstable.getKeyCacheRequest();
                 return Math.max(requests, 1); // to avoid NaN.
             }
-        });
+        }, null);
         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"));
-        coordinatorWriteLatency = Metrics.timer(factory.createMetricName("CoordinatorWriteLatency"));
-        waitingOnFreeMemtableSpace = Metrics.histogram(factory.createMetricName("WaitingOnFreeMemtableSpace"), false);
+        coordinatorReadLatency = createTableTimer("CoordinatorReadLatency");
+        coordinatorScanLatency = createTableTimer("CoordinatorScanLatency");
+        coordinatorWriteLatency = createTableTimer("CoordinatorWriteLatency");
+        waitingOnFreeMemtableSpace = createTableHistogram("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
@@ -926,9 +916,9 @@ public class TableMetrics
 
         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);
+        casPrepare = createLatencyMetrics("CasPrepare", cfs.keyspace.metric.casPrepare);
+        casPropose = createLatencyMetrics("CasPropose", cfs.keyspace.metric.casPropose);
+        casCommit = createLatencyMetrics("CasCommit", cfs.keyspace.metric.casCommit);
 
         repairsStarted = createTableCounter("RepairJobsStarted");
         repairsCompleted = createTableCounter("RepairJobsCompleted");
@@ -981,28 +971,10 @@ public class TableMetrics
      */
     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);
-            }
+        for (ReleasableMetric entry : all)
+        {
+            entry.release();
         }
-        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("CoordinatorWriteLatency"), aliasFactory.createMetricName("CoordinatorWriteLatency"));
-        Metrics.remove(factory.createMetricName("WaitingOnFreeMemtableSpace"), aliasFactory.createMetricName("WaitingOnFreeMemtableSpace"));
     }
 
 
@@ -1038,7 +1010,7 @@ public class TableMetrics
     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))
+        if (register(name, alias, cfGauge) && globalGauge != null)
         {
             Metrics.register(globalFactory.createMetricName(name), globalAliasFactory.createMetricName(alias), globalGauge);
         }
@@ -1135,6 +1107,18 @@ public class TableMetrics
                                                     considerZeroes));
     }
 
+    protected Histogram createTableHistogram(String name, boolean considerZeroes)
+    {
+        return createTableHistogram(name, name, considerZeroes);
+    }
+
+    protected Histogram createTableHistogram(String name, String alias, boolean considerZeroes)
+    {
+        Histogram tableHistogram = Metrics.histogram(factory.createMetricName(name), aliasFactory.createMetricName(alias), considerZeroes);
+        register(name, alias, tableHistogram);
+        return tableHistogram;
+    }
+
     protected TableTimer createTableTimer(String name, Timer keyspaceTimer)
     {
         return createTableTimer(name, name, keyspaceTimer);
@@ -1150,6 +1134,18 @@ public class TableMetrics
                                             globalAliasFactory.createMetricName(alias)));
     }
 
+    protected Timer createTableTimer(String name)
+    {
+        return createTableTimer(name, name);
+    }
+
+    protected Timer createTableTimer(String name, String alias)
+    {
+        Timer tableTimer = Metrics.timer(factory.createMetricName(name), aliasFactory.createMetricName(alias));
+        register(name, alias, tableTimer);
+        return tableTimer;
+    }
+
     protected TableMeter createTableMeter(String name, Meter keyspaceMeter)
     {
         return createTableMeter(name, name, keyspaceMeter);
@@ -1165,6 +1161,13 @@ public class TableMetrics
                                             globalAliasFactory.createMetricName(alias)));
     }
 
+    private LatencyMetrics createLatencyMetrics(String namePrefix, LatencyMetrics ... parents)
+    {
+        LatencyMetrics metric = new LatencyMetrics(factory, namePrefix, parents);
+        all.add(() -> metric.release());
+        return metric;
+    }
+
     /**
      * Registers a metric to be removed when unloading CF.
      * @return true if first time metric with that name has been registered
@@ -1173,10 +1176,22 @@ public class TableMetrics
     {
         boolean ret = allTableMetrics.putIfAbsent(name, ConcurrentHashMap.newKeySet()) == null;
         allTableMetrics.get(name).add(metric);
-        all.put(name, alias);
+        all.add(() -> releaseMetric(name, alias));
         return ret;
     }
 
+    private void releaseMetric(String metricName, String metricAlias)
+    {
+        CassandraMetricsRegistry.MetricName name = factory.createMetricName(metricName);
+        CassandraMetricsRegistry.MetricName alias = aliasFactory.createMetricName(metricAlias);
+        final Metric metric = Metrics.getMetrics().get(name.getMetricName());
+        if (metric != null)
+        {   // Metric will be null if we are releasing a view metric.  Views have null for ViewLockAcquireTime and ViewLockReadTime
+            allTableMetrics.get(metricName).remove(metric);
+            Metrics.remove(name, alias);
+        }
+    }
+
     public static class TableMeter
     {
         public final Meter[] all;
@@ -1307,4 +1322,10 @@ public class TableMetrics
             return new CassandraMetricsRegistry.MetricName(groupName, type, metricName, "all", mbeanName.toString());
         }
     }
+
+    @FunctionalInterface
+    public interface ReleasableMetric
+    {
+        void release();
+    }
 }
diff --git a/test/unit/org/apache/cassandra/metrics/KeyspaceMetricsTest.java b/test/unit/org/apache/cassandra/metrics/KeyspaceMetricsTest.java
new file mode 100644
index 0000000..ae92146
--- /dev/null
+++ b/test/unit/org/apache/cassandra/metrics/KeyspaceMetricsTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.service.EmbeddedCassandraService;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Session;
+
+public class KeyspaceMetricsTest extends SchemaLoader
+{
+    private static Session session;
+
+    @BeforeClass()
+    public static void setup() throws ConfigurationException, IOException
+    {
+        Schema.instance.clear();
+
+        EmbeddedCassandraService cassandra = new EmbeddedCassandraService();
+        cassandra.start();
+
+        Cluster cluster = Cluster.builder().addContactPoint("127.0.0.1").withPort(DatabaseDescriptor.getNativeTransportPort()).build();
+        session = cluster.connect();
+    }
+
+    @Test
+    public void testMetricsCleanupOnDrop()
+    {
+        String keyspace = "keyspacemetricstest_metrics_cleanup";
+        CassandraMetricsRegistry registry = CassandraMetricsRegistry.Metrics;
+        Supplier<Stream<String>> metrics = () -> registry.getNames().stream().filter(m -> m.contains(keyspace));
+
+        // no metrics before creating
+        assertEquals(0, metrics.get().count());
+
+        session.execute(String.format("CREATE KEYSPACE %s WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };", keyspace));
+        // some metrics
+        assertTrue(metrics.get().count() > 0);
+
+        session.execute(String.format("DROP KEYSPACE %s;", keyspace));
+        // no metrics after drop
+        assertEquals(metrics.get().collect(Collectors.joining(",")), 0, metrics.get().count());
+    }
+}
diff --git a/test/unit/org/apache/cassandra/metrics/TableMetricsTest.java b/test/unit/org/apache/cassandra/metrics/TableMetricsTest.java
index c5434fe..56ad401 100644
--- a/test/unit/org/apache/cassandra/metrics/TableMetricsTest.java
+++ b/test/unit/org/apache/cassandra/metrics/TableMetricsTest.java
@@ -19,6 +19,9 @@
 package org.apache.cassandra.metrics;
 
 import java.io.IOException;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -235,4 +238,44 @@ public class TableMetricsTest extends SchemaLoader
     private static void assertGreaterThan(double actual, double expectedLessThan) {
         assertTrue("Expected " + actual + " > " + expectedLessThan, actual > expectedLessThan);
     }
-}
\ No newline at end of file
+
+    @Test
+    public void testMetricsCleanupOnDrop()
+    {
+        String tableName = TABLE + "_metrics_cleanup";
+        CassandraMetricsRegistry registry = CassandraMetricsRegistry.Metrics;
+        Supplier<Stream<String>> metrics = () -> registry.getNames().stream().filter(m -> m.contains(tableName));
+
+        // no metrics before creating
+        assertEquals(0, metrics.get().count());
+
+        recreateTable(tableName);
+        // some metrics
+        assertTrue(metrics.get().count() > 0);
+
+        session.execute(String.format("DROP TABLE IF EXISTS %s.%s", KEYSPACE, tableName));
+        // no metrics after drop
+        assertEquals(metrics.get().collect(Collectors.joining(",")), 0, metrics.get().count());
+    }
+
+    @Test
+    public void testViewMetricsCleanupOnDrop()
+    {
+        String tableName = TABLE + "_metrics_cleanup";
+        String viewName = TABLE + "_materialized_view_cleanup";
+        CassandraMetricsRegistry registry = CassandraMetricsRegistry.Metrics;
+        Supplier<Stream<String>> metrics = () -> registry.getNames().stream().filter(m -> m.contains(viewName));
+
+        // no metrics before creating
+        assertEquals(0, metrics.get().count());
+
+        recreateTable(tableName);
+        session.execute(String.format("CREATE MATERIALIZED VIEW %s.%s AS SELECT id,val1 FROM %s.%s WHERE id IS NOT NULL AND val1 IS NOT NULL PRIMARY KEY (id,val1);", KEYSPACE, viewName, KEYSPACE, tableName));
+        // some metrics
+        assertTrue(metrics.get().count() > 0);
+
+        session.execute(String.format("DROP MATERIALIZED VIEW IF EXISTS %s.%s;", KEYSPACE, viewName));
+        // no metrics after drop
+        assertEquals(metrics.get().collect(Collectors.joining(",")), 0, metrics.get().count());
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org