This is an automated email from the ASF dual-hosted git repository. mmuzaf 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 8619010cdc Reduce heap usage occupied by the metrics 8619010cdc is described below commit 8619010cdca8bce471754e4fbeb861f036535007 Author: Maxim Muzafarov <maxmu...@gmail.com> AuthorDate: Tue Apr 30 22:15:52 2024 +0200 Reduce heap usage occupied by the metrics patch by Maxim Muzafarov; reviewed by Caleb Rackliffe for CASSANDRA-19567 --- CHANGES.txt | 1 + .../org/apache/cassandra/db/ColumnFamilyStore.java | 25 +- .../org/apache/cassandra/db/memtable/Memtable.java | 23 +- .../apache/cassandra/db/memtable/TrieMemtable.java | 7 +- .../db/virtual/walker/CounterMetricRowWalker.java | 15 +- .../db/virtual/walker/GaugeMetricRowWalker.java | 15 +- .../virtual/walker/HistogramMetricRowWalker.java | 15 +- .../db/virtual/walker/MeterMetricRowWalker.java | 15 +- .../db/virtual/walker/MetricGroupRowWalker.java | 15 +- .../db/virtual/walker/MetricRowWalker.java | 15 +- .../db/virtual/walker/ThreadPoolRowWalker.java | 15 +- .../db/virtual/walker/TimerMetricRowWalker.java | 15 +- .../index/sai/metrics/AbstractMetrics.java | 27 +- .../index/sai/metrics/ColumnQueryMetrics.java | 24 +- .../metrics/CassandraMetricsRegistry.java | 272 +++++++-------------- .../apache/cassandra/metrics/ClientMetrics.java | 9 +- .../apache/cassandra/metrics/KeyspaceMetrics.java | 55 ++--- .../org/apache/cassandra/metrics/TableMetrics.java | 108 ++++---- .../cassandra/metrics/TrieMemtableMetricsView.java | 42 ++-- .../cassandra/service/NativeTransportService.java | 1 + .../walker/CollectionEntryTestRowWalker.java | 15 +- .../walker/PartitionEntryTestRowWalker.java | 15 +- .../metrics/CassandraMetricsRegistryTest.java | 48 ++-- 23 files changed, 349 insertions(+), 443 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ed314c60d3..02509ad4dd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.1 + * Reduce heap usage occupied by the metrics (CASSANDRA-19567) * Improve handling of transient replicas during range movements (CASSANDRA-19344) * Enable debounced internode log requests to be cancelled at shutdown (CASSANDRA-19514) * Correctly set last modified epoch when combining multistep operations into a single step (CASSANDRA-19538) diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index be6136dd4a..67ab079a1e 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -90,14 +90,14 @@ import org.apache.cassandra.db.compaction.CompactionStrategyManager; import org.apache.cassandra.db.compaction.OperationType; import org.apache.cassandra.db.filter.ClusteringIndexFilter; import org.apache.cassandra.db.filter.DataLimits; -import org.apache.cassandra.db.memtable.Flushing; -import org.apache.cassandra.db.memtable.Memtable; -import org.apache.cassandra.db.memtable.ShardBoundaries; import org.apache.cassandra.db.lifecycle.LifecycleNewTracker; import org.apache.cassandra.db.lifecycle.LifecycleTransaction; import org.apache.cassandra.db.lifecycle.SSTableSet; import org.apache.cassandra.db.lifecycle.Tracker; import org.apache.cassandra.db.lifecycle.View; +import org.apache.cassandra.db.memtable.Flushing; +import org.apache.cassandra.db.memtable.Memtable; +import org.apache.cassandra.db.memtable.ShardBoundaries; import org.apache.cassandra.db.partitions.CachedPartition; import org.apache.cassandra.db.partitions.PartitionUpdate; import org.apache.cassandra.db.repair.CassandraTableRepairManager; @@ -320,6 +320,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner private final Directories directories; public final TableMetrics metric; + private final Runnable memtableMetricsReleaser; public volatile long sampleReadLatencyMicros; public volatile long additionalWriteLatencyMicros; @@ -506,14 +507,11 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner logger.info("Initializing {}.{}", getKeyspaceName(), name); - // Create Memtable and its metrics object only on online - Memtable initialMemtable = null; - TableMetrics.ReleasableMetric memtableMetrics = null; - if (DatabaseDescriptor.isDaemonInitialized()) - { - initialMemtable = createMemtable(new AtomicReference<>(CommitLog.instance.getCurrentPosition())); - memtableMetrics = memtableFactory.createMemtableMetrics(metadata); - } + Memtable initialMemtable = DatabaseDescriptor.isDaemonInitialized() ? + createMemtable(new AtomicReference<>(CommitLog.instance.getCurrentPosition())) : + null; + memtableMetricsReleaser = memtableFactory.createMemtableMetricsReleaser(metadata); + data = new Tracker(this, initialMemtable, loadSSTables); // Note that this needs to happen before we load the first sstables, or the global sstable tracker will not @@ -545,7 +543,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner indexManager.addIndex(info, true); } - metric = new TableMetrics(this, memtableMetrics); + // See CASSANDRA-16228. We need to ensure that metrics are exposed after the CFS is initialized, + // so the order of the following line is important and should not be moved. + metric = new TableMetrics(this); if (data.loadsstables) { @@ -749,6 +749,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner } // unregister metrics + memtableMetricsReleaser.run(); metric.release(); } diff --git a/src/java/org/apache/cassandra/db/memtable/Memtable.java b/src/java/org/apache/cassandra/db/memtable/Memtable.java index 06dfe19976..f722ec20a9 100644 --- a/src/java/org/apache/cassandra/db/memtable/Memtable.java +++ b/src/java/org/apache/cassandra/db/memtable/Memtable.java @@ -19,7 +19,6 @@ package org.apache.cassandra.db.memtable; import java.util.concurrent.atomic.AtomicReference; - import javax.annotation.concurrent.NotThreadSafe; import org.apache.cassandra.db.ColumnFamilyStore; @@ -33,7 +32,6 @@ import org.apache.cassandra.db.rows.EncodingStats; import org.apache.cassandra.db.rows.UnfilteredSource; import org.apache.cassandra.index.transactions.UpdateTransaction; import org.apache.cassandra.io.sstable.format.SSTableWriter; -import org.apache.cassandra.metrics.TableMetrics; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.TableMetadataRef; import org.apache.cassandra.utils.FBUtilities; @@ -84,6 +82,16 @@ public interface Memtable extends Comparable<Memtable>, UnfilteredSource */ Memtable create(AtomicReference<CommitLogPosition> commitLogLowerBound, TableMetadataRef metadaRef, Owner owner); + /** + * Create a release action for the memtable's metrics. This is used to release any resources that are not needed. + * @param metadataRef Pointer to the up-to-date table metadata. + * @return Runnable that releases the metrics resources. + */ + default Runnable createMemtableMetricsReleaser(TableMetadataRef metadataRef) + { + return () -> {}; + } + /** * If the memtable can achieve write durability directly (i.e. using some feature other than the commitlog, e.g. * persistent memory), it can return true here, in which case the commit log will not store mutations in this @@ -137,17 +145,6 @@ public interface Memtable extends Comparable<Memtable>, UnfilteredSource { return false; } - - /** - * Override this method to include implementation-specific memtable metrics in the table metrics. - * - * Memtable metrics lifecycle matches table lifecycle. It is the table that owns the metrics and - * decides when to release them. - */ - default TableMetrics.ReleasableMetric createMemtableMetrics(TableMetadataRef metadataRef) - { - return null; - } } /** diff --git a/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java b/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java index 83b02db06a..a8fc54b891 100644 --- a/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java @@ -64,7 +64,6 @@ import org.apache.cassandra.dht.Range; import org.apache.cassandra.index.transactions.UpdateTransaction; import org.apache.cassandra.io.compress.BufferType; import org.apache.cassandra.io.sstable.SSTableReadsListener; -import org.apache.cassandra.metrics.TableMetrics; import org.apache.cassandra.metrics.TrieMemtableMetricsView; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.TableMetadataRef; @@ -679,10 +678,10 @@ public class TrieMemtable extends AbstractShardedMemtable } @Override - public TableMetrics.ReleasableMetric createMemtableMetrics(TableMetadataRef metadataRef) + public Runnable createMemtableMetricsReleaser(TableMetadataRef metadataRef) { - TrieMemtableMetricsView metrics = new TrieMemtableMetricsView(metadataRef.keyspace, metadataRef.name); - return metrics::release; + // Metrics are the same for all shards, so we can release them all at once. + return () -> TrieMemtableMetricsView.release(metadataRef.keyspace, metadataRef.name); } public boolean equals(Object o) diff --git a/src/java/org/apache/cassandra/db/virtual/walker/CounterMetricRowWalker.java b/src/java/org/apache/cassandra/db/virtual/walker/CounterMetricRowWalker.java index 8f6876afa3..7b8133eac2 100644 --- a/src/java/org/apache/cassandra/db/virtual/walker/CounterMetricRowWalker.java +++ b/src/java/org/apache/cassandra/db/virtual/walker/CounterMetricRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/src/java/org/apache/cassandra/db/virtual/walker/GaugeMetricRowWalker.java b/src/java/org/apache/cassandra/db/virtual/walker/GaugeMetricRowWalker.java index e510f33b5d..5d4b5da29a 100644 --- a/src/java/org/apache/cassandra/db/virtual/walker/GaugeMetricRowWalker.java +++ b/src/java/org/apache/cassandra/db/virtual/walker/GaugeMetricRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/src/java/org/apache/cassandra/db/virtual/walker/HistogramMetricRowWalker.java b/src/java/org/apache/cassandra/db/virtual/walker/HistogramMetricRowWalker.java index 9347a0a715..3efafd99a9 100644 --- a/src/java/org/apache/cassandra/db/virtual/walker/HistogramMetricRowWalker.java +++ b/src/java/org/apache/cassandra/db/virtual/walker/HistogramMetricRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/src/java/org/apache/cassandra/db/virtual/walker/MeterMetricRowWalker.java b/src/java/org/apache/cassandra/db/virtual/walker/MeterMetricRowWalker.java index 3039bb924b..f5aeaa68ce 100644 --- a/src/java/org/apache/cassandra/db/virtual/walker/MeterMetricRowWalker.java +++ b/src/java/org/apache/cassandra/db/virtual/walker/MeterMetricRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/src/java/org/apache/cassandra/db/virtual/walker/MetricGroupRowWalker.java b/src/java/org/apache/cassandra/db/virtual/walker/MetricGroupRowWalker.java index 0cb7c6f345..4327df7d22 100644 --- a/src/java/org/apache/cassandra/db/virtual/walker/MetricGroupRowWalker.java +++ b/src/java/org/apache/cassandra/db/virtual/walker/MetricGroupRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/src/java/org/apache/cassandra/db/virtual/walker/MetricRowWalker.java b/src/java/org/apache/cassandra/db/virtual/walker/MetricRowWalker.java index f5e46c021b..84cda02398 100644 --- a/src/java/org/apache/cassandra/db/virtual/walker/MetricRowWalker.java +++ b/src/java/org/apache/cassandra/db/virtual/walker/MetricRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/src/java/org/apache/cassandra/db/virtual/walker/ThreadPoolRowWalker.java b/src/java/org/apache/cassandra/db/virtual/walker/ThreadPoolRowWalker.java index 68cbc8c693..5435df9747 100644 --- a/src/java/org/apache/cassandra/db/virtual/walker/ThreadPoolRowWalker.java +++ b/src/java/org/apache/cassandra/db/virtual/walker/ThreadPoolRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/src/java/org/apache/cassandra/db/virtual/walker/TimerMetricRowWalker.java b/src/java/org/apache/cassandra/db/virtual/walker/TimerMetricRowWalker.java index 418495e24b..e4e15ffe67 100644 --- a/src/java/org/apache/cassandra/db/virtual/walker/TimerMetricRowWalker.java +++ b/src/java/org/apache/cassandra/db/virtual/walker/TimerMetricRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/src/java/org/apache/cassandra/index/sai/metrics/AbstractMetrics.java b/src/java/org/apache/cassandra/index/sai/metrics/AbstractMetrics.java index b305720ebb..17fd99291c 100644 --- a/src/java/org/apache/cassandra/index/sai/metrics/AbstractMetrics.java +++ b/src/java/org/apache/cassandra/index/sai/metrics/AbstractMetrics.java @@ -17,14 +17,13 @@ */ package org.apache.cassandra.index.sai.metrics; -import java.util.ArrayList; -import java.util.List; - +import com.codahale.metrics.MetricRegistry; import org.apache.cassandra.index.sai.utils.IndexIdentifier; import org.apache.cassandra.metrics.CassandraMetricsRegistry; import org.apache.cassandra.metrics.DefaultNameFactory; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.resolveShortMetricName; public abstract class AbstractMetrics { @@ -34,7 +33,6 @@ public abstract class AbstractMetrics protected final String table; private final String index; private final String scope; - protected final List<String> tracked = new ArrayList<>(); AbstractMetrics(IndexIdentifier indexIdentifier, String scope) { @@ -57,8 +55,8 @@ public abstract class AbstractMetrics public void release() { - tracked.forEach(Metrics::remove); - tracked.clear(); + Metrics.removeIfMatch(fullName -> resolveShortMetricName(fullName, DefaultNameFactory.GROUP_NAME, TYPE, null), + this::createMetricName, m -> {}); } protected CassandraMetricsRegistry.MetricName createMetricName(String name) @@ -68,17 +66,12 @@ public abstract class AbstractMetrics protected CassandraMetricsRegistry.MetricName createMetricName(String name, String scope) { - String metricScope = keyspace + '.' + table; - if (index != null) - { - metricScope += '.' + index; - } - metricScope += '.' + scope + '.' + name; - - CassandraMetricsRegistry.MetricName metricName = new CassandraMetricsRegistry.MetricName(DefaultNameFactory.GROUP_NAME, - TYPE, name, metricScope, createMBeanName(name, scope)); - tracked.add(metricName.getMetricName()); - return metricName; + assert name.indexOf('.') == -1 : String.format("Metric name '%s' should not contain '.'", name); + return new CassandraMetricsRegistry.MetricName(DefaultNameFactory.GROUP_NAME, + TYPE, + name, + MetricRegistry.name(keyspace, table, index, scope, name), + createMBeanName(name, scope)); } private String createMBeanName(String name, String scope) diff --git a/src/java/org/apache/cassandra/index/sai/metrics/ColumnQueryMetrics.java b/src/java/org/apache/cassandra/index/sai/metrics/ColumnQueryMetrics.java index f922231d0b..d2438510d9 100644 --- a/src/java/org/apache/cassandra/index/sai/metrics/ColumnQueryMetrics.java +++ b/src/java/org/apache/cassandra/index/sai/metrics/ColumnQueryMetrics.java @@ -27,6 +27,9 @@ import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; public abstract class ColumnQueryMetrics extends AbstractMetrics { + private static final String POSTING_DECODES = "PostingDecodes"; + private static final String NUM_POSTINGS = "NumPostings"; + protected ColumnQueryMetrics(IndexIdentifier indexIdentifier) { super(indexIdentifier, "ColumnQueryMetrics"); @@ -49,11 +52,18 @@ public abstract class ColumnQueryMetrics extends AbstractMetrics termsTraversalTotalTime = Metrics.timer(createMetricName("TermsLookupLatency")); - Meter postingDecodes = Metrics.meter(createMetricName("PostingDecodes", TRIE_POSTINGS_TYPE)); + Meter postingDecodes = Metrics.meter(createMetricName(POSTING_DECODES, TRIE_POSTINGS_TYPE)); postingsListener = new PostingListEventsMetrics(postingDecodes); } + @Override + public void release() + { + super.release(); + Metrics.remove(createMetricName(POSTING_DECODES, TRIE_POSTINGS_TYPE)); + } + @Override public void onSegmentHit() { } @@ -90,13 +100,21 @@ public abstract class ColumnQueryMetrics extends AbstractMetrics intersectionLatency = Metrics.timer(createMetricName("BalancedTreeIntersectionLatency")); intersectionEarlyExits = Metrics.meter(createMetricName("BalancedTreeIntersectionEarlyExits")); - postingsNumPostings = Metrics.meter(createMetricName("NumPostings", BALANCED_TREE_POSTINGS_TYPE)); + postingsNumPostings = Metrics.meter(createMetricName(NUM_POSTINGS, BALANCED_TREE_POSTINGS_TYPE)); - Meter postingDecodes = Metrics.meter(createMetricName("PostingDecodes", BALANCED_TREE_POSTINGS_TYPE)); + Meter postingDecodes = Metrics.meter(createMetricName(POSTING_DECODES, BALANCED_TREE_POSTINGS_TYPE)); postingsListener = new PostingListEventsMetrics(postingDecodes); } + @Override + public void release() + { + super.release(); + Metrics.remove(createMetricName(NUM_POSTINGS, BALANCED_TREE_POSTINGS_TYPE)); + Metrics.remove(createMetricName(POSTING_DECODES, BALANCED_TREE_POSTINGS_TYPE)); + } + @Override public void onIntersectionComplete(long intersectionTotalTime, TimeUnit unit) { diff --git a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java index a687ff997e..2464533c99 100644 --- a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java +++ b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java @@ -21,27 +21,24 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nullable; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import org.apache.commons.lang3.ArrayUtils; import com.codahale.metrics.Counter; import com.codahale.metrics.Gauge; @@ -50,7 +47,6 @@ import com.codahale.metrics.Meter; import com.codahale.metrics.Metered; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.MetricRegistryListener; import com.codahale.metrics.MetricSet; import com.codahale.metrics.Timer; import org.apache.cassandra.db.virtual.CollectionVirtualTableAdapter; @@ -93,34 +89,14 @@ public class CassandraMetricsRegistry extends MetricRegistry { public static final UnaryOperator<String> METRICS_GROUP_POSTFIX = name -> name + "_group"; - /** - * A map of metric name constructed by {@link com.codahale.metrics.MetricRegistry#name(String, String...)} and - * its full name in the way how it is represented in JMX. The map is used by {@link CassandraJmxMetricsExporter} - * to export metrics to JMX. - */ - private static final ConcurrentMap<String, Deque<MetricName>> ALIASES = new ConcurrentHashMap<>(); - /** A set of all known metric groups, used to validate metric groups that are statically defined in Cassandra. */ static final Set<String> metricGroups; /** * Root metrics registry that is used by Cassandra to store all metrics. * All modifications to the registry are delegated to the corresponding listeners as well. - * Metrics from the root registry are exported to JMX by {@link CassandraJmxMetricsExporter} and to virtual tables - * via {@link #createMetricsKeyspaceTables()}. */ - public static final CassandraMetricsRegistry Metrics = init(); - private final MetricRegistryListener jmxExporter = new CassandraJmxMetricsExporter(ALIASES); - - /** We have to make sure that this metrics listener is called the last, so that it can clean up aliases. */ - private final MetricRegistryListener housekeepingListener = new BaseMetricRegistryListener() - { - @Override - protected void onMetricRemove(String name) - { - ALIASES.remove(name); - } - }; + public static final CassandraMetricsRegistry Metrics = new CassandraMetricsRegistry(); private final Map<String, ThreadPoolMetrics> threadPoolMetrics = new ConcurrentHashMap<>(); public static final String METRIC_SCOPE_UNDEFINED = "undefined"; @@ -181,15 +157,6 @@ public class CassandraMetricsRegistry extends MetricRegistry { } - private static CassandraMetricsRegistry init() - { - CassandraMetricsRegistry registry = new CassandraMetricsRegistry(); - // Adding listeners to the root registry, so that they can be notified about all metrics changes. - registry.addListener(registry.jmxExporter); - registry.addListener(registry.housekeepingListener); - return registry; - } - @SuppressWarnings("rawtypes") public static String getValueAsString(Metric metric) { @@ -292,7 +259,7 @@ public class CassandraMetricsRegistry extends MetricRegistry return builder.build(); } - private static void addUnknownMetric(MetricName newMetricName) + private static void verifyUnknownMetric(MetricName newMetricName) { String type = newMetricName.getType(); if (type.indexOf('.') >= 0) @@ -303,38 +270,39 @@ public class CassandraMetricsRegistry extends MetricRegistry if (!metricGroups.contains(newMetricName.getSystemViewName())) throw new IllegalStateException("Metric view name must match statically registered groups: " + newMetricName.getSystemViewName()); - - // We have to be sure that aliases are registered the same order as they are added to the registry. - ALIASES.computeIfAbsent(newMetricName.getMetricName(), k -> new LinkedList<>()) - .add(newMetricName); } - private static void setAliases(MetricName... names) + public String getMetricScope(String metricName) { - Arrays.asList(names).forEach(CassandraMetricsRegistry::addUnknownMetric); - ALIASES.get(names[0].getMetricName()).addAll(Arrays.asList(names)); + int groupLen = DefaultNameFactory.GROUP_NAME.length(); + int lastIndex = findNthIndexOf(metricName, groupLen, 2); + return metricName.length() <= groupLen || lastIndex == -1 ? METRIC_SCOPE_UNDEFINED : + metricName.substring(lastIndex + 1); } - public String getMetricScope(String metricName) + // Helper method to find the index of the nth occurrence of a specified character + private static int findNthIndexOf(String str, int start, int n) { - Deque<MetricName> deque = ALIASES.get(metricName); - return deque == null ? METRIC_SCOPE_UNDEFINED : - deque.stream().findFirst().map(MetricName::getScope).orElse(METRIC_SCOPE_UNDEFINED); + int index = start; + while (n-- > 0) + { + index = str.indexOf('.', index + 1); + if (index == -1) break; + } + return index; } public Counter counter(MetricName... name) { - setAliases(name); Counter counter = super.counter(name[0].getMetricName()); - Stream.of(name).skip(1).forEach(n -> register(n, counter)); + Stream.of(name).forEach(n -> register(n, counter)); return counter; } public Meter meter(MetricName... name) { - setAliases(name); Meter meter = super.meter(name[0].getMetricName()); - Stream.of(name).skip(1).forEach(n -> register(n, meter)); + Stream.of(name).forEach(n -> register(n, meter)); return meter; } @@ -345,19 +313,16 @@ public class CassandraMetricsRegistry extends MetricRegistry public Histogram histogram(MetricName name, MetricName alias, boolean considerZeroes) { - setAliases(name, alias); Histogram histogram = histogram(name, considerZeroes); register(alias, histogram); return histogram; } - @SuppressWarnings({ "rawtypes", "unchecked" }) - public <T extends Gauge> T gauge(MetricName name, MetricName alias, MetricSupplier<T> gauge) + public <T extends Gauge<?>> T gauge(MetricName name, MetricName alias, T gauge) { - setAliases(name, alias); - Gauge gaugeLoc = super.gauge(name.getMetricName(), gauge); + T gaugeLoc = register(name, gauge); register(alias, gaugeLoc); - return (T) gaugeLoc; + return gaugeLoc; } public Timer timer(MetricName name) @@ -377,7 +342,6 @@ public class CassandraMetricsRegistry extends MetricRegistry public SnapshottingTimer timer(MetricName name, MetricName alias, TimeUnit durationUnit) { - setAliases(name, alias); SnapshottingTimer timer = timer(name, durationUnit); register(alias, timer); return timer; @@ -411,7 +375,8 @@ public class CassandraMetricsRegistry extends MetricRegistry try { - addUnknownMetric(name); + verifyUnknownMetric(name); + registerMBean(metric, name.getMBeanName(), MBeanWrapper.instance); return super.register(name.getMetricName(), metric); } catch (IllegalArgumentException e) @@ -444,30 +409,85 @@ public class CassandraMetricsRegistry extends MetricRegistry public <T extends Metric> T register(MetricName name, T metric, MetricName... aliases) { - setAliases(ArrayUtils.addAll(new MetricName[]{name}, aliases)); T metricLoc = register(name, metric); Stream.of(aliases).forEach(n -> register(n, metricLoc)); return metricLoc; } + /** + * Removes all metrics that match the given predicate. + * + * @param resolver a function that resolves a short metric name from a full metric name, + * or @{code null} if the metric should not be removed + * @param factory a function that creates a metric name from a short metric name. + * @param onRemoved a callback that is called for each removed metric. + */ + public void removeIfMatch(MetricNameResolver resolver, + Function<String, MetricName> factory, + Consumer<MetricName> onRemoved) + { + removeMatching((full, metric) -> { + String shortName = resolver.resolve(full); + if (shortName == null) + return false; + + MetricName metricName = factory.apply(shortName); + boolean remove = metricName.getMetricName().equals(full); + + if (remove) + { + unregisterMBean(metricName.getMBeanName(), MBeanWrapper.instance); + onRemoved.accept(metricName); + } + return remove; + }); + } + + /** + * Default implementation of the {@link MetricNameResolver} that resolves a short metric name from a full metric name, + * assuming that the full metric name doesn't contain dots. Returns {@code null} if the full metric name doesn't match + * the provided group and type. + * <p> + * The {@code scope} is the last part of the full metric name. Can be {@code null} and it's used for search efficiency. + * + * @param fullName full metric name + * @param group metric group + * @param type metric type + * @param scope metric scope, which is the last part of the full metric name and used for search efficiency. + * @return short metric name or {@code null} if the full metric name doesn't match the group, type, and scope. + */ + public static @Nullable String resolveShortMetricName(String fullName, String group, String type, @Nullable String scope) + { + String prefix = name(group, type); + if (fullName.startsWith(prefix)) + { + int lastDot = fullName.indexOf('.', prefix.length() + 1); + // If a metric scope is null (dots not found), the metric name is the last part of the full metric name. + if (lastDot == -1) + return fullName.substring(prefix.length() + 1); + + if (scope == null) + return fullName.substring(prefix.length() + 1, lastDot); + + return fullName.substring(lastDot + 1).equals(scope) ? + fullName.substring(prefix.length() + 1, lastDot) : + null; + } + + return null; + } + public void remove(MetricName name) { - // Aliases are removed in onMetricRemoved by metrics listener. - remove(name.getMetricName()); + boolean success = remove(name.getMetricName()); + if (success) + unregisterMBean(name.getMBeanName(), MBeanWrapper.instance); } - public boolean remove(String name) + @FunctionalInterface + public interface MetricNameResolver { - LinkedList<String> delete = ofNullable(ALIASES.get(name)) - .map(s -> s.stream().map(MetricName::getMetricName) - .collect(Collectors.toCollection(LinkedList::new))) - .orElse(new LinkedList<>(Collections.singletonList(name))); - // Aliases are removed in onMetricRemoved by metrics listener. - Iterator<String> iter = delete.descendingIterator(); - boolean removed = true; - while (iter.hasNext()) - removed &= super.remove(iter.next()); - return removed; + @Nullable String resolve(String fullName); } private void registerMBean(Metric metric, ObjectName name, MBeanWrapper mBeanServer) @@ -1244,106 +1264,4 @@ public class CassandraMetricsRegistry extends MetricRegistry return name; } } - - private static class CassandraJmxMetricsExporter extends BaseMetricRegistryListener - { - private final MBeanWrapper mBeanWrapper = MBeanWrapper.instance; - private final Map<String, Deque<MetricName>> aliases; - - public CassandraJmxMetricsExporter(Map<String, Deque<MetricName>> aliases) - { - this.aliases = aliases; - } - - protected void onMetricAdded(String name, Metric metric) - { - Deque<MetricName> deque = aliases.get(name); - if (deque == null) - return; - - assert deque.getFirst().getMetricName().equals(name); - Metrics.registerMBean(metric, deque.getFirst().getMBeanName(), mBeanWrapper); - } - - protected void onMetricRemove(String name) - { - Deque<MetricName> deque = aliases.get(name); - if (deque == null) - return; - - assert deque.getFirst().getMetricName().equals(name); - Metrics.unregisterMBean(deque.getFirst().getMBeanName(), mBeanWrapper); - } - } - - private static abstract class BaseMetricRegistryListener implements MetricRegistryListener - { - protected void onMetricAdded(String name, Metric metric) - { - } - - protected void onMetricRemove(String name) - { - } - - @Override - public void onGaugeAdded(String metricName, Gauge<?> gauge) - { - onMetricAdded(metricName, gauge); - } - - @Override - public void onGaugeRemoved(String metricName) - { - onMetricRemove(metricName); - } - - @Override - public void onCounterAdded(String metricName, Counter counter) - { - onMetricAdded(metricName, counter); - } - - @Override - public void onCounterRemoved(String metricName) - { - onMetricRemove(metricName); - } - - @Override - public void onHistogramAdded(String metricName, Histogram histogram) - { - onMetricAdded(metricName, histogram); - } - - @Override - public void onHistogramRemoved(String metricName) - { - onMetricRemove(metricName); - } - - @Override - public void onMeterAdded(String metricName, Meter meter) - { - onMetricAdded(metricName, meter); - } - - @Override - public void onMeterRemoved(String metricName) - { - onMetricRemove(metricName); - } - - @Override - public void onTimerAdded(String metricName, Timer timer) - { - onMetricAdded(metricName, timer); - } - - @Override - public void onTimerRemoved(String metricName) - { - onMetricRemove(metricName); - } - } } diff --git a/src/java/org/apache/cassandra/metrics/ClientMetrics.java b/src/java/org/apache/cassandra/metrics/ClientMetrics.java index 1a465506a6..4e7f5a1b06 100644 --- a/src/java/org/apache/cassandra/metrics/ClientMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ClientMetrics.java @@ -44,6 +44,7 @@ import org.apache.cassandra.transport.Server; import org.apache.cassandra.transport.ServerConnection; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.resolveShortMetricName; public final class ClientMetrics { @@ -283,7 +284,7 @@ public final class ClientMetrics private <T> Gauge<T> registerGauge(String name, String deprecated, Gauge<T> gauge) { - return Metrics.gauge(factory.createMetricName(name), factory.createMetricName(deprecated), () -> gauge); + return Metrics.gauge(factory.createMetricName(name), factory.createMetricName(deprecated), gauge); } private Meter registerMeter(String name) @@ -295,4 +296,10 @@ public final class ClientMetrics { return Metrics.meter(metricNameFactory.createMetricName(name)); } + + public void release() + { + Metrics.removeIfMatch(fullName -> resolveShortMetricName(fullName, DefaultNameFactory.GROUP_NAME, TYPE_NAME, null), + factory::createMetricName, m -> {}); + } } diff --git a/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java b/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java index 83a1651fa7..237fd03e2d 100644 --- a/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java +++ b/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java @@ -17,11 +17,9 @@ */ package org.apache.cassandra.metrics; -import java.util.Set; import java.util.function.ToLongFunction; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Sets; import com.codahale.metrics.Counter; import com.codahale.metrics.Gauge; @@ -34,9 +32,9 @@ import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.io.sstable.GaugeProvider; import org.apache.cassandra.io.sstable.format.SSTableFormat; import org.apache.cassandra.metrics.CassandraMetricsRegistry.MetricName; -import org.apache.cassandra.metrics.TableMetrics.ReleasableMetric; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.resolveShortMetricName; /** * Metrics for {@link ColumnFamilyStore}. @@ -185,12 +183,9 @@ public class KeyspaceMetrics public final ImmutableMap<SSTableFormat<?, ?>, ImmutableMap<String, Gauge<? extends Number>>> formatSpecificGauges; - public final MetricNameFactory factory; + private final KeyspaceMetricNameFactory factory; private final Keyspace keyspace; - /** set containing names of all the metrics stored here, for releasing later */ - private Set<ReleasableMetric> allMetrics = Sets.newHashSet(); - /** * Creates metrics for given {@link ColumnFamilyStore}. * @@ -300,10 +295,11 @@ public class KeyspaceMetrics */ public void release() { - for (ReleasableMetric metric : allMetrics) - { - metric.release(); - } + Metrics.removeIfMatch(fullName -> resolveShortMetricName(fullName, + KeyspaceMetricNameFactory.GROUP_NAME, + TYPE_NAME, + factory.scope()), + factory::createMetricName, m -> {}); } private ImmutableMap<SSTableFormat<?, ?>, ImmutableMap<String, Gauge<? extends Number>>> createFormatSpecificGauges(Keyspace keyspace) @@ -315,7 +311,6 @@ public class KeyspaceMetrics for (GaugeProvider<?> gaugeProvider : format.getFormatSpecificMetricsProviders().getGaugeProviders()) { String finalName = gaugeProvider.name; - allMetrics.add(() -> releaseMetric(finalName)); Gauge<? extends Number> gauge = Metrics.register(factory.createMetricName(finalName), gaugeProvider.getKeyspaceGauge(keyspace)); gauges.put(gaugeProvider.name, gauge); } @@ -334,7 +329,6 @@ public class KeyspaceMetrics */ private Gauge<Long> createKeyspaceGauge(String name, final ToLongFunction<TableMetrics> extractor) { - allMetrics.add(() -> releaseMetric(name)); return Metrics.register(factory.createMetricName(name), new Gauge<Long>() { public Long getValue() @@ -357,7 +351,6 @@ public class KeyspaceMetrics */ private Counter createKeyspaceCounter(String name, final ToLongFunction<TableMetrics> extractor) { - allMetrics.add(() -> releaseMetric(name)); return Metrics.register(factory.createMetricName(name), new Counter() { @Override @@ -375,42 +368,32 @@ public class KeyspaceMetrics 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)); + return new LatencyMetrics(factory, name); } static class KeyspaceMetricNameFactory implements MetricNameFactory { + public static final String GROUP_NAME = TableMetrics.class.getPackage().getName(); private final String keyspaceName; KeyspaceMetricNameFactory(Keyspace ks) @@ -418,18 +401,20 @@ public class KeyspaceMetrics this.keyspaceName = ks.getName(); } + public String scope() + { + return keyspaceName; + } + @Override public MetricName createMetricName(String metricName) { - String groupName = TableMetrics.class.getPackage().getName(); - - StringBuilder mbeanName = new StringBuilder(); - mbeanName.append(groupName).append(":"); - mbeanName.append("type=").append("Keyspace"); - mbeanName.append(",keyspace=").append(keyspaceName); - mbeanName.append(",name=").append(metricName); - - return new MetricName(groupName, TYPE_NAME, metricName, keyspaceName, mbeanName.toString()); + assert metricName.indexOf('.') == -1 : String.format("Metric name '%s' should not contain '.'", metricName); + return new MetricName(GROUP_NAME, TYPE_NAME, metricName, scope(), + GROUP_NAME + ':' + + "type=" + "Keyspace" + + ",keyspace=" + scope() + + ",name=" + metricName); } } } diff --git a/src/java/org/apache/cassandra/metrics/TableMetrics.java b/src/java/org/apache/cassandra/metrics/TableMetrics.java index 7ab1cbbfbf..ab1ab6eb73 100644 --- a/src/java/org/apache/cassandra/metrics/TableMetrics.java +++ b/src/java/org/apache/cassandra/metrics/TableMetrics.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.EnumMap; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -33,7 +34,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import org.apache.commons.lang3.ArrayUtils; import com.codahale.metrics.Counter; @@ -64,6 +64,7 @@ import org.apache.cassandra.utils.Pair; import static java.util.concurrent.TimeUnit.MICROSECONDS; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.resolveShortMetricName; import static org.apache.cassandra.utils.Clock.Global.nanoTime; /** @@ -216,8 +217,8 @@ public class TableMetrics public final Timer coordinatorScanLatency; public final SnapshottingTimer coordinatorWriteLatency; - private final MetricNameFactory factory; - private final MetricNameFactory aliasFactory; + private final TableMetricNameFactory factory; + private final TableMetricNameFactory aliasFactory; public final Counter speculativeRetries; public final Counter speculativeFailedRetries; @@ -357,11 +358,6 @@ public class TableMetrics public final EnumMap<SamplerType, Sampler<?>> samplers; - /** - * Stores all metrics created that can be used when unregistering - */ - private final Set<ReleasableMetric> all = Sets.newHashSet(); - private interface GetHistogram { EstimatedHistogram getHistogram(SSTableReader reader); @@ -405,16 +401,11 @@ public class TableMetrics * * @param cfs ColumnFamilyStore to measure metrics */ - public TableMetrics(final ColumnFamilyStore cfs, ReleasableMetric memtableMetrics) + public TableMetrics(final ColumnFamilyStore cfs) { factory = new TableMetricNameFactory(cfs, cfs.isIndex() ? INDEX_TYPE_NAME : TYPE_NAME); aliasFactory = new TableMetricNameFactory(cfs, cfs.isIndex() ? INDEX_ALIAS_TYPE_NAME : ALIAS_TYPE_NAME); - if (memtableMetrics != null) - { - all.add(memtableMetrics); - } - samplers = new EnumMap<>(SamplerType.class); topReadPartitionFrequency = new FrequencySampler<ByteBuffer>() { @@ -894,10 +885,14 @@ public class TableMetrics */ public void release() { - for (ReleasableMetric entry : all) - { - entry.release(); - } + Metrics.removeIfMatch(fullName -> resolveShortMetricName(fullName, TableMetricNameFactory.GROUP_NAME, + factory.type(), + factory.scope()), + factory::createMetricName, this::releaseMetric); + Metrics.removeIfMatch(fullName -> resolveShortMetricName(fullName, TableMetricNameFactory.GROUP_NAME, + aliasFactory.type(), + aliasFactory.scope()), + aliasFactory::createMetricName, this::releaseMetric); } private ImmutableMap<SSTableFormat<?, ?>, ImmutableMap<String, Gauge<? extends Number>>> createFormatSpecificGauges(ColumnFamilyStore cfs) @@ -1065,18 +1060,6 @@ 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) { Timer cfTimer = Metrics.timer(factory.createMetricName(name), aliasFactory.createMetricName(name)); @@ -1110,9 +1093,8 @@ public class TableMetrics private LatencyMetrics createLatencyMetrics(String namePrefix, LatencyMetrics ... parents) { - LatencyMetrics metric = new LatencyMetrics(factory, namePrefix, parents); - all.add(metric::release); - return metric; + // All metrics which are registered with the same factory type will be removed when release() is called. + return new LatencyMetrics(factory, namePrefix, parents); } /** @@ -1137,22 +1119,16 @@ public class TableMetrics { boolean ret = ALL_TABLE_METRICS.putIfAbsent(name, ConcurrentHashMap.newKeySet()) == null; ALL_TABLE_METRICS.get(name).add(metric); - all.add(() -> releaseMetric(name, alias, deprecated)); return ret; } - private void releaseMetric(String tableMetricName, String cfMetricName, String tableMetricAlias) + private void releaseMetric(CassandraMetricsRegistry.MetricName name) { - CassandraMetricsRegistry.MetricName name = factory.createMetricName(tableMetricName); + Metric metric = Metrics.getMetrics().get(name.getMetricName()); + if (metric == null) + return; - 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 - ALL_TABLE_METRICS.get(tableMetricName).remove(metric); - // Aliases are already known to the parent metrics, so we don't need to remove them here. - Metrics.remove(name); - } + Optional.ofNullable(ALL_TABLE_METRICS.get(name.getName())).ifPresent(set -> set.remove(metric)); } public static class TableMeter @@ -1247,6 +1223,7 @@ public class TableMetrics static class TableMetricNameFactory implements MetricNameFactory { + public static final String GROUP_NAME = TableMetrics.class.getPackage().getName(); private final String keyspaceName; private final String tableName; private final String type; @@ -1258,23 +1235,31 @@ public class TableMetrics this.type = type; } - public CassandraMetricsRegistry.MetricName createMetricName(String metricName) + public String type() { - String groupName = TableMetrics.class.getPackage().getName(); + return 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); + public String scope() + { + return keyspaceName + '.' + tableName; + } - return new CassandraMetricsRegistry.MetricName(groupName, type, metricName, keyspaceName + "." + tableName, mbeanName.toString()); + public CassandraMetricsRegistry.MetricName createMetricName(String metricName) + { + assert metricName.indexOf('.') == -1 : String.format("Metric name must not contain '.' character (got '%s')", metricName); + return new CassandraMetricsRegistry.MetricName(GROUP_NAME, type, metricName, scope(), + GROUP_NAME + ':' + + "type=" + type + + ",keyspace=" + keyspaceName + + ",scope=" + tableName + + ",name=" + metricName); } } static class AllTableMetricNameFactory implements MetricNameFactory { + private static final String GROUP_NAME = TableMetrics.class.getPackage().getName(); private final String type; public AllTableMetricNameFactory(String type) { @@ -1283,21 +1268,14 @@ public class TableMetrics 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()); + assert metricName.indexOf('.') == -1 : String.format("Metric name must not contain '.' character (got '%s')", metricName); + return new CassandraMetricsRegistry.MetricName(GROUP_NAME, type, metricName, "all", + GROUP_NAME + ':' + + "type=" + type + + ",name=" + metricName); } } - @FunctionalInterface - public interface ReleasableMetric - { - void release(); - } - private static class GlobalTableGauge implements Gauge<Long> { private final String name; diff --git a/src/java/org/apache/cassandra/metrics/TrieMemtableMetricsView.java b/src/java/org/apache/cassandra/metrics/TrieMemtableMetricsView.java index a308fbbab8..e012540439 100644 --- a/src/java/org/apache/cassandra/metrics/TrieMemtableMetricsView.java +++ b/src/java/org/apache/cassandra/metrics/TrieMemtableMetricsView.java @@ -20,9 +20,15 @@ package org.apache.cassandra.metrics; import com.codahale.metrics.Counter; +import static com.codahale.metrics.MetricRegistry.name; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.resolveShortMetricName; import static org.apache.cassandra.metrics.DefaultNameFactory.GROUP_NAME; +/** + * Metrics for TrieMemtable, the metrics are shared across all memtables in a single column family and + * are updated by all memtables in the column family. + */ public class TrieMemtableMetricsView { public static final String TYPE_NAME = "TrieMemtable"; @@ -43,11 +49,9 @@ public class TrieMemtableMetricsView // shard sizes distribution public final MinMaxAvgMetric lastFlushShardDataSizes; - private final TrieMemtableMetricNameFactory factory; - public TrieMemtableMetricsView(String keyspace, String table) { - factory = new TrieMemtableMetricNameFactory(keyspace, table); + MetricNameFactory factory = new TrieMemtableMetricNameFactory(keyspace, table); uncontendedPuts = Metrics.counter(factory.createMetricName(UNCONTENDED_PUTS)); contendedPuts = Metrics.counter(factory.createMetricName(CONTENDED_PUTS)); @@ -55,15 +59,15 @@ public class TrieMemtableMetricsView lastFlushShardDataSizes = new MinMaxAvgMetric(factory, LAST_FLUSH_SHARD_SIZES); } - public void release() + public static void release(String keyspace, String table) { - Metrics.remove(factory.createMetricName(UNCONTENDED_PUTS)); - Metrics.remove(factory.createMetricName(CONTENDED_PUTS)); - contentionTime.release(); - lastFlushShardDataSizes.release(); + TrieMemtableMetricNameFactory factory = new TrieMemtableMetricNameFactory(keyspace, table); + Metrics.removeIfMatch(fullName -> resolveShortMetricName(fullName, GROUP_NAME, TYPE_NAME, factory.scope()), + factory::createMetricName, + m -> {}); } - static class TrieMemtableMetricNameFactory implements MetricNameFactory + private static class TrieMemtableMetricNameFactory implements MetricNameFactory { private final String keyspace; private final String table; @@ -74,16 +78,20 @@ public class TrieMemtableMetricsView this.table = table; } - public CassandraMetricsRegistry.MetricName createMetricName(String metricName) + public String scope() { - StringBuilder mbeanName = new StringBuilder(); - mbeanName.append(GROUP_NAME).append(":"); - mbeanName.append("type=").append(TYPE_NAME); - mbeanName.append(",keyspace=").append(keyspace); - mbeanName.append(",scope=").append(table); - mbeanName.append(",name=").append(metricName); + return name(keyspace, table); + } - return new CassandraMetricsRegistry.MetricName(GROUP_NAME, TYPE_NAME, metricName, keyspace + '.' + table, mbeanName.toString()); + public CassandraMetricsRegistry.MetricName createMetricName(String metricName) + { + assert metricName.indexOf('.') == -1 : "metricName should not contain '.'; got " + metricName; + return new CassandraMetricsRegistry.MetricName(GROUP_NAME, TYPE_NAME, metricName, scope(), + GROUP_NAME + ':' + + "type=" + TYPE_NAME + + ",keyspace=" + keyspace + + ",scope=" + table + + ",name=" + metricName); } } } diff --git a/src/java/org/apache/cassandra/service/NativeTransportService.java b/src/java/org/apache/cassandra/service/NativeTransportService.java index cfbc638afe..6fa89bab39 100644 --- a/src/java/org/apache/cassandra/service/NativeTransportService.java +++ b/src/java/org/apache/cassandra/service/NativeTransportService.java @@ -112,6 +112,7 @@ public class NativeTransportService public void destroy() { stop(); + ClientMetrics.instance.release(); server = null; // shutdown executors used by netty for native transport server diff --git a/test/unit/org/apache/cassandra/db/virtual/walker/CollectionEntryTestRowWalker.java b/test/unit/org/apache/cassandra/db/virtual/walker/CollectionEntryTestRowWalker.java index f6ea8d6904..5169e96561 100644 --- a/test/unit/org/apache/cassandra/db/virtual/walker/CollectionEntryTestRowWalker.java +++ b/test/unit/org/apache/cassandra/db/virtual/walker/CollectionEntryTestRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/test/unit/org/apache/cassandra/db/virtual/walker/PartitionEntryTestRowWalker.java b/test/unit/org/apache/cassandra/db/virtual/walker/PartitionEntryTestRowWalker.java index 241f3b68df..e5fb014154 100644 --- a/test/unit/org/apache/cassandra/db/virtual/walker/PartitionEntryTestRowWalker.java +++ b/test/unit/org/apache/cassandra/db/virtual/walker/PartitionEntryTestRowWalker.java @@ -1,12 +1,13 @@ /* - * 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 + * 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/test/unit/org/apache/cassandra/metrics/CassandraMetricsRegistryTest.java b/test/unit/org/apache/cassandra/metrics/CassandraMetricsRegistryTest.java index 897265ef4d..b069d0330f 100644 --- a/test/unit/org/apache/cassandra/metrics/CassandraMetricsRegistryTest.java +++ b/test/unit/org/apache/cassandra/metrics/CassandraMetricsRegistryTest.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -38,7 +37,6 @@ import org.junit.Test; import com.codahale.metrics.Meter; import com.codahale.metrics.Metric; -import com.codahale.metrics.MetricRegistryListener; import com.codahale.metrics.Timer; import com.codahale.metrics.jvm.BufferPoolMetricSet; import com.codahale.metrics.jvm.GarbageCollectorMetricSet; @@ -46,6 +44,8 @@ import com.codahale.metrics.jvm.MemoryUsageGaugeSet; import org.apache.cassandra.metrics.CassandraMetricsRegistry.MetricName; import org.apache.cassandra.utils.EstimatedHistogram; +import static com.codahale.metrics.MetricRegistry.name; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.resolveShortMetricName; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -186,34 +186,18 @@ public class CassandraMetricsRegistryTest } @Test - public void testMetricAliasesOrder() + public void testMetricAliasesRemoveByCondition() { - String dummy = "defaultName"; LinkedList<MetricName> aliases = new LinkedList<>(); - int size = ThreadLocalRandom.current().nextInt(10, 1000); - MetricName first = DefaultNameFactory.createMetricName("Table", "FirstTestMetricAliasesOrder", "FirstScope"); + int size = 10; + DefaultNameFactory factory = new DefaultNameFactory("Table", "FirstScope"); + DefaultNameFactory aliasFactory = new DefaultNameFactory("Table", "SecondScope"); + MetricName first = factory.createMetricName("FirstTestMetricAliasesOrder"); for (int i = 0; i < size; i++) - aliases.add(DefaultNameFactory.createMetricName("Table", "FirstTestMetricAliasesOrder" + UUID.randomUUID(), - UUID.randomUUID().toString())); - - Meter metric = CassandraMetricsRegistry.Metrics.meter(dummy); - LinkedList<MetricName> verify = new LinkedList<>(aliases); - verify.addFirst(first); - MetricRegistryListener listener; - CassandraMetricsRegistry.Metrics.addListener(listener = new MetricRegistryListener.Base() - { - @Override - public void onMeterAdded(String name, Meter meter) - { - if (dummy.equals(name)) - return; + aliases.add(aliasFactory.createMetricName("AliasFirstTestMetricAliasesOrder_" + UUID.randomUUID())); - assertEquals(verify.removeFirst().getMetricName(), name); - } - }); + Meter metric = CassandraMetricsRegistry.Metrics.meter(first); CassandraMetricsRegistry.Metrics.register(first, metric, aliases.toArray(new MetricName[size])); - CassandraMetricsRegistry.Metrics.removeListener(listener); - List<String> all = CassandraMetricsRegistry.Metrics.getMetrics().keySet(). stream() .filter(m -> m.contains("FirstTestMetricAliasesOrder")) @@ -222,12 +206,18 @@ public class CassandraMetricsRegistryTest assertNotNull(all); assertEquals(size + 1, all.size()); - CassandraMetricsRegistry.Metrics.remove(first.getMetricName()); + CassandraMetricsRegistry.Metrics.removeIfMatch(fullName -> + resolveShortMetricName(fullName, DefaultNameFactory.GROUP_NAME, "Table", null), + factory::createMetricName, + m -> {}); Map<String, Metric> metrics = CassandraMetricsRegistry.Metrics.getMetrics(); - assertEquals(1, metrics.size()); - assertEquals(dummy, metrics.keySet().iterator().next()); + assertEquals(size, metrics.size()); + assertTrue(metrics.keySet().stream().allMatch(m -> m.startsWith(name(DefaultNameFactory.GROUP_NAME, "Table", "AliasFirstTestMetricAliasesOrder_")))); - CassandraMetricsRegistry.Metrics.remove(dummy); + CassandraMetricsRegistry.Metrics.removeIfMatch(fullName -> + resolveShortMetricName(fullName, DefaultNameFactory.GROUP_NAME, "Table", null), + aliasFactory::createMetricName, + m -> {}); assertTrue(CassandraMetricsRegistry.Metrics.getMetrics().isEmpty()); } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org