From 8896a70b015102c212d0a27ed1f4e1f0fabe85c4 Mon Sep 17 00:00:00 2001 From: T Jake Luciani Date: Mon, 5 Jan 2015 09:34:08 -0500 Subject: [PATCH] Upgrade metrics library and remove depricated metrics Patch by tjake; reviewed by aleksey for CASSANDRA-5657 --- CHANGES.txt | 1 + build.xml | 4 +- ...-core-2.2.0.txt => metrics-core-3.1.0.txt} | 0 lib/metrics-core-2.2.0.jar | Bin 82123 -> 0 bytes lib/metrics-core-3.1.0.jar | Bin 0 -> 111908 bytes ...JMXEnabledScheduledThreadPoolExecutor.java | 4 +- .../JMXEnabledSharedExecutorPool.java | 113 --- .../JMXEnabledThreadPoolExecutor.java | 21 +- .../JMXEnabledThreadPoolExecutorMBean.java | 19 +- .../cassandra/concurrent/SEPExecutor.java | 32 +- .../concurrent/SharedExecutorPool.java | 9 + .../apache/cassandra/concurrent/Stage.java | 16 + .../cassandra/concurrent/StageManager.java | 2 +- .../cassandra/db/ColumnFamilyStore.java | 180 +--- .../cassandra/db/ColumnFamilyStoreMBean.java | 234 ------ .../cassandra/db/HintedHandOffManager.java | 2 +- .../cassandra/db/commitlog/CommitLog.java | 20 - .../db/commitlog/CommitLogMBean.java | 21 - .../db/compaction/CompactionInfo.java | 3 +- .../db/compaction/CompactionManager.java | 8 +- .../db/compaction/CompactionManagerMBean.java | 28 - .../cassandra/db/filter/ExtendedFilter.java | 2 +- .../org/apache/cassandra/gms/Gossiper.java | 2 +- .../locator/DynamicEndpointSnitch.java | 18 +- .../metrics/CASClientRequestMetrics.java | 20 +- .../apache/cassandra/metrics/CQLMetrics.java | 30 +- .../cassandra/metrics/CacheMetrics.java | 59 +- .../metrics/CassandraMetricsRegistry.java | 791 ++++++++++++++++++ .../ClearableHistogram.java} | 35 +- .../cassandra/metrics/ClientMetrics.java | 13 +- .../metrics/ClientRequestMetrics.java | 26 +- .../metrics/ColumnFamilyMetrics.java | 157 ++-- .../cassandra/metrics/CommitLogMetrics.java | 23 +- .../cassandra/metrics/CompactionMetrics.java | 22 +- .../cassandra/metrics/ConnectionMetrics.java | 66 +- .../cassandra/metrics/DefaultNameFactory.java | 7 +- .../metrics/DroppedMessageMetrics.java | 21 +- .../metrics/EstimatedHistogramReservoir.java | 105 +++ .../cassandra/metrics/FileCacheMetrics.java | 32 +- .../metrics/HintedHandoffMetrics.java | 15 +- .../cassandra/metrics/KeyspaceMetrics.java | 62 +- .../cassandra/metrics/LatencyMetrics.java | 44 +- .../cassandra/metrics/MetricNameFactory.java | 3 +- .../cassandra/metrics/ReadRepairMetrics.java | 14 +- .../cassandra/metrics/RestorableMeter.java | 10 +- .../apache/cassandra/metrics/SEPMetrics.java | 55 +- .../cassandra/metrics/StorageMetrics.java | 13 +- .../cassandra/metrics/StreamingMetrics.java | 16 +- .../metrics/ThreadPoolMetricNameFactory.java | 6 +- .../cassandra/metrics/ThreadPoolMetrics.java | 74 +- .../cassandra/net/MessagingService.java | 30 +- .../cassandra/net/MessagingServiceMBean.java | 15 - .../net/OutboundTcpConnectionPool.java | 6 +- .../scheduler/RoundRobinScheduler.java | 2 - .../cassandra/scheduler/WeightedQueue.java | 43 +- .../scheduler/WeightedQueueMBean.java | 32 - .../cassandra/service/CacheService.java | 67 +- .../cassandra/service/CacheServiceMBean.java | 86 -- .../cassandra/service/CassandraDaemon.java | 7 +- .../cassandra/service/LoadBroadcaster.java | 3 +- .../cassandra/service/StorageProxy.java | 102 +-- .../cassandra/service/StorageProxyMBean.java | 48 -- .../cassandra/service/StorageService.java | 21 +- .../service/StorageServiceMBean.java | 10 - .../cassandra/thrift/CassandraServer.java | 2 +- .../org/apache/cassandra/tools/NodeProbe.java | 109 +-- .../org/apache/cassandra/tools/NodeTool.java | 56 +- .../transport/RequestThreadPoolExecutor.java | 6 +- .../apache/cassandra/utils/StatusLogger.java | 40 +- .../cassandra/utils/concurrent/WaitQueue.java | 10 +- .../LongSharedExecutorPoolTest.java | 2 +- .../cassandra/db/LongFlushMemtableTest.java | 2 +- .../apache/cassandra/utils/LongBTreeTest.java | 15 +- .../cassandra/db/ColumnFamilyMetricTest.java | 12 +- .../cassandra/db/ColumnFamilyStoreTest.java | 5 +- .../org/apache/cassandra/db/KeyspaceTest.java | 17 +- .../org/apache/cassandra/db/RowCacheTest.java | 20 +- .../db/compaction/AntiCompactionTest.java | 4 +- .../io/sstable/SSTableReaderTest.java | 2 +- .../io/sstable/SSTableRewriterTest.java | 16 +- .../cassandra/metrics/CQLMetricsTest.java | 32 +- .../cassandra/metrics/LatencyMetricsTest.java | 2 +- 82 files changed, 1534 insertions(+), 1748 deletions(-) rename lib/licenses/{metrics-core-2.2.0.txt => metrics-core-3.1.0.txt} (100%) delete mode 100644 lib/metrics-core-2.2.0.jar create mode 100644 lib/metrics-core-3.1.0.jar delete mode 100644 src/java/org/apache/cassandra/concurrent/JMXEnabledSharedExecutorPool.java create mode 100644 src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java rename src/java/org/apache/cassandra/{concurrent/IExecutorMBean.java => metrics/ClearableHistogram.java} (55%) create mode 100644 src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java delete mode 100644 src/java/org/apache/cassandra/scheduler/WeightedQueueMBean.java diff --git a/CHANGES.txt b/CHANGES.txt index 36b081b875..16e7c7d824 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0 + * Upgrade Metrics library and remove depricated metrics (CASSANDRA-5657) * Serializing Row cache alternative, fully off heap (CASSANDRA-7438) * Duplicate rows returned when in clause has repeated values (CASSANDRA-6707) * Make CassandraException unchecked, extend RuntimeException (CASSANDRA-8560) diff --git a/build.xml b/build.xml index ce9a603344..0d96f4cd31 100644 --- a/build.xml +++ b/build.xml @@ -368,7 +368,7 @@ - + @@ -483,7 +483,7 @@ - + diff --git a/lib/licenses/metrics-core-2.2.0.txt b/lib/licenses/metrics-core-3.1.0.txt similarity index 100% rename from lib/licenses/metrics-core-2.2.0.txt rename to lib/licenses/metrics-core-3.1.0.txt diff --git a/lib/metrics-core-2.2.0.jar b/lib/metrics-core-2.2.0.jar deleted file mode 100644 index 0f6d1cb0ecbd37f673bc87e109cacc535e23350a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82123 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 diff --git a/lib/metrics-core-3.1.0.jar b/lib/metrics-core-3.1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..564ffb778e3d7e112d71e3d343d802173330476f GIT binary patch literal 111908 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%0D~a-0OIXjJB8IjNWCRsE@|OS#rP-W9DAI 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

({LWoEOR37RKl3=4 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 RU<3Y<04%IM2w07P{XgQPcD(=q literal 0 HcmV?d00001 diff --git a/src/java/org/apache/cassandra/concurrent/JMXEnabledScheduledThreadPoolExecutor.java b/src/java/org/apache/cassandra/concurrent/JMXEnabledScheduledThreadPoolExecutor.java index 64d9267770..50c9591f73 100644 --- a/src/java/org/apache/cassandra/concurrent/JMXEnabledScheduledThreadPoolExecutor.java +++ b/src/java/org/apache/cassandra/concurrent/JMXEnabledScheduledThreadPoolExecutor.java @@ -107,12 +107,12 @@ public class JMXEnabledScheduledThreadPoolExecutor extends DebuggableScheduledTh public int getTotalBlockedTasks() { - return (int) metrics.totalBlocked.count(); + return (int) metrics.totalBlocked.getCount(); } public int getCurrentlyBlockedTasks() { - return (int) metrics.currentBlocked.count(); + return (int) metrics.currentBlocked.getCount(); } public int getCoreThreads() diff --git a/src/java/org/apache/cassandra/concurrent/JMXEnabledSharedExecutorPool.java b/src/java/org/apache/cassandra/concurrent/JMXEnabledSharedExecutorPool.java deleted file mode 100644 index 54bb772261..0000000000 --- a/src/java/org/apache/cassandra/concurrent/JMXEnabledSharedExecutorPool.java +++ /dev/null @@ -1,113 +0,0 @@ -/* -* 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.concurrent; - -import java.lang.management.ManagementFactory; -import javax.management.MBeanServer; -import javax.management.ObjectName; - -import org.apache.cassandra.metrics.SEPMetrics; - -public class JMXEnabledSharedExecutorPool extends SharedExecutorPool -{ - - public static final JMXEnabledSharedExecutorPool SHARED = new JMXEnabledSharedExecutorPool("SharedPool"); - - public JMXEnabledSharedExecutorPool(String poolName) - { - super(poolName); - } - - public interface JMXEnabledSEPExecutorMBean extends JMXEnabledThreadPoolExecutorMBean - { - } - - public class JMXEnabledSEPExecutor extends SEPExecutor implements JMXEnabledSEPExecutorMBean - { - - private final SEPMetrics metrics; - private final String mbeanName; - - public JMXEnabledSEPExecutor(int poolSize, int maxQueuedLength, String name, String jmxPath) - { - super(JMXEnabledSharedExecutorPool.this, poolSize, maxQueuedLength); - metrics = new SEPMetrics(this, jmxPath, name); - - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - mbeanName = "org.apache.cassandra." + jmxPath + ":type=" + name; - - try - { - mbs.registerMBean(this, new ObjectName(mbeanName)); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } - - private void unregisterMBean() - { - try - { - ManagementFactory.getPlatformMBeanServer().unregisterMBean(new ObjectName(mbeanName)); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - - // release metrics - metrics.release(); - } - - @Override - public synchronized void shutdown() - { - // synchronized, because there is no way to access super.mainLock, which would be - // the preferred way to make this threadsafe - if (!isShutdown()) - { - unregisterMBean(); - } - super.shutdown(); - } - - public int getCoreThreads() - { - return 0; - } - - public void setCoreThreads(int number) - { - throw new UnsupportedOperationException(); - } - - public void setMaximumThreads(int number) - { - throw new UnsupportedOperationException(); - } - } - - public TracingAwareExecutorService newExecutor(int maxConcurrency, int maxQueuedTasks, String name, String jmxPath) - { - JMXEnabledSEPExecutor executor = new JMXEnabledSEPExecutor(maxConcurrency, maxQueuedTasks, name, jmxPath); - executors.add(executor); - return executor; - } -} diff --git a/src/java/org/apache/cassandra/concurrent/JMXEnabledThreadPoolExecutor.java b/src/java/org/apache/cassandra/concurrent/JMXEnabledThreadPoolExecutor.java index 3f60df163b..2b8670155c 100644 --- a/src/java/org/apache/cassandra/concurrent/JMXEnabledThreadPoolExecutor.java +++ b/src/java/org/apache/cassandra/concurrent/JMXEnabledThreadPoolExecutor.java @@ -36,7 +36,7 @@ import org.apache.cassandra.metrics.ThreadPoolMetrics; public class JMXEnabledThreadPoolExecutor extends DebuggableThreadPoolExecutor implements JMXEnabledThreadPoolExecutorMBean { private final String mbeanName; - private final ThreadPoolMetrics metrics; + public final ThreadPoolMetrics metrics; public JMXEnabledThreadPoolExecutor(String threadPoolName) { @@ -132,30 +132,17 @@ public class JMXEnabledThreadPoolExecutor extends DebuggableThreadPoolExecutor i return super.shutdownNow(); } - /** - * Get the number of completed tasks - */ - public long getCompletedTasks() - { - return getCompletedTaskCount(); - } - /** - * Get the number of tasks waiting to be executed - */ - public long getPendingTasks() - { - return getTaskCount() - getCompletedTaskCount(); - } + public int getTotalBlockedTasks() { - return (int) metrics.totalBlocked.count(); + return (int) metrics.totalBlocked.getCount(); } public int getCurrentlyBlockedTasks() { - return (int) metrics.currentBlocked.count(); + return (int) metrics.currentBlocked.getCount(); } public int getCoreThreads() diff --git a/src/java/org/apache/cassandra/concurrent/JMXEnabledThreadPoolExecutorMBean.java b/src/java/org/apache/cassandra/concurrent/JMXEnabledThreadPoolExecutorMBean.java index ed2a2ac486..fb964aec13 100644 --- a/src/java/org/apache/cassandra/concurrent/JMXEnabledThreadPoolExecutorMBean.java +++ b/src/java/org/apache/cassandra/concurrent/JMXEnabledThreadPoolExecutorMBean.java @@ -17,24 +17,9 @@ */ package org.apache.cassandra.concurrent; -/** - * @see org.apache.cassandra.metrics.ThreadPoolMetrics - */ -@Deprecated -public interface JMXEnabledThreadPoolExecutorMBean extends IExecutorMBean + +public interface JMXEnabledThreadPoolExecutorMBean { - /** - * Get the number of tasks that had blocked before being accepted (or - * rejected). - */ - public int getTotalBlockedTasks(); - - /** - * Get the number of tasks currently blocked, waiting to be accepted by - * the executor (because all threads are busy and the backing queue is full). - */ - public int getCurrentlyBlockedTasks(); - /** * Returns core pool size of thread pool. */ diff --git a/src/java/org/apache/cassandra/concurrent/SEPExecutor.java b/src/java/org/apache/cassandra/concurrent/SEPExecutor.java index f6d1940fee..d9a0fa8a20 100644 --- a/src/java/org/apache/cassandra/concurrent/SEPExecutor.java +++ b/src/java/org/apache/cassandra/concurrent/SEPExecutor.java @@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import org.apache.cassandra.metrics.SEPMetrics; import org.apache.cassandra.utils.concurrent.SimpleCondition; import org.apache.cassandra.utils.concurrent.WaitQueue; @@ -35,6 +36,7 @@ public class SEPExecutor extends AbstractTracingAwareExecutorService public final int maxWorkers; private final int maxTasksQueued; + private final SEPMetrics metrics; // stores both a set of work permits and task permits: // bottom 32 bits are number of queued tasks, in the range [0..maxTasksQueued] (initially 0) @@ -43,8 +45,6 @@ public class SEPExecutor extends AbstractTracingAwareExecutorService // producers wait on this when there is no room on the queue private final WaitQueue hasRoom = new WaitQueue(); - private final AtomicLong totalBlocked = new AtomicLong(); - private final AtomicInteger currentlyBlocked = new AtomicInteger(); private final AtomicLong completedTasks = new AtomicLong(); volatile boolean shuttingDown = false; @@ -53,12 +53,13 @@ public class SEPExecutor extends AbstractTracingAwareExecutorService // TODO: see if other queue implementations might improve throughput protected final ConcurrentLinkedQueue> tasks = new ConcurrentLinkedQueue<>(); - SEPExecutor(SharedExecutorPool pool, int maxWorkers, int maxTasksQueued) + SEPExecutor(SharedExecutorPool pool, int maxWorkers, int maxTasksQueued, String jmxPath, String name) { this.pool = pool; this.maxWorkers = maxWorkers; this.maxTasksQueued = maxTasksQueued; this.permits.set(combine(0, maxWorkers)); + this.metrics = new SEPMetrics(this, jmxPath, name); } protected void onCompletion() @@ -116,10 +117,11 @@ public class SEPExecutor extends AbstractTracingAwareExecutorService // if we're blocking, we might as well directly schedule a worker if we aren't already at max if (takeWorkPermit(true)) pool.schedule(new Work(this)); - totalBlocked.incrementAndGet(); - currentlyBlocked.incrementAndGet(); + + metrics.totalBlocked.inc(); + metrics.currentBlocked.inc(); s.awaitUninterruptibly(); - currentlyBlocked.decrementAndGet(); + metrics.currentBlocked.dec(); } else // don't propagate our signal when we cancel, just cancel s.cancel(); @@ -207,6 +209,9 @@ public class SEPExecutor extends AbstractTracingAwareExecutorService pool.executors.remove(this); if (getActiveCount() == 0) shutdown.signalAll(); + + // release metrics + metrics.release(); } public synchronized List shutdownNow() @@ -249,21 +254,6 @@ public class SEPExecutor extends AbstractTracingAwareExecutorService return maxWorkers - workPermits(permits.get()); } - public int getTotalBlockedTasks() - { - return (int) totalBlocked.get(); - } - - public int getMaximumThreads() - { - return maxWorkers; - } - - public int getCurrentlyBlockedTasks() - { - return currentlyBlocked.get(); - } - private static int taskPermits(long both) { return (int) both; diff --git a/src/java/org/apache/cassandra/concurrent/SharedExecutorPool.java b/src/java/org/apache/cassandra/concurrent/SharedExecutorPool.java index fb3ff13ba7..8c18c44c69 100644 --- a/src/java/org/apache/cassandra/concurrent/SharedExecutorPool.java +++ b/src/java/org/apache/cassandra/concurrent/SharedExecutorPool.java @@ -54,6 +54,8 @@ import static org.apache.cassandra.concurrent.SEPWorker.Work; public class SharedExecutorPool { + public static final SharedExecutorPool SHARED = new SharedExecutorPool("SharedPool"); + // the name assigned to workers in the pool, and the id suffix final String poolName; final AtomicLong workerId = new AtomicLong(); @@ -100,4 +102,11 @@ public class SharedExecutorPool if (current == 0 && spinningCount.compareAndSet(0, 1)) schedule(Work.SPINNING); } + + public TracingAwareExecutorService newExecutor(int maxConcurrency, int maxQueuedTasks, String jmxPath, String name) + { + SEPExecutor executor = new SEPExecutor(this, maxConcurrency, maxQueuedTasks, jmxPath, name); + executors.add(executor); + return executor; + } } diff --git a/src/java/org/apache/cassandra/concurrent/Stage.java b/src/java/org/apache/cassandra/concurrent/Stage.java index 6192cabad2..4e3700f08c 100644 --- a/src/java/org/apache/cassandra/concurrent/Stage.java +++ b/src/java/org/apache/cassandra/concurrent/Stage.java @@ -17,6 +17,11 @@ */ package org.apache.cassandra.concurrent; +import java.util.Arrays; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + public enum Stage { READ, @@ -31,6 +36,17 @@ public enum Stage INTERNAL_RESPONSE, READ_REPAIR; + public static Iterable jmxEnabledStages() + { + return Iterables.filter(Arrays.asList(values()), new Predicate() + { + public boolean apply(Stage stage) + { + return stage != TRACING; + } + }); + } + public String getJmxType() { switch (this) diff --git a/src/java/org/apache/cassandra/concurrent/StageManager.java b/src/java/org/apache/cassandra/concurrent/StageManager.java index 303f658235..4f03fd5f58 100644 --- a/src/java/org/apache/cassandra/concurrent/StageManager.java +++ b/src/java/org/apache/cassandra/concurrent/StageManager.java @@ -89,7 +89,7 @@ public class StageManager private static TracingAwareExecutorService multiThreadedLowSignalStage(Stage stage, int numThreads) { - return JMXEnabledSharedExecutorPool.SHARED.newExecutor(numThreads, Integer.MAX_VALUE, stage.getJmxName(), stage.getJmxType()); + return SharedExecutorPool.SHARED.newExecutor(numThreads, Integer.MAX_VALUE, stage.getJmxType(), stage.getJmxName()); } /** diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index 9824e4a4aa..15aef94521 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -365,7 +365,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { case PERCENTILE: // get percentile in nanos - assert metric.coordinatorReadLatency.durationUnit() == TimeUnit.MICROSECONDS; sampleLatencyNanos = (long) (metric.coordinatorReadLatency.getSnapshot().getValue(retryPolicy.value) * 1000d); break; case CUSTOM: @@ -425,25 +424,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean metric.release(); } - public long getMinRowSize() - { - return metric.minRowSize.value(); - } - - public long getMaxRowSize() - { - return metric.maxRowSize.value(); - } - - public long getMeanRowSize() - { - return metric.meanRowSize.value(); - } - - public int getMeanColumns() - { - return data.getMeanColumns(); - } public static ColumnFamilyStore createColumnFamilyStore(Keyspace keyspace, String columnFamily, boolean loadSSTables) { @@ -1426,20 +1406,8 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean return valid; } - public long getMemtableColumnsCount() - { - return metric.memtableColumnsCount.value(); - } - public long getMemtableDataSize() - { - return metric.memtableOnHeapSize.value(); - } - public int getMemtableSwitchCount() - { - return (int) metric.memtableSwitchCount.count(); - } /** * Package protected for access from the CompactionManager. @@ -1459,71 +1427,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean return data.getUncompactingSSTables(); } - public long[] getRecentSSTablesPerReadHistogram() - { - return metric.recentSSTablesPerRead.getBuckets(true); - } - - public long[] getSSTablesPerReadHistogram() - { - return metric.sstablesPerRead.getBuckets(false); - } - - public long getReadCount() - { - return metric.readLatency.latency.count(); - } - - public double getRecentReadLatencyMicros() - { - return metric.readLatency.getRecentLatency(); - } - - public long[] getLifetimeReadLatencyHistogramMicros() - { - return metric.readLatency.totalLatencyHistogram.getBuckets(false); - } - - public long[] getRecentReadLatencyHistogramMicros() - { - return metric.readLatency.recentLatencyHistogram.getBuckets(true); - } - - public long getTotalReadLatencyMicros() - { - return metric.readLatency.totalLatency.count(); - } - - public int getPendingTasks() - { - return (int) metric.pendingFlushes.count(); - } - - public long getWriteCount() - { - return metric.writeLatency.latency.count(); - } - - public long getTotalWriteLatencyMicros() - { - return metric.writeLatency.totalLatency.count(); - } - - public double getRecentWriteLatencyMicros() - { - return metric.writeLatency.getRecentLatency(); - } - - public long[] getLifetimeWriteLatencyHistogramMicros() - { - return metric.writeLatency.totalLatencyHistogram.getBuckets(false); - } - - public long[] getRecentWriteLatencyHistogramMicros() - { - return metric.writeLatency.recentLatencyHistogram.getBuckets(true); - } - public ColumnFamily getColumnFamily(DecoratedKey key, Composite start, Composite finish, @@ -2328,19 +2231,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean return directories.getSnapshotDetails(); } - public long getTotalDiskSpaceUsed() + public boolean hasUnreclaimedSpace() { - return metric.totalDiskSpaceUsed.count(); - } - - public long getLiveDiskSpaceUsed() - { - return metric.liveDiskSpaceUsed.count(); - } - - public int getLiveSSTableCount() - { - return metric.liveSSTableCount.value(); + return metric.liveDiskSpaceUsed.getCount() < metric.totalDiskSpaceUsed.getCount(); } /** @@ -2613,45 +2506,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean return runWithCompactionsDisabled(callable, false); } - public long getBloomFilterFalsePositives() - { - return metric.bloomFilterFalsePositives.value(); - } - - public long getRecentBloomFilterFalsePositives() - { - return metric.recentBloomFilterFalsePositives.value(); - } - - public double getBloomFilterFalseRatio() - { - return metric.bloomFilterFalseRatio.value(); - } - - public double getRecentBloomFilterFalseRatio() - { - return metric.recentBloomFilterFalseRatio.value(); - } - - public long getBloomFilterDiskSpaceUsed() - { - return metric.bloomFilterDiskSpaceUsed.value(); - } - - public long getBloomFilterOffHeapMemoryUsed() - { - return metric.bloomFilterOffHeapMemoryUsed.value(); - } - - public long getIndexSummaryOffHeapMemoryUsed() - { - return metric.indexSummaryOffHeapMemoryUsed.value(); - } - - public long getCompressionMetadataOffHeapMemoryUsed() - { - return metric.compressionMetadataOffHeapMemoryUsed.value(); - } @Override public String toString() @@ -2748,38 +2602,18 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean "is deprecated, set the compaction strategy option 'enabled' to 'false' instead or use the nodetool command 'disableautocompaction'."); } - public double getTombstonesPerSlice() - { - return metric.tombstoneScannedHistogram.cf.getSnapshot().getMedian(); - } - - public double getLiveCellsPerSlice() - { - return metric.liveScannedHistogram.cf.getSnapshot().getMedian(); - } - // End JMX get/set. + public int getMeanColumns() + { + return data.getMeanColumns(); + } + public long estimateKeys() { return data.estimatedKeys(); } - public long[] getEstimatedRowSizeHistogram() - { - return metric.estimatedRowSizeHistogram.value(); - } - - public long[] getEstimatedColumnCountHistogram() - { - return metric.estimatedColumnCountHistogram.value(); - } - - public double getCompressionRatio() - { - return metric.compressionRatio.value(); - } - /** true if this CFS contains secondary index data */ public boolean isIndex() { diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java b/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java index d082125948..1aad5c3b3d 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java @@ -34,222 +34,11 @@ public interface ColumnFamilyStoreMBean */ public String getColumnFamilyName(); - /** - * Returns the total amount of data stored in the memtable, including - * column related overhead. - * - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableOnHeapSize - * @return The size in bytes. - * @deprecated - */ - @Deprecated - public long getMemtableDataSize(); - - /** - * Returns the total number of columns present in the memtable. - * - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableColumnsCount - * @return The number of columns. - */ - @Deprecated - public long getMemtableColumnsCount(); - - /** - * Returns the number of times that a flush has resulted in the - * memtable being switched out. - * - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#memtableSwitchCount - * @return the number of memtable switches - */ - @Deprecated - public int getMemtableSwitchCount(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentSSTablesPerRead - * @return a histogram of the number of sstable data files accessed per read: reading this property resets it - */ - @Deprecated - public long[] getRecentSSTablesPerReadHistogram(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#sstablesPerReadHistogram - * @return a histogram of the number of sstable data files accessed per read - */ - @Deprecated - public long[] getSSTablesPerReadHistogram(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency - * @return the number of read operations on this column family - */ - @Deprecated - public long getReadCount(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency - * @return total read latency (divide by getReadCount() for average) - */ - @Deprecated - public long getTotalReadLatencyMicros(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency - * @return an array representing the latency histogram - */ - @Deprecated - public long[] getLifetimeReadLatencyHistogramMicros(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency - * @return an array representing the latency histogram - */ - @Deprecated - public long[] getRecentReadLatencyHistogramMicros(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#readLatency - * @return average latency per read operation since the last call - */ - @Deprecated - public double getRecentReadLatencyMicros(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency - * @return the number of write operations on this column family - */ - @Deprecated - public long getWriteCount(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency - * @return total write latency (divide by getReadCount() for average) - */ - @Deprecated - public long getTotalWriteLatencyMicros(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency - * @return an array representing the latency histogram - */ - @Deprecated - public long[] getLifetimeWriteLatencyHistogramMicros(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency - * @return an array representing the latency histogram - */ - @Deprecated - public long[] getRecentWriteLatencyHistogramMicros(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#writeLatency - * @return average latency per write operation since the last call - */ - @Deprecated - public double getRecentWriteLatencyMicros(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#pendingFlushes - * @return the estimated number of tasks pending for this column family - */ - @Deprecated - public int getPendingTasks(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#liveSSTableCount - * @return the number of SSTables on disk for this CF - */ - @Deprecated - public int getLiveSSTableCount(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#liveDiskSpaceUsed - * @return disk space used by SSTables belonging to this CF - */ - @Deprecated - public long getLiveDiskSpaceUsed(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#totalDiskSpaceUsed - * @return total disk space used by SSTables belonging to this CF, including obsolete ones waiting to be GC'd - */ - @Deprecated - public long getTotalDiskSpaceUsed(); - /** * force a major compaction of this column family */ public void forceMajorCompaction() throws ExecutionException, InterruptedException; - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#minRowSize - * @return the size of the smallest compacted row - */ - @Deprecated - public long getMinRowSize(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#maxRowSize - * @return the size of the largest compacted row - */ - @Deprecated - public long getMaxRowSize(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#meanRowSize - * @return the average row size across all the sstables - */ - @Deprecated - public long getMeanRowSize(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterFalsePositives - */ - @Deprecated - public long getBloomFilterFalsePositives(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentBloomFilterFalsePositives - */ - @Deprecated - public long getRecentBloomFilterFalsePositives(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterFalseRatio - */ - @Deprecated - public double getBloomFilterFalseRatio(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#recentBloomFilterFalseRatio - */ - @Deprecated - public double getRecentBloomFilterFalseRatio(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterDiskSpaceUsed - */ - @Deprecated - public long getBloomFilterDiskSpaceUsed(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#bloomFilterOffHeapMemoryUsed - */ - @Deprecated - public long getBloomFilterOffHeapMemoryUsed(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#indexSummaryOffHeapMemoryUsed - */ - @Deprecated - public long getIndexSummaryOffHeapMemoryUsed(); - - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#compressionMetadataOffHeapMemoryUsed - */ - @Deprecated - public long getCompressionMetadataOffHeapMemoryUsed(); - /** * Gets the minimum number of sstables in queue before compaction kicks off */ @@ -304,31 +93,8 @@ public interface ColumnFamilyStoreMBean public boolean isAutoCompactionDisabled(); - /** Number of tombstoned cells retreived during the last slicequery */ - @Deprecated - public double getTombstonesPerSlice(); - - /** Number of live cells retreived during the last slicequery */ - @Deprecated - public double getLiveCellsPerSlice(); - public long estimateKeys(); - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#estimatedRowSizeHistogram - */ - @Deprecated - public long[] getEstimatedRowSizeHistogram(); - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#estimatedColumnCountHistogram - */ - @Deprecated - public long[] getEstimatedColumnCountHistogram(); - /** - * @see org.apache.cassandra.metrics.ColumnFamilyMetrics#compressionRatio - */ - @Deprecated - public double getCompressionRatio(); /** * Returns a list of the names of the built column indexes for current store diff --git a/src/java/org/apache/cassandra/db/HintedHandOffManager.java b/src/java/org/apache/cassandra/db/HintedHandOffManager.java index 2bc26aea41..324943a9c1 100644 --- a/src/java/org/apache/cassandra/db/HintedHandOffManager.java +++ b/src/java/org/apache/cassandra/db/HintedHandOffManager.java @@ -482,7 +482,7 @@ public class HintedHandOffManager implements HintedHandOffManagerMBean if (meanColumnCount <= 0) return PAGE_SIZE; - int averageColumnSize = (int) (hintStore.getMeanRowSize() / meanColumnCount); + int averageColumnSize = (int) (hintStore.metric.meanRowSize.getValue() / meanColumnCount); if (averageColumnSize <= 0) return PAGE_SIZE; diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java index 416b51de3c..55b00222b4 100644 --- a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java +++ b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java @@ -291,26 +291,6 @@ public class CommitLog implements CommitLogMBean } } - @Override - public long getCompletedTasks() - { - return metrics.completedTasks.value(); - } - - @Override - public long getPendingTasks() - { - return metrics.pendingTasks.value(); - } - - /** - * @return the total size occupied by commitlog segments expressed in bytes. (used by MBean) - */ - public long getTotalCommitlogSize() - { - return metrics.totalCommitLogSize.value(); - } - public List getActiveSegmentNames() { List segmentNames = new ArrayList<>(); diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLogMBean.java b/src/java/org/apache/cassandra/db/commitlog/CommitLogMBean.java index 6c0d8d75ee..150ec20645 100644 --- a/src/java/org/apache/cassandra/db/commitlog/CommitLogMBean.java +++ b/src/java/org/apache/cassandra/db/commitlog/CommitLogMBean.java @@ -23,27 +23,6 @@ import java.util.List; public interface CommitLogMBean { - /** - * Get the number of completed tasks - * @see org.apache.cassandra.metrics.CommitLogMetrics#completedTasks - */ - @Deprecated - public long getCompletedTasks(); - - /** - * Get the number of tasks waiting to be executed - * @see org.apache.cassandra.metrics.CommitLogMetrics#pendingTasks - */ - @Deprecated - public long getPendingTasks(); - - /** - * Get the current size used by all the commitlog segments. - * @see org.apache.cassandra.metrics.CommitLogMetrics#totalCommitLogSize - */ - @Deprecated - public long getTotalCommitlogSize(); - /** * Recover a single file. */ diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionInfo.java b/src/java/org/apache/cassandra/db/compaction/CompactionInfo.java index d086eef0b7..3ee3a685c3 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionInfo.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionInfo.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.UUID; import org.apache.cassandra.config.CFMetaData; +import org.apache.cassandra.metrics.StorageMetrics; import org.apache.cassandra.service.StorageService; /** Implements serializable to allow structured info to be returned via JMX. */ @@ -121,7 +122,7 @@ public final class CompactionInfo implements Serializable { private volatile boolean stopRequested = false; public abstract CompactionInfo getCompactionInfo(); - double load = StorageService.instance.getLoad(); + double load = StorageMetrics.load.getCount(); double reportedSeverity = 0d; public void stop() diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java index 99945b6282..a1672a2ce0 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java @@ -1369,22 +1369,22 @@ public class CompactionManager implements CompactionManagerMBean public long getTotalBytesCompacted() { - return metrics.bytesCompacted.count(); + return metrics.bytesCompacted.getCount(); } public long getTotalCompactionsCompleted() { - return metrics.totalCompactionsCompleted.count(); + return metrics.totalCompactionsCompleted.getCount(); } public int getPendingTasks() { - return metrics.pendingTasks.value(); + return metrics.pendingTasks.getValue(); } public long getCompletedTasks() { - return metrics.completedTasks.value(); + return metrics.completedTasks.getValue(); } private static class CleanupInfo extends CompactionInfo.Holder diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionManagerMBean.java b/src/java/org/apache/cassandra/db/compaction/CompactionManagerMBean.java index 6900b9f4da..9c361927af 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionManagerMBean.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionManagerMBean.java @@ -32,34 +32,6 @@ public interface CompactionManagerMBean /** compaction history **/ public TabularData getCompactionHistory(); - /** - * @see org.apache.cassandra.metrics.CompactionMetrics#pendingTasks - * @return estimated number of compactions remaining to perform - */ - @Deprecated - public int getPendingTasks(); - - /** - * @see org.apache.cassandra.metrics.CompactionMetrics#completedTasks - * @return number of completed compactions since server [re]start - */ - @Deprecated - public long getCompletedTasks(); - - /** - * @see org.apache.cassandra.metrics.CompactionMetrics#bytesCompacted - * @return total number of bytes compacted since server [re]start - */ - @Deprecated - public long getTotalBytesCompacted(); - - /** - * @see org.apache.cassandra.metrics.CompactionMetrics#totalCompactionsCompleted - * @return total number of compactions since server [re]start - */ - @Deprecated - public long getTotalCompactionsCompleted(); - /** * Triggers the compaction of user specified sstables. * You can specify files from various keyspaces and columnfamilies. diff --git a/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java b/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java index fc2ff93131..f908e8c36b 100644 --- a/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java +++ b/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java @@ -214,7 +214,7 @@ public abstract class ExtendedFilter { // if we have a high chance of getting all the columns in a single index slice (and it's not too costly), do that. // otherwise, the extraFilter (lazily created) will fetch by name the columns referenced by the additional expressions. - if (cfs.getMaxRowSize() < DatabaseDescriptor.getColumnIndexSize()) + if (cfs.metric.maxRowSize.getValue() < DatabaseDescriptor.getColumnIndexSize()) { logger.trace("Expanding slice filter to entire row to cover additional expressions"); return new SliceQueryFilter(ColumnSlice.ALL_COLUMNS_ARRAY, ((SliceQueryFilter)filter).reversed, Integer.MAX_VALUE); diff --git a/src/java/org/apache/cassandra/gms/Gossiper.java b/src/java/org/apache/cassandra/gms/Gossiper.java index 74b2c8e9b2..111536c5e2 100644 --- a/src/java/org/apache/cassandra/gms/Gossiper.java +++ b/src/java/org/apache/cassandra/gms/Gossiper.java @@ -673,7 +673,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean long now = System.currentTimeMillis(); long nowNano = System.nanoTime(); - long pending = ((JMXEnabledThreadPoolExecutor) StageManager.getStage(Stage.GOSSIP)).getPendingTasks(); + long pending = ((JMXEnabledThreadPoolExecutor) StageManager.getStage(Stage.GOSSIP)).metrics.pendingTasks.getValue(); if (pending > 0 && lastProcessedMessageAt < now - 1000) { // if some new messages just arrived, give the executor some time to work on them diff --git a/src/java/org/apache/cassandra/locator/DynamicEndpointSnitch.java b/src/java/org/apache/cassandra/locator/DynamicEndpointSnitch.java index e4b714c459..9d71a673d0 100644 --- a/src/java/org/apache/cassandra/locator/DynamicEndpointSnitch.java +++ b/src/java/org/apache/cassandra/locator/DynamicEndpointSnitch.java @@ -24,6 +24,8 @@ import java.net.UnknownHostException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; + +import com.codahale.metrics.ExponentiallyDecayingReservoir; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -33,7 +35,6 @@ import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.FBUtilities; -import com.yammer.metrics.stats.ExponentiallyDecayingSample; /** * A dynamic snitch that sorts endpoints by latency with an adapted phi failure detector @@ -55,7 +56,7 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa private boolean registered = false; private final ConcurrentHashMap scores = new ConcurrentHashMap(); - private final ConcurrentHashMap samples = new ConcurrentHashMap(); + private final ConcurrentHashMap samples = new ConcurrentHashMap<>(); public final IEndpointSnitch subsnitch; @@ -217,10 +218,10 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa public void receiveTiming(InetAddress host, long latency) // this is cheap { - ExponentiallyDecayingSample sample = samples.get(host); + ExponentiallyDecayingReservoir sample = samples.get(host); if (sample == null) { - ExponentiallyDecayingSample maybeNewSample = new ExponentiallyDecayingSample(WINDOW_SIZE, ALPHA); + ExponentiallyDecayingReservoir maybeNewSample = new ExponentiallyDecayingReservoir(WINDOW_SIZE, ALPHA); sample = samples.putIfAbsent(host, maybeNewSample); if (sample == null) sample = maybeNewSample; @@ -244,14 +245,14 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa double maxLatency = 1; // We're going to weight the latency for each host against the worst one we see, to // arrive at sort of a 'badness percentage' for them. First, find the worst for each: - for (Map.Entry entry : samples.entrySet()) + for (Map.Entry entry : samples.entrySet()) { double mean = entry.getValue().getSnapshot().getMedian(); if (mean > maxLatency) maxLatency = mean; } // now make another pass to do the weighting based on the maximums we found before - for (Map.Entry entry: samples.entrySet()) + for (Map.Entry entry: samples.entrySet()) { double score = entry.getValue().getSnapshot().getMedian() / maxLatency; // finally, add the severity without any weighting, since hosts scale this relative to their own load and the size of the task causing the severity. @@ -265,8 +266,7 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa private void reset() { - for (ExponentiallyDecayingSample sample : samples.values()) - sample.clear(); + samples.clear(); } public Map getScores() @@ -295,7 +295,7 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa { InetAddress host = InetAddress.getByName(hostname); ArrayList timings = new ArrayList(); - ExponentiallyDecayingSample sample = samples.get(host); + ExponentiallyDecayingReservoir sample = samples.get(host); if (sample != null) { for (double time: sample.getSnapshot().getValues()) diff --git a/src/java/org/apache/cassandra/metrics/CASClientRequestMetrics.java b/src/java/org/apache/cassandra/metrics/CASClientRequestMetrics.java index 3210d4588f..e6f2b81cb7 100644 --- a/src/java/org/apache/cassandra/metrics/CASClientRequestMetrics.java +++ b/src/java/org/apache/cassandra/metrics/CASClientRequestMetrics.java @@ -18,12 +18,14 @@ */ package org.apache.cassandra.metrics; -import com.yammer.metrics.*; -import com.yammer.metrics.core.*; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Histogram; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + public class CASClientRequestMetrics extends ClientRequestMetrics { - public final Histogram contention; /* Used only for write */ public final Counter conditionNotMet; @@ -32,16 +34,16 @@ public class CASClientRequestMetrics extends ClientRequestMetrics public CASClientRequestMetrics(String scope) { super(scope); - contention = Metrics.newHistogram(factory.createMetricName("ContentionHistogram"), true); - conditionNotMet = Metrics.newCounter(factory.createMetricName("ConditionNotMet")); - unfinishedCommit = Metrics.newCounter(factory.createMetricName("UnfinishedCommit")); + contention = Metrics.histogram(factory.createMetricName("ContentionHistogram")); + conditionNotMet = Metrics.counter(factory.createMetricName("ConditionNotMet")); + unfinishedCommit = Metrics.counter(factory.createMetricName("UnfinishedCommit")); } public void release() { super.release(); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("ContentionHistogram")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("ConditionNotMet")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("UnfinishedCommit")); + Metrics.remove(factory.createMetricName("ContentionHistogram")); + Metrics.remove(factory.createMetricName("ConditionNotMet")); + Metrics.remove(factory.createMetricName("UnfinishedCommit")); } } diff --git a/src/java/org/apache/cassandra/metrics/CQLMetrics.java b/src/java/org/apache/cassandra/metrics/CQLMetrics.java index a7076ddc43..1020e927db 100644 --- a/src/java/org/apache/cassandra/metrics/CQLMetrics.java +++ b/src/java/org/apache/cassandra/metrics/CQLMetrics.java @@ -17,11 +17,12 @@ */ package org.apache.cassandra.metrics; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.RatioGauge; import org.apache.cassandra.cql3.QueryProcessor; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.util.RatioGauge; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; public class CQLMetrics { @@ -36,27 +37,32 @@ public class CQLMetrics public CQLMetrics() { - regularStatementsExecuted = Metrics.newCounter(factory.createMetricName("RegularStatementsExecuted")); - preparedStatementsExecuted = Metrics.newCounter(factory.createMetricName("PreparedStatementsExecuted")); - preparedStatementsEvicted = Metrics.newCounter(factory.createMetricName("PreparedStatementsEvicted")); + regularStatementsExecuted = Metrics.counter(factory.createMetricName("RegularStatementsExecuted")); + preparedStatementsExecuted = Metrics.counter(factory.createMetricName("PreparedStatementsExecuted")); + preparedStatementsEvicted = Metrics.counter(factory.createMetricName("PreparedStatementsEvicted")); - preparedStatementsCount = Metrics.newGauge(factory.createMetricName("PreparedStatementsCount"), new Gauge() + preparedStatementsCount = Metrics.register(factory.createMetricName("PreparedStatementsCount"), new Gauge() { - public Integer value() + public Integer getValue() { return QueryProcessor.preparedStatementsCount(); } }); - preparedStatementsRatio = Metrics.newGauge(factory.createMetricName("PreparedStatementsRatio"), new RatioGauge() + preparedStatementsRatio = Metrics.register(factory.createMetricName("PreparedStatementsRatio"), new RatioGauge() { + public Ratio getRatio() + { + return Ratio.of(getNumerator(), getDenominator()); + } + public double getNumerator() { - return preparedStatementsExecuted.count(); + return preparedStatementsExecuted.getCount(); } public double getDenominator() { - return regularStatementsExecuted.count() + preparedStatementsExecuted.count(); + return regularStatementsExecuted.getCount() + preparedStatementsExecuted.getCount(); } }); } diff --git a/src/java/org/apache/cassandra/metrics/CacheMetrics.java b/src/java/org/apache/cassandra/metrics/CacheMetrics.java index 9d5d78396a..8b00e1c4ed 100644 --- a/src/java/org/apache/cassandra/metrics/CacheMetrics.java +++ b/src/java/org/apache/cassandra/metrics/CacheMetrics.java @@ -17,16 +17,15 @@ */ package org.apache.cassandra.metrics; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.util.RatioGauge; - +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; +import com.codahale.metrics.RatioGauge; import org.apache.cassandra.cache.ICache; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + /** * Metrics for {@code ICache}. */ @@ -45,9 +44,6 @@ public class CacheMetrics /** Total number of cache entries */ public final Gauge entries; - private final AtomicLong lastRequests = new AtomicLong(0); - private final AtomicLong lastHits = new AtomicLong(0); - /** * Create metrics for given cache. * @@ -58,57 +54,36 @@ public class CacheMetrics { MetricNameFactory factory = new DefaultNameFactory("Cache", type); - capacity = Metrics.newGauge(factory.createMetricName("Capacity"), new Gauge() + capacity = Metrics.register(factory.createMetricName("Capacity"), new Gauge() { - public Long value() + public Long getValue() { return cache.capacity(); } }); - hits = Metrics.newMeter(factory.createMetricName("Hits"), "hits", TimeUnit.SECONDS); - requests = Metrics.newMeter(factory.createMetricName("Requests"), "requests", TimeUnit.SECONDS); - hitRate = Metrics.newGauge(factory.createMetricName("HitRate"), new RatioGauge() + hits = Metrics.meter(factory.createMetricName("Hits")); + requests = Metrics.meter(factory.createMetricName("Requests")); + hitRate = Metrics.register(factory.createMetricName("HitRate"), new RatioGauge() { - protected double getNumerator() + @Override + public Ratio getRatio() { - return hits.count(); - } - - protected double getDenominator() - { - return requests.count(); + return Ratio.of(hits.getCount(), requests.getCount()); } }); - size = Metrics.newGauge(factory.createMetricName("Size"), new Gauge() + size = Metrics.register(factory.createMetricName("Size"), new Gauge() { - public Long value() + public Long getValue() { return cache.weightedSize(); } }); - entries = Metrics.newGauge(factory.createMetricName("Entries"), new Gauge() + entries = Metrics.register(factory.createMetricName("Entries"), new Gauge() { - public Integer value() + public Integer getValue() { return cache.size(); } }); } - - // for backward compatibility - @Deprecated - public double getRecentHitRate() - { - long r = requests.count(); - long h = hits.count(); - try - { - return ((double)(h - lastHits.get())) / (r - lastRequests.get()); - } - finally - { - lastRequests.set(r); - lastHits.set(h); - } - } } diff --git a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java new file mode 100644 index 0000000000..27eec895be --- /dev/null +++ b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java @@ -0,0 +1,791 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.metrics; + +import java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.codahale.metrics.*; +import javax.management.*; + +/** + * Makes integrating 3.0 metrics API with 2.0. + *

+ * The 3.0 API comes with poor JMX integration + */ +public class CassandraMetricsRegistry extends MetricRegistry +{ + protected static final Logger logger = LoggerFactory.getLogger(CassandraMetricsRegistry.class); + + public static final CassandraMetricsRegistry Metrics = new CassandraMetricsRegistry(); + private MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + + private CassandraMetricsRegistry() + { + super(); + } + + public Counter counter(MetricName name) + { + Counter counter = counter(name.getMetricName()); + registerMBean(counter, name.getMBeanName()); + + return counter; + } + + public Meter meter(MetricName name) + { + Meter meter = meter(name.getMetricName()); + registerMBean(meter, name.getMBeanName()); + + return meter; + } + + public Histogram histogram(MetricName name) + { + Histogram histogram = register(name, new ClearableHistogram(new EstimatedHistogramReservoir())); + registerMBean(histogram, name.getMBeanName()); + + return histogram; + } + + public Timer timer(MetricName name) + { + Timer timer = register(name, new Timer(new EstimatedHistogramReservoir())); + registerMBean(timer, name.getMBeanName()); + + return timer; + } + + public T register(MetricName name, T metric) + { + try + { + register(name.getMetricName(), metric); + registerMBean(metric, name.getMBeanName()); + return metric; + } + catch (IllegalArgumentException e) + { + Metric existing = Metrics.getMetrics().get(name.getMetricName()); + return (T)existing; + } + } + + public boolean remove(MetricName name) + { + boolean removed = remove(name.getMetricName()); + + try + { + mBeanServer.unregisterMBean(name.getMBeanName()); + } catch (InstanceNotFoundException | MBeanRegistrationException e) + { + logger.debug("Unable to remove mbean"); + } + + return removed; + } + + private void registerMBean(Metric metric, ObjectName name) + { + AbstractBean mbean; + + if (metric instanceof Gauge) + { + mbean = new JmxGauge((Gauge) metric, name); + } else if (metric instanceof Counter) + { + mbean = new JmxCounter((Counter) metric, name); + } else if (metric instanceof Histogram) + { + mbean = new JmxHistogram((Histogram) metric, name); + } else if (metric instanceof Meter) + { + mbean = new JmxMeter((Meter) metric, name, TimeUnit.SECONDS); + } else if (metric instanceof Timer) + { + mbean = new JmxTimer((Timer) metric, name, TimeUnit.SECONDS, TimeUnit.MICROSECONDS); + } else + { + throw new IllegalArgumentException("Unknown metric type: " + metric.getClass()); + } + + try + { + mBeanServer.registerMBean(mbean, name); + } catch (InstanceAlreadyExistsException e) + { + logger.debug("Metric bean already exists", e); + } catch (MBeanRegistrationException e) + { + logger.debug("Unable to register metric bean", e); + } catch (NotCompliantMBeanException e) + { + logger.warn("Unable to register metric bean", e); + } + } + + public interface MetricMBean + { + ObjectName objectName(); + } + + private abstract static class AbstractBean implements MetricMBean + { + private final ObjectName objectName; + + AbstractBean(ObjectName objectName) + { + this.objectName = objectName; + } + + @Override + public ObjectName objectName() + { + return objectName; + } + } + + + public interface JmxGaugeMBean extends MetricMBean + { + Object getValue(); + } + + private static class JmxGauge extends AbstractBean implements JmxGaugeMBean + { + private final Gauge metric; + + private JmxGauge(Gauge metric, ObjectName objectName) + { + super(objectName); + this.metric = metric; + } + + @Override + public Object getValue() + { + return metric.getValue(); + } + } + + public interface JmxHistogramMBean extends MetricMBean + { + long getCount(); + + long getMin(); + + long getMax(); + + double getMean(); + + double getStdDev(); + + double get50thPercentile(); + + double get75thPercentile(); + + double get95thPercentile(); + + double get98thPercentile(); + + double get99thPercentile(); + + double get999thPercentile(); + + long[] values(); + } + + private static class JmxHistogram extends AbstractBean implements JmxHistogramMBean + { + private final Histogram metric; + + private JmxHistogram(Histogram metric, ObjectName objectName) + { + super(objectName); + this.metric = metric; + } + + @Override + public double get50thPercentile() + { + return metric.getSnapshot().getMedian(); + } + + @Override + public long getCount() + { + return metric.getCount(); + } + + @Override + public long getMin() + { + return metric.getSnapshot().getMin(); + } + + @Override + public long getMax() + { + return metric.getSnapshot().getMax(); + } + + @Override + public double getMean() + { + return metric.getSnapshot().getMean(); + } + + @Override + public double getStdDev() + { + return metric.getSnapshot().getStdDev(); + } + + @Override + public double get75thPercentile() + { + return metric.getSnapshot().get75thPercentile(); + } + + @Override + public double get95thPercentile() + { + return metric.getSnapshot().get95thPercentile(); + } + + @Override + public double get98thPercentile() + { + return metric.getSnapshot().get98thPercentile(); + } + + @Override + public double get99thPercentile() + { + return metric.getSnapshot().get99thPercentile(); + } + + @Override + public double get999thPercentile() + { + return metric.getSnapshot().get999thPercentile(); + } + + @Override + public long[] values() + { + return metric.getSnapshot().getValues(); + } + } + + public interface JmxCounterMBean extends MetricMBean + { + long getCount(); + } + + private static class JmxCounter extends AbstractBean implements JmxCounterMBean + { + private final Counter metric; + + private JmxCounter(Counter metric, ObjectName objectName) + { + super(objectName); + this.metric = metric; + } + + @Override + public long getCount() + { + return metric.getCount(); + } + } + + public interface JmxMeterMBean extends MetricMBean + { + long getCount(); + + double getMeanRate(); + + double getOneMinuteRate(); + + double getFiveMinuteRate(); + + double getFifteenMinuteRate(); + + String getRateUnit(); + } + + private static class JmxMeter extends AbstractBean implements JmxMeterMBean + { + private final Metered metric; + private final double rateFactor; + private final String rateUnit; + + private JmxMeter(Metered metric, ObjectName objectName, TimeUnit rateUnit) + { + super(objectName); + this.metric = metric; + this.rateFactor = rateUnit.toSeconds(1); + this.rateUnit = "events/" + calculateRateUnit(rateUnit); + } + + @Override + public long getCount() + { + return metric.getCount(); + } + + @Override + public double getMeanRate() + { + return metric.getMeanRate() * rateFactor; + } + + @Override + public double getOneMinuteRate() + { + return metric.getOneMinuteRate() * rateFactor; + } + + @Override + public double getFiveMinuteRate() + { + return metric.getFiveMinuteRate() * rateFactor; + } + + @Override + public double getFifteenMinuteRate() + { + return metric.getFifteenMinuteRate() * rateFactor; + } + + @Override + public String getRateUnit() + { + return rateUnit; + } + + private String calculateRateUnit(TimeUnit unit) + { + final String s = unit.toString().toLowerCase(Locale.US); + return s.substring(0, s.length() - 1); + } + } + + public interface JmxTimerMBean extends JmxMeterMBean + { + double getMin(); + + double getMax(); + + double getMean(); + + double getStdDev(); + + double get50thPercentile(); + + double get75thPercentile(); + + double get95thPercentile(); + + double get98thPercentile(); + + double get99thPercentile(); + + double get999thPercentile(); + + long[] values(); + + String getDurationUnit(); + } + + static class JmxTimer extends JmxMeter implements JmxTimerMBean + { + private final Timer metric; + private final double durationFactor; + private final String durationUnit; + + private JmxTimer(Timer metric, + ObjectName objectName, + TimeUnit rateUnit, + TimeUnit durationUnit) + { + super(metric, objectName, rateUnit); + this.metric = metric; + this.durationFactor = 1.0 / durationUnit.toNanos(1); + this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); + } + + @Override + public double get50thPercentile() + { + return metric.getSnapshot().getMedian() * durationFactor; + } + + @Override + public double getMin() + { + return metric.getSnapshot().getMin() * durationFactor; + } + + @Override + public double getMax() + { + return metric.getSnapshot().getMax() * durationFactor; + } + + @Override + public double getMean() + { + return metric.getSnapshot().getMean() * durationFactor; + } + + @Override + public double getStdDev() + { + return metric.getSnapshot().getStdDev() * durationFactor; + } + + @Override + public double get75thPercentile() + { + return metric.getSnapshot().get75thPercentile() * durationFactor; + } + + @Override + public double get95thPercentile() + { + return metric.getSnapshot().get95thPercentile() * durationFactor; + } + + @Override + public double get98thPercentile() + { + return metric.getSnapshot().get98thPercentile() * durationFactor; + } + + @Override + public double get99thPercentile() + { + return metric.getSnapshot().get99thPercentile() * durationFactor; + } + + @Override + public double get999thPercentile() + { + return metric.getSnapshot().get999thPercentile() * durationFactor; + } + + @Override + public long[] values() + { + return metric.getSnapshot().getValues(); + } + + @Override + public String getDurationUnit() + { + return durationUnit; + } + } + + /** + * A value class encapsulating a metric's owning class and name. + */ + public static class MetricName implements Comparable + { + private final String group; + private final String type; + private final String name; + private final String scope; + private final String mBeanName; + + /** + * Creates a new {@link MetricName} without a scope. + * + * @param klass the {@link Class} to which the {@link Metric} belongs + * @param name the name of the {@link Metric} + */ + public MetricName(Class klass, String name) + { + this(klass, name, null); + } + + /** + * Creates a new {@link MetricName} without a scope. + * + * @param group the group to which the {@link Metric} belongs + * @param type the type to which the {@link Metric} belongs + * @param name the name of the {@link Metric} + */ + public MetricName(String group, String type, String name) + { + this(group, type, name, null); + } + + /** + * Creates a new {@link MetricName} without a scope. + * + * @param klass the {@link Class} to which the {@link Metric} belongs + * @param name the name of the {@link Metric} + * @param scope the scope of the {@link Metric} + */ + public MetricName(Class klass, String name, String scope) + { + this(klass.getPackage() == null ? "" : klass.getPackage().getName(), + klass.getSimpleName().replaceAll("\\$$", ""), + name, + scope); + } + + /** + * Creates a new {@link MetricName} without a scope. + * + * @param group the group to which the {@link Metric} belongs + * @param type the type to which the {@link Metric} belongs + * @param name the name of the {@link Metric} + * @param scope the scope of the {@link Metric} + */ + public MetricName(String group, String type, String name, String scope) + { + this(group, type, name, scope, createMBeanName(group, type, name, scope)); + } + + /** + * Creates a new {@link MetricName} without a scope. + * + * @param group the group to which the {@link Metric} belongs + * @param type the type to which the {@link Metric} belongs + * @param name the name of the {@link Metric} + * @param scope the scope of the {@link Metric} + * @param mBeanName the 'ObjectName', represented as a string, to use when registering the + * MBean. + */ + public MetricName(String group, String type, String name, String scope, String mBeanName) + { + if (group == null || type == null) + { + throw new IllegalArgumentException("Both group and type need to be specified"); + } + if (name == null) + { + throw new IllegalArgumentException("Name needs to be specified"); + } + this.group = group; + this.type = type; + this.name = name; + this.scope = scope; + this.mBeanName = mBeanName; + } + + /** + * Returns the group to which the {@link Metric} belongs. For class-based metrics, this will be + * the package name of the {@link Class} to which the {@link Metric} belongs. + * + * @return the group to which the {@link Metric} belongs + */ + public String getGroup() + { + return group; + } + + /** + * Returns the type to which the {@link Metric} belongs. For class-based metrics, this will be + * the simple class name of the {@link Class} to which the {@link Metric} belongs. + * + * @return the type to which the {@link Metric} belongs + */ + public String getType() + { + return type; + } + + /** + * Returns the name of the {@link Metric}. + * + * @return the name of the {@link Metric} + */ + public String getName() + { + return name; + } + + public String getMetricName() + { + return MetricRegistry.name(group, type, name, scope); + } + + /** + * Returns the scope of the {@link Metric}. + * + * @return the scope of the {@link Metric} + */ + public String getScope() + { + return scope; + } + + /** + * Returns {@code true} if the {@link Metric} has a scope, {@code false} otherwise. + * + * @return {@code true} if the {@link Metric} has a scope + */ + public boolean hasScope() + { + return scope != null; + } + + /** + * Returns the MBean name for the {@link Metric} identified by this metric name. + * + * @return the MBean name + */ + public ObjectName getMBeanName() + { + + String mname = mBeanName; + + if (mname == null) + mname = getMetricName(); + + try + { + + return new ObjectName(mname); + } catch (MalformedObjectNameException e) + { + try + { + return new ObjectName(ObjectName.quote(mname)); + } catch (MalformedObjectNameException e1) + { + throw new RuntimeException(e1); + } + } + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + final MetricName that = (MetricName) o; + return mBeanName.equals(that.mBeanName); + } + + @Override + public int hashCode() + { + return mBeanName.hashCode(); + } + + @Override + public String toString() + { + return mBeanName; + } + + @Override + public int compareTo(MetricName o) + { + return mBeanName.compareTo(o.mBeanName); + } + + private static String createMBeanName(String group, String type, String name, String scope) + { + final StringBuilder nameBuilder = new StringBuilder(); + nameBuilder.append(ObjectName.quote(group)); + nameBuilder.append(":type="); + nameBuilder.append(ObjectName.quote(type)); + if (scope != null) + { + nameBuilder.append(",scope="); + nameBuilder.append(ObjectName.quote(scope)); + } + if (name.length() > 0) + { + nameBuilder.append(",name="); + nameBuilder.append(ObjectName.quote(name)); + } + return nameBuilder.toString(); + } + + /** + * If the group is empty, use the package name of the given class. Otherwise use group + * + * @param group The group to use by default + * @param klass The class being tracked + * @return a group for the metric + */ + public static String chooseGroup(String group, Class klass) + { + if (group == null || group.isEmpty()) + { + group = klass.getPackage() == null ? "" : klass.getPackage().getName(); + } + return group; + } + + /** + * If the type is empty, use the simple name of the given class. Otherwise use type + * + * @param type The type to use by default + * @param klass The class being tracked + * @return a type for the metric + */ + public static String chooseType(String type, Class klass) + { + if (type == null || type.isEmpty()) + { + type = klass.getSimpleName().replaceAll("\\$$", ""); + } + return type; + } + + /** + * If name is empty, use the name of the given method. Otherwise use name + * + * @param name The name to use by default + * @param method The method being tracked + * @return a name for the metric + */ + public static String chooseName(String name, Method method) + { + if (name == null || name.isEmpty()) + { + name = method.getName(); + } + return name; + } + } +} + + diff --git a/src/java/org/apache/cassandra/concurrent/IExecutorMBean.java b/src/java/org/apache/cassandra/metrics/ClearableHistogram.java similarity index 55% rename from src/java/org/apache/cassandra/concurrent/IExecutorMBean.java rename to src/java/org/apache/cassandra/metrics/ClearableHistogram.java index f085b9cdf1..85f2fa9989 100644 --- a/src/java/org/apache/cassandra/concurrent/IExecutorMBean.java +++ b/src/java/org/apache/cassandra/metrics/ClearableHistogram.java @@ -15,27 +15,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.cassandra.concurrent; +package org.apache.cassandra.metrics; + +import com.google.common.annotations.VisibleForTesting; + +import com.codahale.metrics.Histogram; /** - * @see org.apache.cassandra.metrics.ThreadPoolMetrics + * Adds ability to reset a histogram */ -@Deprecated -public interface IExecutorMBean +public class ClearableHistogram extends Histogram { - /** - * Get the current number of running tasks - */ - public int getActiveCount(); + private final EstimatedHistogramReservoir reservoirRef; /** - * Get the number of completed tasks + * Creates a new {@link com.codahale.metrics.Histogram} with the given reservoir. + * + * @param reservoir the reservoir to create a histogram from */ - public long getCompletedTasks(); + public ClearableHistogram(EstimatedHistogramReservoir reservoir) + { + super(reservoir); - /** - * Get the number of tasks waiting to be executed - */ - public long getPendingTasks(); + this.reservoirRef = reservoir; + } + @VisibleForTesting + public void clear() + { + reservoirRef.clear(); + } } diff --git a/src/java/org/apache/cassandra/metrics/ClientMetrics.java b/src/java/org/apache/cassandra/metrics/ClientMetrics.java index 59bc94dd01..4a384ebc2f 100644 --- a/src/java/org/apache/cassandra/metrics/ClientMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ClientMetrics.java @@ -20,8 +20,10 @@ package org.apache.cassandra.metrics; import java.util.concurrent.Callable; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Gauge; +import com.codahale.metrics.Gauge; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + public class ClientMetrics { @@ -35,15 +37,14 @@ public class ClientMetrics public void addCounter(String name, final Callable provider) { - Metrics.newGauge(factory.createMetricName(name), new Gauge() + Metrics.register(factory.createMetricName(name), new Gauge() { - public Integer value() + public Integer getValue() { try { return provider.call(); - } - catch (Exception e) + } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/java/org/apache/cassandra/metrics/ClientRequestMetrics.java b/src/java/org/apache/cassandra/metrics/ClientRequestMetrics.java index 68a2d21944..e3a6970f7e 100644 --- a/src/java/org/apache/cassandra/metrics/ClientRequestMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ClientRequestMetrics.java @@ -20,20 +20,14 @@ */ package org.apache.cassandra.metrics; -import java.util.concurrent.TimeUnit; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Meter; +import com.codahale.metrics.Meter; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + public class ClientRequestMetrics extends LatencyMetrics { - @Deprecated public static final Counter readTimeouts = Metrics.newCounter(DefaultNameFactory.createMetricName("ClientRequestMetrics", "ReadTimeouts", null)); - @Deprecated public static final Counter writeTimeouts = Metrics.newCounter(DefaultNameFactory.createMetricName("ClientRequestMetrics", "WriteTimeouts", null)); - @Deprecated public static final Counter readUnavailables = Metrics.newCounter(DefaultNameFactory.createMetricName("ClientRequestMetrics", "ReadUnavailables", null)); - @Deprecated public static final Counter writeUnavailables = Metrics.newCounter(DefaultNameFactory.createMetricName("ClientRequestMetrics", "WriteUnavailables", null)); - @Deprecated public static final Counter readFailures = Metrics.newCounter(DefaultNameFactory.createMetricName("ClientRequestMetrics", "ReadFailures", null)); - public final Meter timeouts; public final Meter unavailables; public final Meter failures; @@ -42,16 +36,16 @@ public class ClientRequestMetrics extends LatencyMetrics { super("ClientRequest", scope); - timeouts = Metrics.newMeter(factory.createMetricName("Timeouts"), "timeouts", TimeUnit.SECONDS); - unavailables = Metrics.newMeter(factory.createMetricName("Unavailables"), "unavailables", TimeUnit.SECONDS); - failures = Metrics.newMeter(factory.createMetricName("Failures"), "failures", TimeUnit.SECONDS); + timeouts = Metrics.meter(factory.createMetricName("Timeouts")); + unavailables = Metrics.meter(factory.createMetricName("Unavailables")); + failures = Metrics.meter(factory.createMetricName("Failures")); } public void release() { super.release(); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("Timeouts")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("Unavailables")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("Failures")); + Metrics.remove(factory.createMetricName("Timeouts")); + Metrics.remove(factory.createMetricName("Unavailables")); + Metrics.remove(factory.createMetricName("Failures")); } } diff --git a/src/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java b/src/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java index 07246cfef7..90b6b80fff 100644 --- a/src/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java @@ -20,8 +20,9 @@ package org.apache.cassandra.metrics; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; +import com.codahale.metrics.*; +import com.codahale.metrics.Timer; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.io.sstable.format.SSTableReader; @@ -31,16 +32,16 @@ import org.apache.cassandra.utils.TopKSampler; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; -import com.yammer.metrics.core.Timer; -import com.yammer.metrics.util.RatioGauge; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + /** * Metrics for {@link ColumnFamilyStore}. */ public class ColumnFamilyMetrics { + /** Total amount of data stored in the memtable that resides on-heap, including column related overhead and overwritten rows. */ public final Gauge memtableOnHeapSize; /** Total amount of data stored in the memtable that resides off-heap, including column related overhead and overwritten rows. */ @@ -130,17 +131,13 @@ public class ColumnFamilyMetrics public final Timer coordinatorScanLatency; /** Time spent waiting for free memtable space, either on- or off-heap */ - public final Timer waitingOnFreeMemtableSpace; + public final Histogram waitingOnFreeMemtableSpace; private final MetricNameFactory factory; - private static final MetricNameFactory globalNameFactory = new AllColumnFamilyMetricNameFactory();; + private static final MetricNameFactory globalNameFactory = new AllColumnFamilyMetricNameFactory(); public final Counter speculativeRetries; - // for backward compatibility - @Deprecated public final EstimatedHistogram sstablesPerRead = new EstimatedHistogram(35); - @Deprecated public final EstimatedHistogram recentSSTablesPerRead = new EstimatedHistogram(35); - public final static LatencyMetrics globalReadLatency = new LatencyMetrics(globalNameFactory, "Read"); public final static LatencyMetrics globalWriteLatency = new LatencyMetrics(globalNameFactory, "Write"); public final static LatencyMetrics globalRangeLatency = new LatencyMetrics(globalNameFactory, "Range"); @@ -213,35 +210,35 @@ public class ColumnFamilyMetrics memtableColumnsCount = createColumnFamilyGauge("MemtableColumnsCount", new Gauge() { - public Long value() + public Long getValue() { return cfs.getDataTracker().getView().getCurrentMemtable().getOperations(); } }); memtableOnHeapSize = createColumnFamilyGauge("MemtableOnHeapSize", new Gauge() { - public Long value() + public Long getValue() { return cfs.getDataTracker().getView().getCurrentMemtable().getAllocator().onHeap().owns(); } }); memtableOffHeapSize = createColumnFamilyGauge("MemtableOffHeapSize", new Gauge() { - public Long value() + public Long getValue() { return cfs.getDataTracker().getView().getCurrentMemtable().getAllocator().offHeap().owns(); } }); memtableLiveDataSize = createColumnFamilyGauge("MemtableLiveDataSize", new Gauge() { - public Long value() + public Long getValue() { return cfs.getDataTracker().getView().getCurrentMemtable().getLiveDataSize(); } }); allMemtablesOnHeapSize = createColumnFamilyGauge("AllMemtablesHeapSize", new Gauge() { - public Long value() + public Long getValue() { long size = 0; for (ColumnFamilyStore cfs2 : cfs.concatWithIndexes()) @@ -251,7 +248,7 @@ public class ColumnFamilyMetrics }); allMemtablesOffHeapSize = createColumnFamilyGauge("AllMemtablesOffHeapSize", new Gauge() { - public Long value() + public Long getValue() { long size = 0; for (ColumnFamilyStore cfs2 : cfs.concatWithIndexes()) @@ -261,7 +258,7 @@ public class ColumnFamilyMetrics }); allMemtablesLiveDataSize = createColumnFamilyGauge("AllMemtablesLiveDataSize", new Gauge() { - public Long value() + public Long getValue() { long size = 0; for (ColumnFamilyStore cfs2 : cfs.concatWithIndexes()) @@ -270,9 +267,9 @@ public class ColumnFamilyMetrics } }); memtableSwitchCount = createColumnFamilyCounter("MemtableSwitchCount"); - estimatedRowSizeHistogram = Metrics.newGauge(factory.createMetricName("EstimatedRowSizeHistogram"), new Gauge() + estimatedRowSizeHistogram = Metrics.register(factory.createMetricName("EstimatedRowSizeHistogram"), new Gauge() { - public long[] value() + public long[] getValue() { return combineHistograms(cfs.getSSTables(), new GetHistogram() { @@ -283,9 +280,9 @@ public class ColumnFamilyMetrics }); } }); - estimatedColumnCountHistogram = Metrics.newGauge(factory.createMetricName("EstimatedColumnCountHistogram"), new Gauge() + estimatedColumnCountHistogram = Metrics.register(factory.createMetricName("EstimatedColumnCountHistogram"), new Gauge() { - public long[] value() + public long[] getValue() { return combineHistograms(cfs.getSSTables(), new GetHistogram() { @@ -299,7 +296,7 @@ public class ColumnFamilyMetrics sstablesPerReadHistogram = createColumnFamilyHistogram("SSTablesPerReadHistogram", cfs.keyspace.metric.sstablesPerReadHistogram); compressionRatio = createColumnFamilyGauge("CompressionRatio", new Gauge() { - public Double value() + public Double getValue() { double sum = 0; int total = 0; @@ -315,7 +312,7 @@ public class ColumnFamilyMetrics } }, new Gauge() // global gauge { - public Double value() + public Double getValue() { double sum = 0; int total = 0; @@ -339,14 +336,14 @@ public class ColumnFamilyMetrics pendingFlushes = createColumnFamilyCounter("PendingFlushes"); pendingCompactions = createColumnFamilyGauge("PendingCompactions", new Gauge() { - public Integer value() + public Integer getValue() { return cfs.getCompactionStrategy().getEstimatedRemainingTasks(); } }); liveSSTableCount = createColumnFamilyGauge("LiveSSTableCount", new Gauge() { - public Integer value() + public Integer getValue() { return cfs.getDataTracker().getSSTables().size(); } @@ -355,7 +352,7 @@ public class ColumnFamilyMetrics totalDiskSpaceUsed = createColumnFamilyCounter("TotalDiskSpaceUsed"); minRowSize = createColumnFamilyGauge("MinRowSize", new Gauge() { - public Long value() + public Long getValue() { long min = 0; for (SSTableReader sstable : cfs.getSSTables()) @@ -367,19 +364,19 @@ public class ColumnFamilyMetrics } }, new Gauge() // global gauge { - public Long value() + public Long getValue() { long min = Long.MAX_VALUE; for (Metric cfGauge : allColumnFamilyMetrics.get("MinRowSize")) { - min = Math.min(min, ((Gauge) cfGauge).value().longValue()); + min = Math.min(min, ((Gauge) cfGauge).getValue().longValue()); } return min; } }); maxRowSize = createColumnFamilyGauge("MaxRowSize", new Gauge() { - public Long value() + public Long getValue() { long max = 0; for (SSTableReader sstable : cfs.getSSTables()) @@ -391,19 +388,19 @@ public class ColumnFamilyMetrics } }, new Gauge() // global gauge { - public Long value() + public Long getValue() { long max = 0; for (Metric cfGauge : allColumnFamilyMetrics.get("MaxRowSize")) { - max = Math.max(max, ((Gauge) cfGauge).value().longValue()); + max = Math.max(max, ((Gauge) cfGauge).getValue().longValue()); } return max; } }); meanRowSize = createColumnFamilyGauge("MeanRowSize", new Gauge() { - public Long value() + public Long getValue() { long sum = 0; long count = 0; @@ -417,7 +414,7 @@ public class ColumnFamilyMetrics } }, new Gauge() // global gauge { - public Long value() + public Long getValue() { long sum = 0; long count = 0; @@ -435,7 +432,7 @@ public class ColumnFamilyMetrics }); bloomFilterFalsePositives = createColumnFamilyGauge("BloomFilterFalsePositives", new Gauge() { - public Long value() + public Long getValue() { long count = 0L; for (SSTableReader sstable: cfs.getSSTables()) @@ -445,7 +442,7 @@ public class ColumnFamilyMetrics }); recentBloomFilterFalsePositives = createColumnFamilyGauge("RecentBloomFilterFalsePositives", new Gauge() { - public Long value() + public Long getValue() { long count = 0L; for (SSTableReader sstable : cfs.getSSTables()) @@ -455,7 +452,7 @@ public class ColumnFamilyMetrics }); bloomFilterFalseRatio = createColumnFamilyGauge("BloomFilterFalseRatio", new Gauge() { - public Double value() + public Double getValue() { long falseCount = 0L; long trueCount = 0L; @@ -470,7 +467,7 @@ public class ColumnFamilyMetrics } }, new Gauge() // global gauge { - public Double value() + public Double getValue() { long falseCount = 0L; long trueCount = 0L; @@ -489,7 +486,7 @@ public class ColumnFamilyMetrics }); recentBloomFilterFalseRatio = createColumnFamilyGauge("RecentBloomFilterFalseRatio", new Gauge() { - public Double value() + public Double getValue() { long falseCount = 0L; long trueCount = 0L; @@ -504,7 +501,7 @@ public class ColumnFamilyMetrics } }, new Gauge() // global gauge { - public Double value() + public Double getValue() { long falseCount = 0L; long trueCount = 0L; @@ -523,7 +520,7 @@ public class ColumnFamilyMetrics }); bloomFilterDiskSpaceUsed = createColumnFamilyGauge("BloomFilterDiskSpaceUsed", new Gauge() { - public Long value() + public Long getValue() { long total = 0; for (SSTableReader sst : cfs.getSSTables()) @@ -533,7 +530,7 @@ public class ColumnFamilyMetrics }); bloomFilterOffHeapMemoryUsed = createColumnFamilyGauge("BloomFilterOffHeapMemoryUsed", new Gauge() { - public Long value() + public Long getValue() { long total = 0; for (SSTableReader sst : cfs.getSSTables()) @@ -543,7 +540,7 @@ public class ColumnFamilyMetrics }); indexSummaryOffHeapMemoryUsed = createColumnFamilyGauge("IndexSummaryOffHeapMemoryUsed", new Gauge() { - public Long value() + public Long getValue() { long total = 0; for (SSTableReader sst : cfs.getSSTables()) @@ -553,7 +550,7 @@ public class ColumnFamilyMetrics }); compressionMetadataOffHeapMemoryUsed = createColumnFamilyGauge("CompressionMetadataOffHeapMemoryUsed", new Gauge() { - public Long value() + public Long getValue() { long total = 0; for (SSTableReader sst : cfs.getSSTables()) @@ -562,8 +559,14 @@ public class ColumnFamilyMetrics } }); speculativeRetries = createColumnFamilyCounter("SpeculativeRetries"); - keyCacheHitRate = Metrics.newGauge(factory.createMetricName("KeyCacheHitRate"), new RatioGauge() + keyCacheHitRate = Metrics.register(factory.createMetricName("KeyCacheHitRate"), new RatioGauge() { + @Override + public Ratio getRatio() + { + return Ratio.of(getNumerator(), getDenominator()); + } + protected double getNumerator() { long hits = 0L; @@ -583,13 +586,13 @@ public class ColumnFamilyMetrics tombstoneScannedHistogram = createColumnFamilyHistogram("TombstoneScannedHistogram", cfs.keyspace.metric.tombstoneScannedHistogram); liveScannedHistogram = createColumnFamilyHistogram("LiveScannedHistogram", cfs.keyspace.metric.liveScannedHistogram); colUpdateTimeDeltaHistogram = createColumnFamilyHistogram("ColUpdateTimeDeltaHistogram", cfs.keyspace.metric.colUpdateTimeDeltaHistogram); - coordinatorReadLatency = Metrics.newTimer(factory.createMetricName("CoordinatorReadLatency"), TimeUnit.MICROSECONDS, TimeUnit.SECONDS); - coordinatorScanLatency = Metrics.newTimer(factory.createMetricName("CoordinatorScanLatency"), TimeUnit.MICROSECONDS, TimeUnit.SECONDS); - waitingOnFreeMemtableSpace = Metrics.newTimer(factory.createMetricName("WaitingOnFreeMemtableSpace"), TimeUnit.MICROSECONDS, TimeUnit.SECONDS); + coordinatorReadLatency = Metrics.timer(factory.createMetricName("CoordinatorReadLatency")); + coordinatorScanLatency = Metrics.timer(factory.createMetricName("CoordinatorScanLatency")); + waitingOnFreeMemtableSpace = Metrics.histogram(factory.createMetricName("WaitingOnFreeMemtableSpace")); trueSnapshotsSize = createColumnFamilyGauge("SnapshotsSize", new Gauge() { - public Long value() + public Long getValue() { return cfs.trueSnapshotsSize(); } @@ -606,8 +609,6 @@ public class ColumnFamilyMetrics public void updateSSTableIterated(int count) { sstablesPerReadHistogram.update(count); - recentSSTablesPerRead.add(count); - sstablesPerRead.add(count); } /** @@ -617,18 +618,18 @@ public class ColumnFamilyMetrics { for(String name : all) { - allColumnFamilyMetrics.get(name).remove(Metrics.defaultRegistry().allMetrics().get(factory.createMetricName(name))); - Metrics.defaultRegistry().removeMetric(factory.createMetricName(name)); + allColumnFamilyMetrics.get(name).remove(Metrics.getMetrics().get(factory.createMetricName(name))); + Metrics.remove(factory.createMetricName(name)); } readLatency.release(); writeLatency.release(); rangeLatency.release(); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("EstimatedRowSizeHistogram")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("EstimatedColumnCountHistogram")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("KeyCacheHitRate")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CoordinatorReadLatency")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CoordinatorScanLatency")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("WaitingOnFreeMemtableSpace")); + Metrics.remove(factory.createMetricName("EstimatedRowSizeHistogram")); + Metrics.remove(factory.createMetricName("EstimatedColumnCountHistogram")); + Metrics.remove(factory.createMetricName("KeyCacheHitRate")); + Metrics.remove(factory.createMetricName("CoordinatorReadLatency")); + Metrics.remove(factory.createMetricName("CoordinatorScanLatency")); + Metrics.remove(factory.createMetricName("WaitingOnFreeMemtableSpace")); } @@ -640,12 +641,12 @@ public class ColumnFamilyMetrics { return createColumnFamilyGauge(name, gauge, new Gauge() { - public Long value() + public Long getValue() { long total = 0; for (Metric cfGauge : allColumnFamilyMetrics.get(name)) { - total = total + ((Gauge) cfGauge).value().longValue(); + total = total + ((Gauge) cfGauge).getValue().longValue(); } return total; } @@ -658,10 +659,10 @@ public class ColumnFamilyMetrics */ protected Gauge createColumnFamilyGauge(String name, Gauge gauge, Gauge globalGauge) { - Gauge cfGauge = Metrics.newGauge(factory.createMetricName(name), gauge); + Gauge cfGauge = Metrics.register(factory.createMetricName(name), gauge); if (register(name, cfGauge)) { - Metrics.newGauge(globalNameFactory.createMetricName(name), globalGauge); + Metrics.register(globalNameFactory.createMetricName(name), globalGauge); } return cfGauge; } @@ -672,17 +673,17 @@ public class ColumnFamilyMetrics */ protected Counter createColumnFamilyCounter(final String name) { - Counter cfCounter = Metrics.newCounter(factory.createMetricName(name)); + Counter cfCounter = Metrics.counter(factory.createMetricName(name)); if (register(name, cfCounter)) { - Metrics.newGauge(globalNameFactory.createMetricName(name), new Gauge() + Metrics.register(globalNameFactory.createMetricName(name), new Gauge() { - public Long value() + public Long getValue() { long total = 0; for (Metric cfGauge : allColumnFamilyMetrics.get(name)) { - total += ((Counter) cfGauge).count(); + total += ((Counter) cfGauge).getCount(); } return total; } @@ -695,13 +696,13 @@ public class ColumnFamilyMetrics * Create a histogram-like interface that will register both a CF, keyspace and global level * histogram and forward any updates to both */ - protected ColumnFamilyHistogram createColumnFamilyHistogram(String name, Histogram keyspaceHistogram) + protected ColumnFamilyHistogram createColumnFamilyHistogram(String name, Histogram keyspaceHistogram) { - Histogram cfHistogram = Metrics.newHistogram(factory.createMetricName(name), true); + Histogram cfHistogram = Metrics.histogram(factory.createMetricName(name)); register(name, cfHistogram); - return new ColumnFamilyHistogram(cfHistogram, keyspaceHistogram, Metrics.newHistogram(globalNameFactory.createMetricName(name), true)); + return new ColumnFamilyHistogram(cfHistogram, keyspaceHistogram, Metrics.histogram(globalNameFactory.createMetricName(name))); } - + /** * Registers a metric to be removed when unloading CF. * @return true if first time metric with that name has been registered @@ -718,13 +719,13 @@ public class ColumnFamilyMetrics { public final Histogram[] all; public final Histogram cf; - private ColumnFamilyHistogram(Histogram cf, Histogram keyspace, Histogram global) + private ColumnFamilyHistogram(Histogram cf, Histogram keyspace, Histogram global) { this.cf = cf; this.all = new Histogram[]{cf, keyspace, global}; } - - public void update(long i) + + public void update(long i) { for(Histogram histo : all) { @@ -746,7 +747,7 @@ public class ColumnFamilyMetrics isIndex = cfs.isIndex(); } - public MetricName createMetricName(String metricName) + public CassandraMetricsRegistry.MetricName createMetricName(String metricName) { String groupName = ColumnFamilyMetrics.class.getPackage().getName(); String type = isIndex ? "IndexColumnFamily" : "ColumnFamily"; @@ -758,20 +759,20 @@ public class ColumnFamilyMetrics mbeanName.append(",scope=").append(columnFamilyName); mbeanName.append(",name=").append(metricName); - return new MetricName(groupName, type, metricName, keyspaceName + "." + columnFamilyName, mbeanName.toString()); + return new CassandraMetricsRegistry.MetricName(groupName, type, metricName, keyspaceName + "." + columnFamilyName, mbeanName.toString()); } } static class AllColumnFamilyMetricNameFactory implements MetricNameFactory { - public MetricName createMetricName(String metricName) + public CassandraMetricsRegistry.MetricName createMetricName(String metricName) { String groupName = ColumnFamilyMetrics.class.getPackage().getName(); StringBuilder mbeanName = new StringBuilder(); mbeanName.append(groupName).append(":"); mbeanName.append("type=ColumnFamily"); mbeanName.append(",name=").append(metricName); - return new MetricName(groupName, "ColumnFamily", metricName, "all", mbeanName.toString()); + return new CassandraMetricsRegistry.MetricName(groupName, "ColumnFamily", metricName, "all", mbeanName.toString()); } } diff --git a/src/java/org/apache/cassandra/metrics/CommitLogMetrics.java b/src/java/org/apache/cassandra/metrics/CommitLogMetrics.java index e9c0719970..670fa6bf3c 100644 --- a/src/java/org/apache/cassandra/metrics/CommitLogMetrics.java +++ b/src/java/org/apache/cassandra/metrics/CommitLogMetrics.java @@ -17,14 +17,13 @@ */ package org.apache.cassandra.metrics; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.core.Timer; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Timer; import org.apache.cassandra.db.commitlog.AbstractCommitLogService; import org.apache.cassandra.db.commitlog.CommitLogSegmentManager; -import java.util.concurrent.TimeUnit; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; /** * Metrics for commit log @@ -46,28 +45,28 @@ public class CommitLogMetrics public CommitLogMetrics(final AbstractCommitLogService service, final CommitLogSegmentManager allocator) { - completedTasks = Metrics.newGauge(factory.createMetricName("CompletedTasks"), new Gauge() + completedTasks = Metrics.register(factory.createMetricName("CompletedTasks"), new Gauge() { - public Long value() + public Long getValue() { return service.getCompletedTasks(); } }); - pendingTasks = Metrics.newGauge(factory.createMetricName("PendingTasks"), new Gauge() + pendingTasks = Metrics.register(factory.createMetricName("PendingTasks"), new Gauge() { - public Long value() + public Long getValue() { return service.getPendingTasks(); } }); - totalCommitLogSize = Metrics.newGauge(factory.createMetricName("TotalCommitLogSize"), new Gauge() + totalCommitLogSize = Metrics.register(factory.createMetricName("TotalCommitLogSize"), new Gauge() { - public Long value() + public Long getValue() { return allocator.bytesUsed(); } }); - waitingOnSegmentAllocation = Metrics.newTimer(factory.createMetricName("WaitingOnSegmentAllocation"), TimeUnit.MICROSECONDS, TimeUnit.SECONDS); - waitingOnCommit = Metrics.newTimer(factory.createMetricName("WaitingOnCommit"), TimeUnit.MICROSECONDS, TimeUnit.SECONDS); + waitingOnSegmentAllocation = Metrics.timer(factory.createMetricName("WaitingOnSegmentAllocation")); + waitingOnCommit = Metrics.timer(factory.createMetricName("WaitingOnCommit")); } } diff --git a/src/java/org/apache/cassandra/metrics/CompactionMetrics.java b/src/java/org/apache/cassandra/metrics/CompactionMetrics.java index b015130331..a62e3c4d2a 100644 --- a/src/java/org/apache/cassandra/metrics/CompactionMetrics.java +++ b/src/java/org/apache/cassandra/metrics/CompactionMetrics.java @@ -19,12 +19,10 @@ package org.apache.cassandra.metrics; import java.util.*; import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.core.Meter; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; import org.apache.cassandra.config.Schema; import org.apache.cassandra.db.ColumnFamilyStore; @@ -32,6 +30,8 @@ import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.compaction.CompactionInfo; import org.apache.cassandra.db.compaction.CompactionManager; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + /** * Metrics for compaction. */ @@ -53,9 +53,9 @@ public class CompactionMetrics implements CompactionManager.CompactionExecutorSt public CompactionMetrics(final ThreadPoolExecutor... collectors) { - pendingTasks = Metrics.newGauge(factory.createMetricName("PendingTasks"), new Gauge() + pendingTasks = Metrics.register(factory.createMetricName("PendingTasks"), new Gauge() { - public Integer value() + public Integer getValue() { int n = 0; for (String keyspaceName : Schema.instance.getKeyspaces()) @@ -68,9 +68,9 @@ public class CompactionMetrics implements CompactionManager.CompactionExecutorSt return n; } }); - completedTasks = Metrics.newGauge(factory.createMetricName("CompletedTasks"), new Gauge() + completedTasks = Metrics.register(factory.createMetricName("CompletedTasks"), new Gauge() { - public Long value() + public Long getValue() { long completedTasks = 0; for (ThreadPoolExecutor collector : collectors) @@ -78,8 +78,8 @@ public class CompactionMetrics implements CompactionManager.CompactionExecutorSt return completedTasks; } }); - totalCompactionsCompleted = Metrics.newMeter(factory.createMetricName("TotalCompactionsCompleted"), "compaction completed", TimeUnit.SECONDS); - bytesCompacted = Metrics.newCounter(factory.createMetricName("BytesCompacted")); + totalCompactionsCompleted = Metrics.meter(factory.createMetricName("TotalCompactionsCompleted")); + bytesCompacted = Metrics.counter(factory.createMetricName("BytesCompacted")); } public void beginCompaction(CompactionInfo.Holder ci) diff --git a/src/java/org/apache/cassandra/metrics/ConnectionMetrics.java b/src/java/org/apache/cassandra/metrics/ConnectionMetrics.java index eaf29f842a..60020b3966 100644 --- a/src/java/org/apache/cassandra/metrics/ConnectionMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ConnectionMetrics.java @@ -18,11 +18,12 @@ package org.apache.cassandra.metrics; import java.net.InetAddress; -import java.util.concurrent.TimeUnit; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.core.Meter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + import org.apache.cassandra.net.OutboundTcpConnectionPool; @@ -34,8 +35,7 @@ public class ConnectionMetrics public static final String TYPE_NAME = "Connection"; /** Total number of timeouts happened on this node */ - public static final Meter totalTimeouts = Metrics.newMeter(DefaultNameFactory.createMetricName(TYPE_NAME, "TotalTimeouts", null), "total timeouts", TimeUnit.SECONDS); - private static long recentTimeouts; + public static final Meter totalTimeouts = Metrics.meter(DefaultNameFactory.createMetricName(TYPE_NAME, "TotalTimeouts", null)); public final String address; /** Pending tasks for Command(Mutations, Read etc) TCP Connections */ @@ -53,8 +53,6 @@ public class ConnectionMetrics private final MetricNameFactory factory; - private long recentTimeoutCount; - /** * Create metrics for given connection pool. * @@ -68,69 +66,51 @@ public class ConnectionMetrics factory = new DefaultNameFactory("Connection", address); - commandPendingTasks = Metrics.newGauge(factory.createMetricName("CommandPendingTasks"), new Gauge() + commandPendingTasks = Metrics.register(factory.createMetricName("CommandPendingTasks"), new Gauge() { - public Integer value() + public Integer getValue() { return connectionPool.cmdCon.getPendingMessages(); } }); - commandCompletedTasks = Metrics.newGauge(factory.createMetricName("CommandCompletedTasks"), new Gauge() + commandCompletedTasks = Metrics.register(factory.createMetricName("CommandCompletedTasks"), new Gauge() { - public Long value() + public Long getValue() { return connectionPool.cmdCon.getCompletedMesssages(); } }); - commandDroppedTasks = Metrics.newGauge(factory.createMetricName("CommandDroppedTasks"), new Gauge() + commandDroppedTasks = Metrics.register(factory.createMetricName("CommandDroppedTasks"), new Gauge() { - public Long value() + public Long getValue() { return connectionPool.cmdCon.getDroppedMessages(); } }); - responsePendingTasks = Metrics.newGauge(factory.createMetricName("ResponsePendingTasks"), new Gauge() + responsePendingTasks = Metrics.register(factory.createMetricName("ResponsePendingTasks"), new Gauge() { - public Integer value() + public Integer getValue() { return connectionPool.ackCon.getPendingMessages(); } }); - responseCompletedTasks = Metrics.newGauge(factory.createMetricName("ResponseCompletedTasks"), new Gauge() + responseCompletedTasks = Metrics.register(factory.createMetricName("ResponseCompletedTasks"), new Gauge() { - public Long value() + public Long getValue() { return connectionPool.ackCon.getCompletedMesssages(); } }); - timeouts = Metrics.newMeter(factory.createMetricName("Timeouts"), "timeouts", TimeUnit.SECONDS); + timeouts = Metrics.meter(factory.createMetricName("Timeouts")); } public void release() { - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CommandPendingTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CommandCompletedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CommandDroppedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("ResponsePendingTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("ResponseCompletedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("Timeouts")); - } - - @Deprecated - public static long getRecentTotalTimeout() - { - long total = totalTimeouts.count(); - long recent = total - recentTimeouts; - recentTimeouts = total; - return recent; - } - - @Deprecated - public long getRecentTimeout() - { - long timeoutCount = timeouts.count(); - long recent = timeoutCount - recentTimeoutCount; - recentTimeoutCount = timeoutCount; - return recent; + Metrics.remove(factory.createMetricName("CommandPendingTasks")); + Metrics.remove(factory.createMetricName("CommandCompletedTasks")); + Metrics.remove(factory.createMetricName("CommandDroppedTasks")); + Metrics.remove(factory.createMetricName("ResponsePendingTasks")); + Metrics.remove(factory.createMetricName("ResponseCompletedTasks")); + Metrics.remove(factory.createMetricName("Timeouts")); } } diff --git a/src/java/org/apache/cassandra/metrics/DefaultNameFactory.java b/src/java/org/apache/cassandra/metrics/DefaultNameFactory.java index f7f23ee58d..db51906cfd 100644 --- a/src/java/org/apache/cassandra/metrics/DefaultNameFactory.java +++ b/src/java/org/apache/cassandra/metrics/DefaultNameFactory.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.metrics; -import com.yammer.metrics.core.MetricName; /** * MetricNameFactory that generates default MetricName of metrics. @@ -40,14 +39,14 @@ public class DefaultNameFactory implements MetricNameFactory this.scope = scope; } - public MetricName createMetricName(String metricName) + public CassandraMetricsRegistry.MetricName createMetricName(String metricName) { return createMetricName(type, metricName, scope); } - public static MetricName createMetricName(String type, String metricName, String scope) + public static CassandraMetricsRegistry.MetricName createMetricName(String type, String metricName, String scope) { - return new MetricName(GROUP_NAME, type, metricName, scope, createDefaultMBeanName(type, metricName, scope)); + return new CassandraMetricsRegistry.MetricName(GROUP_NAME, type, metricName, scope, createDefaultMBeanName(type, metricName, scope)); } protected static String createDefaultMBeanName(String type, String name, String scope) diff --git a/src/java/org/apache/cassandra/metrics/DroppedMessageMetrics.java b/src/java/org/apache/cassandra/metrics/DroppedMessageMetrics.java index f94ea04d6e..58c80fb6c2 100644 --- a/src/java/org/apache/cassandra/metrics/DroppedMessageMetrics.java +++ b/src/java/org/apache/cassandra/metrics/DroppedMessageMetrics.java @@ -17,13 +17,11 @@ */ package org.apache.cassandra.metrics; -import java.util.concurrent.TimeUnit; - -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Meter; - +import com.codahale.metrics.Meter; import org.apache.cassandra.net.MessagingService; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + /** * Metrics for dropped messages by verb. */ @@ -32,20 +30,9 @@ public class DroppedMessageMetrics /** Number of dropped messages */ public final Meter dropped; - private long lastDropped = 0; - public DroppedMessageMetrics(MessagingService.Verb verb) { MetricNameFactory factory = new DefaultNameFactory("DroppedMessage", verb.toString()); - dropped = Metrics.newMeter(factory.createMetricName("Dropped"), "dropped", TimeUnit.SECONDS); - } - - @Deprecated - public int getRecentlyDropped() - { - long currentDropped = dropped.count(); - long recentlyDropped = currentDropped - lastDropped; - lastDropped = currentDropped; - return (int)recentlyDropped; + dropped = Metrics.meter(factory.createMetricName("Dropped")); } } diff --git a/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java b/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java new file mode 100644 index 0000000000..221f2e171a --- /dev/null +++ b/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java @@ -0,0 +1,105 @@ +/* + * 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 com.google.common.annotations.VisibleForTesting; + +import com.codahale.metrics.Reservoir; +import com.codahale.metrics.Snapshot; +import com.codahale.metrics.UniformSnapshot; +import org.apache.cassandra.utils.EstimatedHistogram; + +/** + * Allows our Histogram implementation to be used by the metrics library. + * + * Default buckets allows nanosecond timings. + */ +public class EstimatedHistogramReservoir implements Reservoir +{ + EstimatedHistogram histogram; + + public EstimatedHistogramReservoir() + { + this(128); + } + + public EstimatedHistogramReservoir(int numBuckets) + { + histogram = new EstimatedHistogram(numBuckets); + } + + @Override + public int size() + { + return histogram.getBucketOffsets().length + 1; + } + + @Override + public void update(long value) + { + histogram.add(value); + } + + @Override + public Snapshot getSnapshot() + { + return new HistogramSnapshot(histogram); + } + + @VisibleForTesting + public void clear() + { + histogram.getBuckets(true); + } + + class HistogramSnapshot extends UniformSnapshot + { + EstimatedHistogram histogram; + + public HistogramSnapshot(EstimatedHistogram histogram) + { + super(histogram.getBuckets(false)); + + this.histogram = histogram; + } + + @Override + public double getValue(double quantile) + { + return histogram.percentile(quantile); + } + + @Override + public long getMax() + { + return histogram.max(); + } + + @Override + public long getMin() + { + return histogram.min(); + } + + @Override + public double getMean() + { + return histogram.mean(); + } + } +} diff --git a/src/java/org/apache/cassandra/metrics/FileCacheMetrics.java b/src/java/org/apache/cassandra/metrics/FileCacheMetrics.java index 5f6bd20136..c240c0389b 100644 --- a/src/java/org/apache/cassandra/metrics/FileCacheMetrics.java +++ b/src/java/org/apache/cassandra/metrics/FileCacheMetrics.java @@ -17,14 +17,14 @@ */ package org.apache.cassandra.metrics; -import java.util.concurrent.TimeUnit; - -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Gauge; -import com.yammer.metrics.core.Meter; -import com.yammer.metrics.util.RatioGauge; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; +import com.codahale.metrics.RatioGauge; import org.apache.cassandra.service.FileCacheService; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + + public class FileCacheMetrics { private static final MetricNameFactory factory = new DefaultNameFactory("FileCache"); @@ -40,23 +40,19 @@ public class FileCacheMetrics public FileCacheMetrics() { - hits = Metrics.newMeter(factory.createMetricName("Hits"), "hits", TimeUnit.SECONDS); - requests = Metrics.newMeter(factory.createMetricName("Requests"), "requests", TimeUnit.SECONDS); - hitRate = Metrics.newGauge(factory.createMetricName("HitRate"), new RatioGauge() + hits = Metrics.meter(factory.createMetricName("Hits")); + requests = Metrics.meter(factory.createMetricName("Requests")); + hitRate = Metrics.register(factory.createMetricName("HitRate"), new RatioGauge() { - protected double getNumerator() + @Override + public Ratio getRatio() { - return hits.count(); - } - - protected double getDenominator() - { - return requests.count(); + return Ratio.of(hits.getCount(), requests.getCount()); } }); - size = Metrics.newGauge(factory.createMetricName("Size"), new Gauge() + size = Metrics.register(factory.createMetricName("Size"), new Gauge() { - public Long value() + public Long getValue() { return FileCacheService.instance.sizeInBytes(); } diff --git a/src/java/org/apache/cassandra/metrics/HintedHandoffMetrics.java b/src/java/org/apache/cassandra/metrics/HintedHandoffMetrics.java index d002e399ff..fef8c1ffe4 100644 --- a/src/java/org/apache/cassandra/metrics/HintedHandoffMetrics.java +++ b/src/java/org/apache/cassandra/metrics/HintedHandoffMetrics.java @@ -20,6 +20,7 @@ package org.apache.cassandra.metrics; import java.net.InetAddress; import java.util.Map.Entry; +import com.codahale.metrics.Counter; import org.apache.cassandra.db.HintedHandOffManager; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.utils.UUIDGen; @@ -29,8 +30,8 @@ import org.slf4j.LoggerFactory; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; /** * Metrics for {@link HintedHandOffManager}. @@ -39,7 +40,7 @@ public class HintedHandoffMetrics { private static final Logger logger = LoggerFactory.getLogger(HintedHandoffMetrics.class); - private final MetricNameFactory factory = new DefaultNameFactory("HintedHandOffManager"); + private static final MetricNameFactory factory = new DefaultNameFactory("HintedHandOffManager"); /** Total number of hints which are not stored, This is not a cache. */ private final LoadingCache notStored = CacheBuilder.newBuilder().build(new CacheLoader() @@ -55,7 +56,7 @@ public class HintedHandoffMetrics { public Counter load(InetAddress address) { - return Metrics.newCounter(factory.createMetricName("Hints_created-" + address.getHostAddress())); + return Metrics.counter(factory.createMetricName("Hints_created-" + address.getHostAddress())); } }); @@ -88,12 +89,12 @@ public class HintedHandoffMetrics public DifferencingCounter(InetAddress address) { - this.meter = Metrics.newCounter(factory.createMetricName("Hints_not_stored-" + address.getHostAddress())); + this.meter = Metrics.counter(factory.createMetricName("Hints_not_stored-" + address.getHostAddress())); } public long difference() { - long current = meter.count(); + long current = meter.getCount(); long difference = current - reported; this.reported = current; return difference; @@ -101,7 +102,7 @@ public class HintedHandoffMetrics public long count() { - return meter.count(); + return meter.getCount(); } public void mark() diff --git a/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java b/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java index fa6f33b277..0bac4212c6 100644 --- a/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java +++ b/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java @@ -19,13 +19,17 @@ package org.apache.cassandra.metrics; import java.util.Set; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.MetricRegistry; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + /** * Metrics for {@link ColumnFamilyStore}. @@ -104,112 +108,112 @@ public class KeyspaceMetrics { public Long getValue(ColumnFamilyMetrics metric) { - return metric.memtableColumnsCount.value(); + return metric.memtableColumnsCount.getValue(); } }); memtableLiveDataSize = createKeyspaceGauge("MemtableLiveDataSize", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.memtableLiveDataSize.value(); + return metric.memtableLiveDataSize.getValue(); } }); memtableOnHeapDataSize = createKeyspaceGauge("MemtableOnHeapDataSize", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.memtableOnHeapSize.value(); + return metric.memtableOnHeapSize.getValue(); } }); memtableOffHeapDataSize = createKeyspaceGauge("MemtableOffHeapDataSize", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.memtableOffHeapSize.value(); + return metric.memtableOffHeapSize.getValue(); } }); allMemtablesLiveDataSize = createKeyspaceGauge("AllMemtablesLiveDataSize", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.allMemtablesLiveDataSize.value(); + return metric.allMemtablesLiveDataSize.getValue(); } }); allMemtablesOnHeapDataSize = createKeyspaceGauge("AllMemtablesOnHeapDataSize", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.allMemtablesOnHeapSize.value(); + return metric.allMemtablesOnHeapSize.getValue(); } }); allMemtablesOffHeapDataSize = createKeyspaceGauge("AllMemtablesOffHeapDataSize", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.allMemtablesOffHeapSize.value(); + return metric.allMemtablesOffHeapSize.getValue(); } }); memtableSwitchCount = createKeyspaceGauge("MemtableSwitchCount", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.memtableSwitchCount.count(); + return metric.memtableSwitchCount.getCount(); } }); pendingCompactions = createKeyspaceGauge("PendingCompactions", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return (long) metric.pendingCompactions.value(); + return (long) metric.pendingCompactions.getValue(); } }); pendingFlushes = createKeyspaceGauge("PendingFlushes", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return (long) metric.pendingFlushes.count(); + return (long) metric.pendingFlushes.getCount(); } }); liveDiskSpaceUsed = createKeyspaceGauge("LiveDiskSpaceUsed", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.liveDiskSpaceUsed.count(); + return metric.liveDiskSpaceUsed.getCount(); } }); totalDiskSpaceUsed = createKeyspaceGauge("TotalDiskSpaceUsed", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.totalDiskSpaceUsed.count(); + return metric.totalDiskSpaceUsed.getCount(); } }); bloomFilterDiskSpaceUsed = createKeyspaceGauge("BloomFilterDiskSpaceUsed", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.bloomFilterDiskSpaceUsed.value(); + return metric.bloomFilterDiskSpaceUsed.getValue(); } }); bloomFilterOffHeapMemoryUsed = createKeyspaceGauge("BloomFilterOffHeapMemoryUsed", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.bloomFilterOffHeapMemoryUsed.value(); + return metric.bloomFilterOffHeapMemoryUsed.getValue(); } }); indexSummaryOffHeapMemoryUsed = createKeyspaceGauge("IndexSummaryOffHeapMemoryUsed", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.indexSummaryOffHeapMemoryUsed.value(); + return metric.indexSummaryOffHeapMemoryUsed.getValue(); } }); compressionMetadataOffHeapMemoryUsed = createKeyspaceGauge("CompressionMetadataOffHeapMemoryUsed", new MetricValue() { public Long getValue(ColumnFamilyMetrics metric) { - return metric.compressionMetadataOffHeapMemoryUsed.value(); + return metric.compressionMetadataOffHeapMemoryUsed.getValue(); } }); // latency metrics for ColumnFamilyMetrics to update @@ -217,10 +221,10 @@ public class KeyspaceMetrics writeLatency = new LatencyMetrics(factory, "Write"); rangeLatency = new LatencyMetrics(factory, "Range"); // create histograms for ColumnFamilyMetrics to replicate updates to - sstablesPerReadHistogram = Metrics.newHistogram(factory.createMetricName("SSTablesPerReadHistogram"), true); - tombstoneScannedHistogram = Metrics.newHistogram(factory.createMetricName("TombstoneScannedHistogram"), true); - liveScannedHistogram = Metrics.newHistogram(factory.createMetricName("LiveScannedHistogram"), true); - colUpdateTimeDeltaHistogram = Metrics.newHistogram(factory.createMetricName("ColUpdateTimeDeltaHistogram"), true); + sstablesPerReadHistogram = Metrics.histogram(factory.createMetricName("SSTablesPerReadHistogram")); + tombstoneScannedHistogram = Metrics.histogram(factory.createMetricName("TombstoneScannedHistogram")); + liveScannedHistogram = Metrics.histogram(factory.createMetricName("LiveScannedHistogram")); + colUpdateTimeDeltaHistogram = Metrics.histogram(factory.createMetricName("ColUpdateTimeDeltaHistogram")); // add manually since histograms do not use createKeyspaceGauge method allMetrics.addAll(Lists.newArrayList("SSTablesPerReadHistogram", "TombstoneScannedHistogram", "LiveScannedHistogram")); @@ -236,7 +240,7 @@ public class KeyspaceMetrics { for(String name : allMetrics) { - Metrics.defaultRegistry().removeMetric(factory.createMetricName(name)); + Metrics.remove(factory.createMetricName(name)); } // latency metrics contain multiple metrics internally and need to be released manually readLatency.release(); @@ -251,7 +255,7 @@ public class KeyspaceMetrics { /** * get value of a metric - * @param columnfamilymetrics of a column family in this keyspace + * @param metric of a column family in this keyspace * @return current value of a metric */ public Long getValue(ColumnFamilyMetrics metric); @@ -260,15 +264,15 @@ public class KeyspaceMetrics /** * Creates a gauge that will sum the current value of a metric for all column families in this keyspace * @param name - * @param MetricValue + * @param extractor * @return Gauge>Long> that computes sum of MetricValue.getValue() */ private Gauge createKeyspaceGauge(String name, final MetricValue extractor) { allMetrics.add(name); - return Metrics.newGauge(factory.createMetricName(name), new Gauge() + return Metrics.register(factory.createMetricName(name), new Gauge() { - public Long value() + public Long getValue() { long sum = 0; for (ColumnFamilyStore cf : keyspace.getColumnFamilyStores()) @@ -289,7 +293,7 @@ public class KeyspaceMetrics this.keyspaceName = ks.getName(); } - public MetricName createMetricName(String metricName) + public CassandraMetricsRegistry.MetricName createMetricName(String metricName) { String groupName = ColumnFamilyMetrics.class.getPackage().getName(); @@ -299,7 +303,7 @@ public class KeyspaceMetrics mbeanName.append(",keyspace=").append(keyspaceName); mbeanName.append(",name=").append(metricName); - return new MetricName(groupName, "keyspace", metricName, keyspaceName, mbeanName.toString()); + return new CassandraMetricsRegistry.MetricName(groupName, "keyspace", metricName, keyspaceName, mbeanName.toString()); } } } diff --git a/src/java/org/apache/cassandra/metrics/LatencyMetrics.java b/src/java/org/apache/cassandra/metrics/LatencyMetrics.java index 149af917fb..ced56a84f4 100644 --- a/src/java/org/apache/cassandra/metrics/LatencyMetrics.java +++ b/src/java/org/apache/cassandra/metrics/LatencyMetrics.java @@ -20,13 +20,14 @@ package org.apache.cassandra.metrics; import java.util.List; import java.util.concurrent.TimeUnit; -import org.apache.cassandra.utils.EstimatedHistogram; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Timer; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; -import com.yammer.metrics.core.Timer; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + /** * Metrics about latencies @@ -44,11 +45,6 @@ public class LatencyMetrics protected final MetricNameFactory factory; protected final String namePrefix; - @Deprecated public final EstimatedHistogram totalLatencyHistogram = new EstimatedHistogram(); - @Deprecated public final EstimatedHistogram recentLatencyHistogram = new EstimatedHistogram(); - protected long lastLatency; - protected long lastOpCount; - /** * Create LatencyMetrics with given group, type, and scope. Name prefix for each metric will be empty. * @@ -83,8 +79,8 @@ public class LatencyMetrics this.factory = factory; this.namePrefix = namePrefix; - latency = Metrics.newTimer(factory.createMetricName(namePrefix + "Latency"), TimeUnit.MICROSECONDS, TimeUnit.SECONDS); - totalLatency = Metrics.newCounter(factory.createMetricName(namePrefix + "TotalLatency")); + latency = Metrics.timer(factory.createMetricName(namePrefix + "Latency")); + totalLatency = Metrics.counter(factory.createMetricName(namePrefix + "TotalLatency")); } /** @@ -106,9 +102,7 @@ public class LatencyMetrics { // convert to microseconds. 1 millionth latency.update(nanos, TimeUnit.NANOSECONDS); - totalLatency.inc(nanos / 1000); - totalLatencyHistogram.add(nanos / 1000); - recentLatencyHistogram.add(nanos / 1000); + totalLatency.inc(nanos); for(LatencyMetrics parent : parents) { parent.addNano(nanos); @@ -117,25 +111,7 @@ public class LatencyMetrics public void release() { - Metrics.defaultRegistry().removeMetric(factory.createMetricName(namePrefix + "Latency")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName(namePrefix + "TotalLatency")); - } - - @Deprecated - public synchronized double getRecentLatency() - { - long ops = latency.count(); - long n = totalLatency.count(); - if (ops == lastOpCount) - return 0; - try - { - return ((double) n - lastLatency) / (ops - lastOpCount); - } - finally - { - lastLatency = n; - lastOpCount = ops; - } + Metrics.remove(factory.createMetricName(namePrefix + "Latency")); + Metrics.remove(factory.createMetricName(namePrefix + "TotalLatency")); } } diff --git a/src/java/org/apache/cassandra/metrics/MetricNameFactory.java b/src/java/org/apache/cassandra/metrics/MetricNameFactory.java index 78f8b14d0e..4e8f2b45ac 100644 --- a/src/java/org/apache/cassandra/metrics/MetricNameFactory.java +++ b/src/java/org/apache/cassandra/metrics/MetricNameFactory.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.metrics; -import com.yammer.metrics.core.MetricName; public interface MetricNameFactory { @@ -27,5 +26,5 @@ public interface MetricNameFactory * @param metricName part of qualified name. * @return new String with given metric name. */ - MetricName createMetricName(String metricName); + CassandraMetricsRegistry.MetricName createMetricName(String metricName); } diff --git a/src/java/org/apache/cassandra/metrics/ReadRepairMetrics.java b/src/java/org/apache/cassandra/metrics/ReadRepairMetrics.java index f9b1a40ab7..9ee1c6073c 100644 --- a/src/java/org/apache/cassandra/metrics/ReadRepairMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ReadRepairMetrics.java @@ -17,10 +17,9 @@ */ package org.apache.cassandra.metrics; -import java.util.concurrent.TimeUnit; +import com.codahale.metrics.Meter; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Meter; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; /** * Metrics related to Read Repair. @@ -29,10 +28,7 @@ public class ReadRepairMetrics { private static final MetricNameFactory factory = new DefaultNameFactory("ReadRepair"); - public static final Meter repairedBlocking = - Metrics.newMeter(factory.createMetricName("RepairedBlocking"), "RepairedBlocking", TimeUnit.SECONDS); - public static final Meter repairedBackground = - Metrics.newMeter(factory.createMetricName("RepairedBackground"), "RepairedBackground", TimeUnit.SECONDS); - public static final Meter attempted = - Metrics.newMeter(factory.createMetricName("Attempted"), "Attempted", TimeUnit.SECONDS); + public static final Meter repairedBlocking = Metrics.meter(factory.createMetricName("RepairedBlocking")); + public static final Meter repairedBackground = Metrics.meter(factory.createMetricName("RepairedBackground")); + public static final Meter attempted = Metrics.meter(factory.createMetricName("Attempted")); } diff --git a/src/java/org/apache/cassandra/metrics/RestorableMeter.java b/src/java/org/apache/cassandra/metrics/RestorableMeter.java index c421618059..b543665904 100644 --- a/src/java/org/apache/cassandra/metrics/RestorableMeter.java +++ b/src/java/org/apache/cassandra/metrics/RestorableMeter.java @@ -18,12 +18,12 @@ */ package org.apache.cassandra.metrics; -import com.yammer.metrics.core.Clock; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import static java.lang.Math.exp; +import com.codahale.metrics.Clock; /** * A meter metric which measures mean throughput as well as fifteen-minute and two-hour @@ -52,7 +52,7 @@ public class RestorableMeter public RestorableMeter() { this.m15Rate = new RestorableEWMA(TimeUnit.MINUTES.toSeconds(15)); this.m120Rate = new RestorableEWMA(TimeUnit.MINUTES.toSeconds(120)); - this.startTime = this.clock.tick(); + this.startTime = this.clock.getTick(); this.lastTick = new AtomicLong(startTime); } @@ -64,7 +64,7 @@ public class RestorableMeter public RestorableMeter(double lastM15Rate, double lastM120Rate) { this.m15Rate = new RestorableEWMA(lastM15Rate, TimeUnit.MINUTES.toSeconds(15)); this.m120Rate = new RestorableEWMA(lastM120Rate, TimeUnit.MINUTES.toSeconds(120)); - this.startTime = this.clock.tick(); + this.startTime = this.clock.getTick(); this.lastTick = new AtomicLong(startTime); } @@ -73,7 +73,7 @@ public class RestorableMeter */ private void tickIfNecessary() { final long oldTick = lastTick.get(); - final long newTick = clock.tick(); + final long newTick = clock.getTick(); final long age = newTick - oldTick; if (age > TICK_INTERVAL) { final long newIntervalStartTick = newTick - age % TICK_INTERVAL; @@ -139,7 +139,7 @@ public class RestorableMeter if (count() == 0) { return 0.0; } else { - final long elapsed = (clock.tick() - startTime); + final long elapsed = (clock.getTick() - startTime); return (count() / (double) elapsed) * NANOS_PER_SECOND; } } diff --git a/src/java/org/apache/cassandra/metrics/SEPMetrics.java b/src/java/org/apache/cassandra/metrics/SEPMetrics.java index 58fe9c5b91..35f02b493a 100644 --- a/src/java/org/apache/cassandra/metrics/SEPMetrics.java +++ b/src/java/org/apache/cassandra/metrics/SEPMetrics.java @@ -17,22 +17,24 @@ */ package org.apache.cassandra.metrics; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Gauge; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; import org.apache.cassandra.concurrent.SEPExecutor; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + public class SEPMetrics { /** Number of active tasks. */ public final Gauge activeTasks; /** Number of tasks that had blocked before being accepted (or rejected). */ - public final Gauge totalBlocked; + public final Counter totalBlocked; /** * Number of tasks currently blocked, waiting to be accepted by * the executor (because all threads are busy and the backing queue is full). */ - public final Gauge currentBlocked; + public final Counter currentBlocked; /** Number of completed tasks. */ public final Gauge completedTasks; /** Number of tasks waiting to be executed. */ @@ -52,44 +54,33 @@ public class SEPMetrics public SEPMetrics(final SEPExecutor executor, String path, String poolName) { this.factory = new ThreadPoolMetricNameFactory("ThreadPools", path, poolName); - activeTasks = Metrics.newGauge(factory.createMetricName("ActiveTasks"), new Gauge() + activeTasks = Metrics.register(factory.createMetricName("ActiveTasks"), new Gauge() { - public Integer value() + public Integer getValue() { return executor.getActiveCount(); } }); - pendingTasks = Metrics.newGauge(factory.createMetricName("PendingTasks"), new Gauge() + pendingTasks = Metrics.register(factory.createMetricName("PendingTasks"), new Gauge() { - public Long value() + public Long getValue() { return executor.getPendingTasks(); } }); - totalBlocked = Metrics.newGauge(factory.createMetricName("TotalBlockedTasks"), new Gauge() + totalBlocked = Metrics.counter(factory.createMetricName("TotalBlockedTasks")); + currentBlocked = Metrics.counter(factory.createMetricName("CurrentlyBlockedTasks")); + + completedTasks = Metrics.register(factory.createMetricName("CompletedTasks"), new Gauge() { - public Integer value() - { - return executor.getTotalBlockedTasks(); - } - }); - currentBlocked = Metrics.newGauge(factory.createMetricName("CurrentlyBlockedTasks"), new Gauge() - { - public Long value() - { - return (long) executor.getCurrentlyBlockedTasks(); - } - }); - completedTasks = Metrics.newGauge(factory.createMetricName("CompletedTasks"), new Gauge() - { - public Long value() + public Long getValue() { return executor.getCompletedTasks(); } }); - maxPoolSize = Metrics.newGauge(factory.createMetricName("MaxPoolSize"), new Gauge() + maxPoolSize = Metrics.register(factory.createMetricName("MaxPoolSize"), new Gauge() { - public Integer value() + public Integer getValue() { return executor.maxWorkers; } @@ -98,11 +89,11 @@ public class SEPMetrics public void release() { - Metrics.defaultRegistry().removeMetric(factory.createMetricName("ActiveTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("PendingTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CompletedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("TotalBlockedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CurrentlyBlockedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("MaxPoolSize")); + Metrics.remove(factory.createMetricName("ActiveTasks")); + Metrics.remove(factory.createMetricName("PendingTasks")); + Metrics.remove(factory.createMetricName("CompletedTasks")); + Metrics.remove(factory.createMetricName("TotalBlockedTasks")); + Metrics.remove(factory.createMetricName("CurrentlyBlockedTasks")); + Metrics.remove(factory.createMetricName("MaxPoolSize")); } } diff --git a/src/java/org/apache/cassandra/metrics/StorageMetrics.java b/src/java/org/apache/cassandra/metrics/StorageMetrics.java index 8ee90bbcc8..12196f70e8 100644 --- a/src/java/org/apache/cassandra/metrics/StorageMetrics.java +++ b/src/java/org/apache/cassandra/metrics/StorageMetrics.java @@ -17,8 +17,9 @@ */ package org.apache.cassandra.metrics; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; +import com.codahale.metrics.Counter; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; /** * Metrics related to Storage. @@ -27,8 +28,8 @@ public class StorageMetrics { private static final MetricNameFactory factory = new DefaultNameFactory("Storage"); - public static final Counter load = Metrics.newCounter(factory.createMetricName("Load")); - public static final Counter exceptions = Metrics.newCounter(factory.createMetricName("Exceptions")); - public static final Counter totalHintsInProgress = Metrics.newCounter(factory.createMetricName("TotalHintsInProgress")); - public static final Counter totalHints = Metrics.newCounter(factory.createMetricName("TotalHints")); + public static final Counter load = Metrics.counter(factory.createMetricName("Load")); + public static final Counter exceptions = Metrics.counter(factory.createMetricName("Exceptions")); + public static final Counter totalHintsInProgress = Metrics.counter(factory.createMetricName("TotalHintsInProgress")); + public static final Counter totalHints = Metrics.counter(factory.createMetricName("TotalHints")); } diff --git a/src/java/org/apache/cassandra/metrics/StreamingMetrics.java b/src/java/org/apache/cassandra/metrics/StreamingMetrics.java index 51b4f8c972..d00513c4e0 100644 --- a/src/java/org/apache/cassandra/metrics/StreamingMetrics.java +++ b/src/java/org/apache/cassandra/metrics/StreamingMetrics.java @@ -20,10 +20,12 @@ package org.apache.cassandra.metrics; import java.net.InetAddress; import java.util.concurrent.ConcurrentMap; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Counter; + +import com.codahale.metrics.Counter; import org.cliffc.high_scale_lib.NonBlockingHashMap; +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + /** * Metrics for streaming. */ @@ -33,9 +35,9 @@ public class StreamingMetrics private static final ConcurrentMap instances = new NonBlockingHashMap(); - public static final Counter activeStreamsOutbound = Metrics.newCounter(DefaultNameFactory.createMetricName(TYPE_NAME, "ActiveOutboundStreams", null)); - public static final Counter totalIncomingBytes = Metrics.newCounter(DefaultNameFactory.createMetricName(TYPE_NAME, "TotalIncomingBytes", null)); - public static final Counter totalOutgoingBytes = Metrics.newCounter(DefaultNameFactory.createMetricName(TYPE_NAME, "TotalOutgoingBytes", null)); + public static final Counter activeStreamsOutbound = Metrics.counter(DefaultNameFactory.createMetricName(TYPE_NAME, "ActiveOutboundStreams", null)); + public static final Counter totalIncomingBytes = Metrics.counter(DefaultNameFactory.createMetricName(TYPE_NAME, "TotalIncomingBytes", null)); + public static final Counter totalOutgoingBytes = Metrics.counter(DefaultNameFactory.createMetricName(TYPE_NAME, "TotalOutgoingBytes", null)); public final Counter incomingBytes; public final Counter outgoingBytes; @@ -53,7 +55,7 @@ public class StreamingMetrics public StreamingMetrics(final InetAddress peer) { MetricNameFactory factory = new DefaultNameFactory("Streaming", peer.getHostAddress().replaceAll(":", ".")); - incomingBytes = Metrics.newCounter(factory.createMetricName("IncomingBytes")); - outgoingBytes= Metrics.newCounter(factory.createMetricName("OutgoingBytes")); + incomingBytes = Metrics.counter(factory.createMetricName("IncomingBytes")); + outgoingBytes= Metrics.counter(factory.createMetricName("OutgoingBytes")); } } diff --git a/src/java/org/apache/cassandra/metrics/ThreadPoolMetricNameFactory.java b/src/java/org/apache/cassandra/metrics/ThreadPoolMetricNameFactory.java index 4afc4d395a..781010872c 100644 --- a/src/java/org/apache/cassandra/metrics/ThreadPoolMetricNameFactory.java +++ b/src/java/org/apache/cassandra/metrics/ThreadPoolMetricNameFactory.java @@ -17,8 +17,6 @@ */ package org.apache.cassandra.metrics; -import com.yammer.metrics.core.MetricName; - class ThreadPoolMetricNameFactory implements MetricNameFactory { private final String type; @@ -32,7 +30,7 @@ class ThreadPoolMetricNameFactory implements MetricNameFactory this.poolName = poolName; } - public MetricName createMetricName(String metricName) + public CassandraMetricsRegistry.MetricName createMetricName(String metricName) { String groupName = ThreadPoolMetrics.class.getPackage().getName(); StringBuilder mbeanName = new StringBuilder(); @@ -42,6 +40,6 @@ class ThreadPoolMetricNameFactory implements MetricNameFactory mbeanName.append(",scope=").append(poolName); mbeanName.append(",name=").append(metricName); - return new MetricName(groupName, type, metricName, path + "." + poolName, mbeanName.toString()); + return new CassandraMetricsRegistry.MetricName(groupName, type, metricName, path + "." + poolName, mbeanName.toString()); } } diff --git a/src/java/org/apache/cassandra/metrics/ThreadPoolMetrics.java b/src/java/org/apache/cassandra/metrics/ThreadPoolMetrics.java index a5e6dafbb3..992e676c7f 100644 --- a/src/java/org/apache/cassandra/metrics/ThreadPoolMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ThreadPoolMetrics.java @@ -19,8 +19,16 @@ package org.apache.cassandra.metrics; import java.util.concurrent.ThreadPoolExecutor; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.*; +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.JmxReporter; + +import javax.management.JMX; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + /** * Metrics for {@link ThreadPoolExecutor}. @@ -56,32 +64,32 @@ public class ThreadPoolMetrics { this.factory = new ThreadPoolMetricNameFactory("ThreadPools", path, poolName); - activeTasks = Metrics.newGauge(factory.createMetricName("ActiveTasks"), new Gauge() + activeTasks = Metrics.register(factory.createMetricName("ActiveTasks"), new Gauge() { - public Integer value() + public Integer getValue() { return executor.getActiveCount(); } }); - totalBlocked = Metrics.newCounter(factory.createMetricName("TotalBlockedTasks")); - currentBlocked = Metrics.newCounter(factory.createMetricName("CurrentlyBlockedTasks")); - completedTasks = Metrics.newGauge(factory.createMetricName("CompletedTasks"), new Gauge() + totalBlocked = Metrics.counter(factory.createMetricName("TotalBlockedTasks")); + currentBlocked = Metrics.counter(factory.createMetricName("CurrentlyBlockedTasks")); + completedTasks = Metrics.register(factory.createMetricName("CompletedTasks"), new Gauge() { - public Long value() + public Long getValue() { return executor.getCompletedTaskCount(); } }); - pendingTasks = Metrics.newGauge(factory.createMetricName("PendingTasks"), new Gauge() + pendingTasks = Metrics.register(factory.createMetricName("PendingTasks"), new Gauge() { - public Long value() + public Long getValue() { return executor.getTaskCount() - executor.getCompletedTaskCount(); } }); - maxPoolSize = Metrics.newGauge(factory.createMetricName("MaxPoolSize"), new Gauge() + maxPoolSize = Metrics.register(factory.createMetricName("MaxPoolSize"), new Gauge() { - public Integer value() + public Integer getValue() { return executor.getMaximumPoolSize(); } @@ -90,11 +98,41 @@ public class ThreadPoolMetrics public void release() { - Metrics.defaultRegistry().removeMetric(factory.createMetricName("ActiveTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("PendingTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CompletedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("TotalBlockedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("CurrentlyBlockedTasks")); - Metrics.defaultRegistry().removeMetric(factory.createMetricName("MaxPoolSize")); + Metrics.remove(factory.createMetricName("ActiveTasks")); + Metrics.remove(factory.createMetricName("PendingTasks")); + Metrics.remove(factory.createMetricName("CompletedTasks")); + Metrics.remove(factory.createMetricName("TotalBlockedTasks")); + Metrics.remove(factory.createMetricName("CurrentlyBlockedTasks")); + Metrics.remove(factory.createMetricName("MaxPoolSize")); } + + + public static Object getJmxMetric(MBeanServerConnection mbeanServerConn, String jmxPath, String poolName, String metricName) + { + String name = String.format("org.apache.cassandra.metrics:type=ThreadPools,path=%s,scope=%s,name=%s", jmxPath, poolName, metricName); + + try + { + ObjectName oName = new ObjectName(name); + + switch (metricName) + { + case "ActiveTasks": + case "PendingTasks": + case "CompletedTasks": + return JMX.newMBeanProxy(mbeanServerConn, oName, JmxReporter.JmxGaugeMBean.class).getValue(); + case "TotalBlockedTasks": + case "CurrentlyBlockedTasks": + return JMX.newMBeanProxy(mbeanServerConn, oName, JmxReporter.JmxCounterMBean.class).getCount(); + default: + throw new AssertionError("Unknown metric name " + metricName); + } + + } + catch (Throwable e) + { + throw new RuntimeException("Error reading: " + name, e); + } + } + } diff --git a/src/java/org/apache/cassandra/net/MessagingService.java b/src/java/org/apache/cassandra/net/MessagingService.java index 83ca384e3d..b33cf81579 100644 --- a/src/java/org/apache/cassandra/net/MessagingService.java +++ b/src/java/org/apache/cassandra/net/MessagingService.java @@ -879,7 +879,7 @@ public final class MessagingService implements MessagingServiceMBean boolean logTpstats = false; for (Map.Entry entry : droppedMessages.entrySet()) { - int dropped = (int) entry.getValue().dropped.count(); + int dropped = (int) entry.getValue().dropped.getCount(); Verb verb = entry.getKey(); int recent = dropped - lastDroppedInternal.get(verb); if (recent > 0) @@ -1018,26 +1018,14 @@ public final class MessagingService implements MessagingServiceMBean { Map map = new HashMap(droppedMessages.size()); for (Map.Entry entry : droppedMessages.entrySet()) - map.put(entry.getKey().toString(), (int) entry.getValue().dropped.count()); + map.put(entry.getKey().toString(), (int) entry.getValue().dropped.getCount()); return map; } - public Map getRecentlyDroppedMessages() - { - Map map = new HashMap(droppedMessages.size()); - for (Map.Entry entry : droppedMessages.entrySet()) - map.put(entry.getKey().toString(), entry.getValue().getRecentlyDropped()); - return map; - } public long getTotalTimeouts() { - return ConnectionMetrics.totalTimeouts.count(); - } - - public long getRecentTotalTimouts() - { - return ConnectionMetrics.getRecentTotalTimeout(); + return ConnectionMetrics.totalTimeouts.getCount(); } public Map getTimeoutsPerHost() @@ -1051,16 +1039,4 @@ public final class MessagingService implements MessagingServiceMBean } return result; } - - public Map getRecentTimeoutsPerHost() - { - Map result = new HashMap(connectionManagers.size()); - for (Map.Entry entry: connectionManagers.entrySet()) - { - String ip = entry.getKey().getHostAddress(); - long recent = entry.getValue().getRecentTimeouts(); - result.put(ip, recent); - } - return result; - } } diff --git a/src/java/org/apache/cassandra/net/MessagingServiceMBean.java b/src/java/org/apache/cassandra/net/MessagingServiceMBean.java index ff39e238e5..1d00656638 100644 --- a/src/java/org/apache/cassandra/net/MessagingServiceMBean.java +++ b/src/java/org/apache/cassandra/net/MessagingServiceMBean.java @@ -58,11 +58,6 @@ public interface MessagingServiceMBean */ public Map getDroppedMessages(); - /** - * dropped message counts since last called - */ - public Map getRecentlyDroppedMessages(); - /** * Total number of timeouts happened on this node */ @@ -73,15 +68,5 @@ public interface MessagingServiceMBean */ public Map getTimeoutsPerHost(); - /** - * Number of timeouts since last check. - */ - public long getRecentTotalTimouts(); - - /** - * Number of timeouts since last check per host. - */ - public Map getRecentTimeoutsPerHost(); - public int getVersion(String address) throws UnknownHostException; } diff --git a/src/java/org/apache/cassandra/net/OutboundTcpConnectionPool.java b/src/java/org/apache/cassandra/net/OutboundTcpConnectionPool.java index 66a0362baf..6395aea3f1 100644 --- a/src/java/org/apache/cassandra/net/OutboundTcpConnectionPool.java +++ b/src/java/org/apache/cassandra/net/OutboundTcpConnectionPool.java @@ -101,13 +101,9 @@ public class OutboundTcpConnectionPool public long getTimeouts() { - return metrics.timeouts.count(); + return metrics.timeouts.getCount(); } - public long getRecentTimeouts() - { - return metrics.getRecentTimeout(); - } public void incrementTimeout() { diff --git a/src/java/org/apache/cassandra/scheduler/RoundRobinScheduler.java b/src/java/org/apache/cassandra/scheduler/RoundRobinScheduler.java index 296867240a..1a18e613c8 100644 --- a/src/java/org/apache/cassandra/scheduler/RoundRobinScheduler.java +++ b/src/java/org/apache/cassandra/scheduler/RoundRobinScheduler.java @@ -145,8 +145,6 @@ public class RoundRobinScheduler implements IRequestScheduler weightedQueue = queues.putIfAbsent(id, maybenew); if (weightedQueue == null) { - // created new queue: register for monitoring - maybenew.register(); return maybenew; } diff --git a/src/java/org/apache/cassandra/scheduler/WeightedQueue.java b/src/java/org/apache/cassandra/scheduler/WeightedQueue.java index 6ab5215625..298938d1a7 100644 --- a/src/java/org/apache/cassandra/scheduler/WeightedQueue.java +++ b/src/java/org/apache/cassandra/scheduler/WeightedQueue.java @@ -26,7 +26,7 @@ import javax.management.ObjectName; import org.apache.cassandra.metrics.LatencyMetrics; -class WeightedQueue implements WeightedQueueMBean +class WeightedQueue { private final LatencyMetrics metric; @@ -41,20 +41,6 @@ class WeightedQueue implements WeightedQueueMBean this.metric = new LatencyMetrics("scheduler", "WeightedQueue", key); } - public void register() - { - // expose monitoring data - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - try - { - mbs.registerMBean(this, new ObjectName("org.apache.cassandra.scheduler:type=WeightedQueue,queue=" + key)); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } - public void put(Thread t, long timeoutMS) throws InterruptedException, TimeoutException { if (!queue.offer(new WeightedQueue.Entry(t), timeoutMS, TimeUnit.MILLISECONDS)) @@ -85,31 +71,4 @@ class WeightedQueue implements WeightedQueueMBean this.thread = thread; } } - - /** MBean related methods */ - - public long getOperations() - { - return metric.latency.count(); - } - - public long getTotalLatencyMicros() - { - return metric.totalLatency.count(); - } - - public double getRecentLatencyMicros() - { - return metric.getRecentLatency(); - } - - public long[] getTotalLatencyHistogramMicros() - { - return metric.totalLatencyHistogram.getBuckets(false); - } - - public long[] getRecentLatencyHistogramMicros() - { - return metric.recentLatencyHistogram.getBuckets(true); - } } diff --git a/src/java/org/apache/cassandra/scheduler/WeightedQueueMBean.java b/src/java/org/apache/cassandra/scheduler/WeightedQueueMBean.java deleted file mode 100644 index d16d00735b..0000000000 --- a/src/java/org/apache/cassandra/scheduler/WeightedQueueMBean.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.scheduler; - -/** - * Exposes client request scheduling metrics for a particular scheduler queue. - * @see org.apache.cassandra.metrics.LatencyMetrics - */ -@Deprecated -public interface WeightedQueueMBean -{ - public long getOperations(); - public long getTotalLatencyMicros(); - public double getRecentLatencyMicros(); - public long[] getTotalLatencyHistogramMicros(); - public long[] getRecentLatencyHistogramMicros(); -} diff --git a/src/java/org/apache/cassandra/service/CacheService.java b/src/java/org/apache/cassandra/service/CacheService.java index fb8153c2f5..e9f853c9ef 100644 --- a/src/java/org/apache/cassandra/service/CacheService.java +++ b/src/java/org/apache/cassandra/service/CacheService.java @@ -180,35 +180,6 @@ public class CacheService implements CacheServiceMBean return cache; } - public long getKeyCacheHits() - { - return keyCache.getMetrics().hits.count(); - } - - public long getRowCacheHits() - { - return rowCache.getMetrics().hits.count(); - } - - public long getKeyCacheRequests() - { - return keyCache.getMetrics().requests.count(); - } - - public long getRowCacheRequests() - { - return rowCache.getMetrics().requests.count(); - } - - public double getKeyCacheRecentHitRate() - { - return keyCache.getMetrics().getRecentHitRate(); - } - - public double getRowCacheRecentHitRate() - { - return rowCache.getMetrics().getRecentHitRate(); - } public int getRowCacheSavePeriodInSeconds() { @@ -339,15 +310,8 @@ public class CacheService implements CacheServiceMBean counterCache.clear(); } - public long getRowCacheCapacityInBytes() - { - return rowCache.getMetrics().capacity.value(); - } - public long getRowCacheCapacityInMB() - { - return getRowCacheCapacityInBytes() / 1024 / 1024; - } + public void setRowCacheCapacityInMB(long capacity) { @@ -357,15 +321,6 @@ public class CacheService implements CacheServiceMBean rowCache.setCapacity(capacity * 1024 * 1024); } - public long getKeyCacheCapacityInBytes() - { - return keyCache.getMetrics().capacity.value(); - } - - public long getKeyCacheCapacityInMB() - { - return getKeyCacheCapacityInBytes() / 1024 / 1024; - } public void setKeyCacheCapacityInMB(long capacity) { @@ -383,26 +338,6 @@ public class CacheService implements CacheServiceMBean counterCache.setCapacity(capacity * 1024 * 1024); } - public long getRowCacheSize() - { - return rowCache.getMetrics().size.value(); - } - - public long getRowCacheEntries() - { - return rowCache.size(); - } - - public long getKeyCacheSize() - { - return keyCache.getMetrics().size.value(); - } - - public long getKeyCacheEntries() - { - return keyCache.size(); - } - public void saveCaches() throws ExecutionException, InterruptedException { List> futures = new ArrayList<>(3); diff --git a/src/java/org/apache/cassandra/service/CacheServiceMBean.java b/src/java/org/apache/cassandra/service/CacheServiceMBean.java index 28e9d3b958..8ac0694e85 100644 --- a/src/java/org/apache/cassandra/service/CacheServiceMBean.java +++ b/src/java/org/apache/cassandra/service/CacheServiceMBean.java @@ -64,90 +64,4 @@ public interface CacheServiceMBean * @throws InterruptedException when a thread is waiting, sleeping, or otherwise occupied, and the thread is interrupted, either before or during the activity. */ public void saveCaches() throws ExecutionException, InterruptedException; - - // - // remaining methods are provided for backwards compatibility; modern clients should use CacheMetrics instead - // - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#hits - */ - @Deprecated - public long getKeyCacheHits(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#hits - */ - @Deprecated - public long getRowCacheHits(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#requests - */ - @Deprecated - public long getKeyCacheRequests(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#requests - */ - @Deprecated - public long getRowCacheRequests(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#hitRate - */ - @Deprecated - public double getKeyCacheRecentHitRate(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#hitRate - */ - @Deprecated - public double getRowCacheRecentHitRate(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#capacity - */ - @Deprecated - public long getRowCacheCapacityInMB(); - /** - * @see org.apache.cassandra.metrics.CacheMetrics#capacity - */ - @Deprecated - public long getRowCacheCapacityInBytes(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#capacity - */ - @Deprecated - public long getKeyCacheCapacityInMB(); - /** - * @see org.apache.cassandra.metrics.CacheMetrics#capacity - */ - @Deprecated - public long getKeyCacheCapacityInBytes(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#size - */ - @Deprecated - public long getRowCacheSize(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#entries - */ - @Deprecated - public long getRowCacheEntries(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#size - */ - @Deprecated - public long getKeyCacheSize(); - - /** - * @see org.apache.cassandra.metrics.CacheMetrics#entries - */ - @Deprecated - public long getKeyCacheEntries(); } diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java b/src/java/org/apache/cassandra/service/CassandraDaemon.java index 9a6c7d760b..0cfa1713e3 100644 --- a/src/java/org/apache/cassandra/service/CassandraDaemon.java +++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java @@ -58,6 +58,7 @@ import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.io.FSError; import org.apache.cassandra.io.sstable.CorruptSSTableException; import org.apache.cassandra.io.util.FileUtils; +import org.apache.cassandra.metrics.CassandraMetricsRegistry; import org.apache.cassandra.metrics.StorageMetrics; import org.apache.cassandra.thrift.ThriftServer; import org.apache.cassandra.tracing.Tracing; @@ -558,9 +559,9 @@ public class CassandraDaemon while (numOkay < GOSSIP_SETTLE_POLL_SUCCESSES_REQUIRED) { Uninterruptibles.sleepUninterruptibly(GOSSIP_SETTLE_POLL_INTERVAL_MS, TimeUnit.MILLISECONDS); - long completed = gossipStage.getCompletedTasks(); - long active = gossipStage.getActiveCount(); - long pending = gossipStage.getPendingTasks(); + long completed = gossipStage.metrics.completedTasks.getValue(); + long active = gossipStage.metrics.activeTasks.getValue(); + long pending = gossipStage.metrics.pendingTasks.getValue(); totalPolls++; if (active == 0 && pending == 0) { diff --git a/src/java/org/apache/cassandra/service/LoadBroadcaster.java b/src/java/org/apache/cassandra/service/LoadBroadcaster.java index d12ffba8d9..238daae331 100644 --- a/src/java/org/apache/cassandra/service/LoadBroadcaster.java +++ b/src/java/org/apache/cassandra/service/LoadBroadcaster.java @@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; +import org.apache.cassandra.metrics.StorageMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,7 +90,7 @@ public class LoadBroadcaster implements IEndpointStateChangeSubscriber if (logger.isDebugEnabled()) logger.debug("Disseminating load info ..."); Gossiper.instance.addLocalApplicationState(ApplicationState.LOAD, - StorageService.instance.valueFactory.load(StorageService.instance.getLoad())); + StorageService.instance.valueFactory.load(StorageMetrics.load.getCount())); } }; ScheduledExecutors.scheduledTasks.scheduleWithFixedDelay(runnable, 2 * Gossiper.intervalInMillis, BROADCAST_INTERVAL, TimeUnit.MILLISECONDS); diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java index cf9990b635..f74e6017a1 100644 --- a/src/java/org/apache/cassandra/service/StorageProxy.java +++ b/src/java/org/apache/cassandra/service/StorageProxy.java @@ -568,7 +568,6 @@ public class StorageProxy implements StorageProxyMBean else { writeMetrics.timeouts.mark(); - ClientRequestMetrics.writeTimeouts.inc(); Tracing.trace("Write timeout; received {} of {} required replies", ex.received, ex.blockFor); throw ex; } @@ -576,13 +575,12 @@ public class StorageProxy implements StorageProxyMBean catch (UnavailableException e) { writeMetrics.unavailables.mark(); - ClientRequestMetrics.writeUnavailables.inc(); Tracing.trace("Unavailable"); throw e; } catch (OverloadedException e) { - ClientRequestMetrics.writeUnavailables.inc(); + writeMetrics.unavailables.mark(); Tracing.trace("Overloaded"); throw e; } @@ -651,14 +649,12 @@ public class StorageProxy implements StorageProxyMBean catch (UnavailableException e) { writeMetrics.unavailables.mark(); - ClientRequestMetrics.writeUnavailables.inc(); Tracing.trace("Unavailable"); throw e; } catch (WriteTimeoutException e) { writeMetrics.timeouts.mark(); - ClientRequestMetrics.writeTimeouts.inc(); Tracing.trace("Write timeout; received {} of {} required replies", e.received, e.blockFor); throw e; } @@ -864,10 +860,10 @@ public class StorageProxy implements StorageProxyMBean // The idea is that if we have over maxHintsInProgress hints in flight, this is probably due to // a small number of nodes causing problems, so we should avoid shutting down writes completely to // healthy nodes. Any node with no hintsInProgress is considered healthy. - if (StorageMetrics.totalHintsInProgress.count() > maxHintsInProgress + if (StorageMetrics.totalHintsInProgress.getCount() > maxHintsInProgress && (getHintsInProgressFor(destination).get() > 0 && shouldHint(destination))) { - throw new OverloadedException("Too many in flight hints: " + StorageMetrics.totalHintsInProgress.count()); + throw new OverloadedException("Too many in flight hints: " + StorageMetrics.totalHintsInProgress.getCount()); } if (FailureDetector.instance.isAlive(destination)) @@ -1184,7 +1180,6 @@ public class StorageProxy implements StorageProxyMBean if (StorageService.instance.isBootstrapMode() && !systemKeyspaceQuery(commands)) { readMetrics.unavailables.mark(); - ClientRequestMetrics.readUnavailables.inc(); throw new IsBootstrappingException(); } @@ -1233,21 +1228,18 @@ public class StorageProxy implements StorageProxyMBean catch (UnavailableException e) { readMetrics.unavailables.mark(); - ClientRequestMetrics.readUnavailables.inc(); casReadMetrics.unavailables.mark(); throw e; } catch (ReadTimeoutException e) { readMetrics.timeouts.mark(); - ClientRequestMetrics.readTimeouts.inc(); casReadMetrics.timeouts.mark(); throw e; } catch (ReadFailureException e) { readMetrics.failures.mark(); - ClientRequestMetrics.readFailures.inc(); casReadMetrics.failures.mark(); throw e; } @@ -1277,19 +1269,16 @@ public class StorageProxy implements StorageProxyMBean catch (UnavailableException e) { readMetrics.unavailables.mark(); - ClientRequestMetrics.readUnavailables.inc(); throw e; } catch (ReadTimeoutException e) { readMetrics.timeouts.mark(); - ClientRequestMetrics.readTimeouts.inc(); throw e; } catch (ReadFailureException e) { readMetrics.failures.mark(); - ClientRequestMetrics.readFailures.inc(); throw e; } finally @@ -1989,81 +1978,6 @@ public class StorageProxy implements StorageProxyMBean return ranges; } - public long getReadOperations() - { - return readMetrics.latency.count(); - } - - public long getTotalReadLatencyMicros() - { - return readMetrics.totalLatency.count(); - } - - public double getRecentReadLatencyMicros() - { - return readMetrics.getRecentLatency(); - } - - public long[] getTotalReadLatencyHistogramMicros() - { - return readMetrics.totalLatencyHistogram.getBuckets(false); - } - - public long[] getRecentReadLatencyHistogramMicros() - { - return readMetrics.recentLatencyHistogram.getBuckets(true); - } - - public long getRangeOperations() - { - return rangeMetrics.latency.count(); - } - - public long getTotalRangeLatencyMicros() - { - return rangeMetrics.totalLatency.count(); - } - - public double getRecentRangeLatencyMicros() - { - return rangeMetrics.getRecentLatency(); - } - - public long[] getTotalRangeLatencyHistogramMicros() - { - return rangeMetrics.totalLatencyHistogram.getBuckets(false); - } - - public long[] getRecentRangeLatencyHistogramMicros() - { - return rangeMetrics.recentLatencyHistogram.getBuckets(true); - } - - public long getWriteOperations() - { - return writeMetrics.latency.count(); - } - - public long getTotalWriteLatencyMicros() - { - return writeMetrics.totalLatency.count(); - } - - public double getRecentWriteLatencyMicros() - { - return writeMetrics.getRecentLatency(); - } - - public long[] getTotalWriteLatencyHistogramMicros() - { - return writeMetrics.totalLatencyHistogram.getBuckets(false); - } - - public long[] getRecentWriteLatencyHistogramMicros() - { - return writeMetrics.recentLatencyHistogram.getBuckets(true); - } - public boolean getHintedHandoffEnabled() { return DatabaseDescriptor.hintedHandoffEnabled(); @@ -2291,7 +2205,7 @@ public class StorageProxy implements StorageProxyMBean public long getTotalHints() { - return StorageMetrics.totalHints.count(); + return StorageMetrics.totalHints.getCount(); } public int getMaxHintsInProgress() @@ -2306,7 +2220,7 @@ public class StorageProxy implements StorageProxyMBean public int getHintsInProgress() { - return (int) StorageMetrics.totalHintsInProgress.count(); + return (int) StorageMetrics.totalHintsInProgress.getCount(); } public void verifyNoHintsInProgress() @@ -2339,14 +2253,14 @@ public class StorageProxy implements StorageProxyMBean public long getReadRepairAttempted() { - return ReadRepairMetrics.attempted.count(); + return ReadRepairMetrics.attempted.getCount(); } public long getReadRepairRepairedBlocking() { - return ReadRepairMetrics.repairedBlocking.count(); + return ReadRepairMetrics.repairedBlocking.getCount(); } public long getReadRepairRepairedBackground() { - return ReadRepairMetrics.repairedBackground.count(); + return ReadRepairMetrics.repairedBackground.getCount(); } } diff --git a/src/java/org/apache/cassandra/service/StorageProxyMBean.java b/src/java/org/apache/cassandra/service/StorageProxyMBean.java index a04b660549..55627be656 100644 --- a/src/java/org/apache/cassandra/service/StorageProxyMBean.java +++ b/src/java/org/apache/cassandra/service/StorageProxyMBean.java @@ -23,54 +23,6 @@ import java.util.Set; public interface StorageProxyMBean { - /** - * @see org.apache.cassandra.metrics.LatencyMetrics#lastOpCount - */ - @Deprecated - public long getReadOperations(); - /** - * @see org.apache.cassandra.metrics.LatencyMetrics#totalLatencyHistogram - */ - @Deprecated - public long getTotalReadLatencyMicros(); - /** - * @see org.apache.cassandra.metrics.LatencyMetrics#recentLatencyHistogram - */ - @Deprecated - public double getRecentReadLatencyMicros(); - /** - * @see org.apache.cassandra.metrics.LatencyMetrics#totalLatencyHistogram - */ - @Deprecated - public long[] getTotalReadLatencyHistogramMicros(); - /** - * @see org.apache.cassandra.metrics.LatencyMetrics#recentLatencyHistogram - */ - @Deprecated - public long[] getRecentReadLatencyHistogramMicros(); - - @Deprecated - public long getRangeOperations(); - @Deprecated - public long getTotalRangeLatencyMicros(); - @Deprecated - public double getRecentRangeLatencyMicros(); - @Deprecated - public long[] getTotalRangeLatencyHistogramMicros(); - @Deprecated - public long[] getRecentRangeLatencyHistogramMicros(); - - @Deprecated - public long getWriteOperations(); - @Deprecated - public long getTotalWriteLatencyMicros(); - @Deprecated - public double getRecentWriteLatencyMicros(); - @Deprecated - public long[] getTotalWriteLatencyHistogramMicros(); - @Deprecated - public long[] getRecentWriteLatencyHistogramMicros(); - public long getTotalHints(); public boolean getHintedHandoffEnabled(); public Set getHintedHandoffEnabledByDC(); diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index bb3e88294c..61bd938ee6 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -2009,24 +2009,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE onDead(endpoint, state); } - /** raw load value */ - public double getLoad() - { - double bytes = 0; - for (String keyspaceName : Schema.instance.getKeyspaces()) - { - Keyspace keyspace = Schema.instance.getKeyspaceInstance(keyspaceName); - if (keyspace == null) - continue; - for (ColumnFamilyStore cfs : keyspace.getColumnFamilyStores()) - bytes += cfs.getLiveDiskSpaceUsed(); - } - return bytes; - } public String getLoadString() { - return FileUtils.stringifyFileSize(getLoad()); + return FileUtils.stringifyFileSize(StorageMetrics.load.getCount()); } public Map getLoadMap() @@ -4113,11 +4099,6 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return loader.stream(); } - public int getExceptionCount() - { - return (int)StorageMetrics.exceptions.count(); - } - public void rescheduleFailedDeletions() { SSTableDeletingTask.rescheduleFailedTasks(); diff --git a/src/java/org/apache/cassandra/service/StorageServiceMBean.java b/src/java/org/apache/cassandra/service/StorageServiceMBean.java index 007ef31d21..06ab56a8e1 100644 --- a/src/java/org/apache/cassandra/service/StorageServiceMBean.java +++ b/src/java/org/apache/cassandra/service/StorageServiceMBean.java @@ -161,13 +161,6 @@ public interface StorageServiceMBean extends NotificationEmitter /** Retrieve the mapping of endpoint to host ID */ public Map getHostIdMap(); - /** - * Numeric load value. - * @see org.apache.cassandra.metrics.StorageMetrics#load - */ - @Deprecated - public double getLoad(); - /** Human-readable load value */ public String getLoadString(); @@ -441,9 +434,6 @@ public interface StorageServiceMBean extends NotificationEmitter public void joinRing() throws IOException; public boolean isJoined(); - @Deprecated - public int getExceptionCount(); - public void setStreamThroughputMbPerSec(int value); public int getStreamThroughputMbPerSec(); diff --git a/src/java/org/apache/cassandra/thrift/CassandraServer.java b/src/java/org/apache/cassandra/thrift/CassandraServer.java index c0de59f311..6546932d5a 100644 --- a/src/java/org/apache/cassandra/thrift/CassandraServer.java +++ b/src/java/org/apache/cassandra/thrift/CassandraServer.java @@ -538,7 +538,7 @@ public class CassandraServer implements Cassandra.Iface // request by page if this is a large row if (cfs.getMeanColumns() > 0) { - int averageColumnSize = (int) (cfs.getMeanRowSize() / cfs.getMeanColumns()); + int averageColumnSize = (int) (cfs.metric.meanRowSize.getValue() / cfs.getMeanColumns()); pageSize = Math.min(COUNT_PAGE_SIZE, 4 * 1024 * 1024 / averageColumnSize); pageSize = Math.max(2, pageSize); logger.debug("average row column size is {}; using pageSize of {}", averageColumnSize, pageSize); diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java b/src/java/org/apache/cassandra/tools/NodeProbe.java index 8aa5992051..9e8945fe49 100644 --- a/src/java/org/apache/cassandra/tools/NodeProbe.java +++ b/src/java/org/apache/cassandra/tools/NodeProbe.java @@ -27,7 +27,10 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import java.util.Map.Entry; -import java.util.concurrent.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + import javax.management.*; import javax.management.openmbean.CompositeData; import javax.management.remote.JMXConnector; @@ -39,8 +42,7 @@ import com.google.common.base.Function; import com.google.common.collect.*; import com.google.common.util.concurrent.Uninterruptibles; -import com.yammer.metrics.reporting.JmxReporter; -import org.apache.cassandra.concurrent.JMXEnabledThreadPoolExecutorMBean; +import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.db.ColumnFamilyStoreMBean; import org.apache.cassandra.db.HintedHandOffManager; import org.apache.cassandra.db.HintedHandOffManagerMBean; @@ -51,7 +53,9 @@ import org.apache.cassandra.gms.FailureDetectorMBean; import org.apache.cassandra.gms.Gossiper; import org.apache.cassandra.gms.GossiperMBean; import org.apache.cassandra.locator.EndpointSnitchInfoMBean; -import org.apache.cassandra.metrics.ColumnFamilyMetrics.Sampler; +import org.apache.cassandra.metrics.CassandraMetricsRegistry; +import org.apache.cassandra.metrics.StorageMetrics; +import org.apache.cassandra.metrics.ThreadPoolMetrics; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.MessagingServiceMBean; import org.apache.cassandra.service.*; @@ -59,6 +63,8 @@ import org.apache.cassandra.streaming.StreamState; import org.apache.cassandra.streaming.StreamManagerMBean; import org.apache.cassandra.streaming.management.StreamStateCompositeData; +import org.apache.cassandra.metrics.ColumnFamilyMetrics.Sampler; + /** * JMX client operations for Cassandra. */ @@ -525,22 +531,6 @@ public class NodeProbe implements AutoCloseable gossProxy.assassinateEndpoint(address); } - public Iterator> getThreadPoolMBeanProxies() - { - try - { - return new ThreadPoolProxyMBeanIterator(mbeanServerConn); - } - catch (MalformedObjectNameException e) - { - throw new RuntimeException("Invalid ObjectName? Please report this as a bug.", e); - } - catch (IOException e) - { - throw new RuntimeException("Could not retrieve list of stat mbeans.", e); - } - } - /** * Set the compaction threshold * @@ -879,7 +869,7 @@ public class NodeProbe implements AutoCloseable public int getExceptionCount() { - return ssProxy.getExceptionCount(); + return (int)StorageMetrics.exceptions.getCount(); } public Map getDroppedMessages() @@ -980,12 +970,12 @@ public class NodeProbe implements AutoCloseable case "Size": return JMX.newMBeanProxy(mbeanServerConn, new ObjectName("org.apache.cassandra.metrics:type=Cache,scope=" + cacheType + ",name=" + metricName), - JmxReporter.GaugeMBean.class).getValue(); + CassandraMetricsRegistry.JmxGaugeMBean.class).getValue(); case "Requests": case "Hits": return JMX.newMBeanProxy(mbeanServerConn, new ObjectName("org.apache.cassandra.metrics:type=Cache,scope=" + cacheType + ",name=" + metricName), - JmxReporter.MeterMBean.class).getCount(); + CassandraMetricsRegistry.JmxMeterMBean.class).getCount(); default: throw new RuntimeException("Unknown cache metric name."); @@ -997,6 +987,11 @@ public class NodeProbe implements AutoCloseable } } + public Object getThreadPoolMetric(Stage stage, String metricName) + { + return ThreadPoolMetrics.getJmxMetric(mbeanServerConn, stage.getJmxType(), stage.getJmxName(), metricName); + } + /** * Retrieve ColumnFamily metrics * @param ks Keyspace for which stats are to be displayed. @@ -1031,7 +1026,7 @@ public class NodeProbe implements AutoCloseable case "RecentBloomFilterFalsePositives": case "RecentBloomFilterFalseRatio": case "SnapshotsSize": - return JMX.newMBeanProxy(mbeanServerConn, oName, JmxReporter.GaugeMBean.class).getValue(); + return JMX.newMBeanProxy(mbeanServerConn, oName, CassandraMetricsRegistry.JmxGaugeMBean.class).getValue(); case "LiveDiskSpaceUsed": case "MemtableSwitchCount": case "SpeculativeRetries": @@ -1039,16 +1034,16 @@ public class NodeProbe implements AutoCloseable case "WriteTotalLatency": case "ReadTotalLatency": case "PendingFlushes": - return JMX.newMBeanProxy(mbeanServerConn, oName, JmxReporter.CounterMBean.class).getCount(); - case "ReadLatency": + return JMX.newMBeanProxy(mbeanServerConn, oName, CassandraMetricsRegistry.JmxCounterMBean.class).getCount(); case "CoordinatorReadLatency": case "CoordinatorScanLatency": + case "ReadLatency": case "WriteLatency": - return JMX.newMBeanProxy(mbeanServerConn, oName, JmxReporter.TimerMBean.class); + return JMX.newMBeanProxy(mbeanServerConn, oName, CassandraMetricsRegistry.JmxTimerMBean.class); case "LiveScannedHistogram": case "SSTablesPerReadHistogram": case "TombstoneScannedHistogram": - return JMX.newMBeanProxy(mbeanServerConn, oName, JmxReporter.HistogramMBean.class); + return JMX.newMBeanProxy(mbeanServerConn, oName, CassandraMetricsRegistry.JmxHistogramMBean.class); default: throw new RuntimeException("Unknown table metric."); } @@ -1063,13 +1058,13 @@ public class NodeProbe implements AutoCloseable * Retrieve Proxy metrics * @param scope RangeSlice, Read or Write */ - public JmxReporter.TimerMBean getProxyMetric(String scope) + public CassandraMetricsRegistry.JmxTimerMBean getProxyMetric(String scope) { try { return JMX.newMBeanProxy(mbeanServerConn, new ObjectName("org.apache.cassandra.metrics:type=ClientRequest,scope=" + scope + ",name=Latency"), - JmxReporter.TimerMBean.class); + CassandraMetricsRegistry.JmxTimerMBean.class); } catch (MalformedObjectNameException e) { @@ -1090,16 +1085,16 @@ public class NodeProbe implements AutoCloseable case "BytesCompacted": return JMX.newMBeanProxy(mbeanServerConn, new ObjectName("org.apache.cassandra.metrics:type=Compaction,name=" + metricName), - JmxReporter.CounterMBean.class); + CassandraMetricsRegistry.JmxCounterMBean.class); case "CompletedTasks": case "PendingTasks": return JMX.newMBeanProxy(mbeanServerConn, new ObjectName("org.apache.cassandra.metrics:type=Compaction,name=" + metricName), - JmxReporter.GaugeMBean.class).getValue(); + CassandraMetricsRegistry.JmxGaugeMBean.class).getValue(); case "TotalCompactionsCompleted": return JMX.newMBeanProxy(mbeanServerConn, new ObjectName("org.apache.cassandra.metrics:type=Compaction,name=" + metricName), - JmxReporter.MeterMBean.class); + CassandraMetricsRegistry.JmxMeterMBean.class); default: throw new RuntimeException("Unknown compaction metric."); } @@ -1120,7 +1115,7 @@ public class NodeProbe implements AutoCloseable { return JMX.newMBeanProxy(mbeanServerConn, new ObjectName("org.apache.cassandra.metrics:type=Storage,name=" + metricName), - JmxReporter.CounterMBean.class).getCount(); + CassandraMetricsRegistry.JmxCounterMBean.class).getCount(); } catch (MalformedObjectNameException e) { @@ -1128,7 +1123,18 @@ public class NodeProbe implements AutoCloseable } } - public double[] metricPercentilesAsArray(JmxReporter.HistogramMBean metric) + public double[] metricPercentilesAsArray(CassandraMetricsRegistry.JmxHistogramMBean metric) + { + return new double[]{ metric.get50thPercentile(), + metric.get75thPercentile(), + metric.get95thPercentile(), + metric.get98thPercentile(), + metric.get99thPercentile(), + metric.getMin(), + metric.getMax()}; + } + + public double[] metricPercentilesAsArray(CassandraMetricsRegistry.JmxTimerMBean metric) { return new double[]{ metric.get50thPercentile(), metric.get75thPercentile(), @@ -1242,36 +1248,3 @@ class ColumnFamilyStoreMBeanIterator implements Iterator> -{ - private final Iterator resIter; - private final MBeanServerConnection mbeanServerConn; - - public ThreadPoolProxyMBeanIterator(MBeanServerConnection mbeanServerConn) - throws MalformedObjectNameException, NullPointerException, IOException - { - Set requests = mbeanServerConn.queryNames(new ObjectName("org.apache.cassandra.request:type=*"), null); - Set internal = mbeanServerConn.queryNames(new ObjectName("org.apache.cassandra.internal:type=*"), null); - resIter = Iterables.concat(requests, internal).iterator(); - this.mbeanServerConn = mbeanServerConn; - } - - public boolean hasNext() - { - return resIter.hasNext(); - } - - public Map.Entry next() - { - ObjectName objectName = resIter.next(); - String poolName = objectName.getKeyProperty("type"); - JMXEnabledThreadPoolExecutorMBean threadPoolProxy = JMX.newMBeanProxy(mbeanServerConn, objectName, JMXEnabledThreadPoolExecutorMBean.class); - return new AbstractMap.SimpleImmutableEntry(poolName, threadPoolProxy); - } - - public void remove() - { - throw new UnsupportedOperationException(); - } -} diff --git a/src/java/org/apache/cassandra/tools/NodeTool.java b/src/java/org/apache/cassandra/tools/NodeTool.java index 18feac7b20..e99fd15317 100644 --- a/src/java/org/apache/cassandra/tools/NodeTool.java +++ b/src/java/org/apache/cassandra/tools/NodeTool.java @@ -31,24 +31,22 @@ import javax.management.openmbean.*; import com.google.common.base.Joiner; import com.google.common.base.Throwables; - import com.google.common.collect.*; -import com.yammer.metrics.reporting.JmxReporter; - import io.airlift.command.*; + +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.cassandra.concurrent.JMXEnabledThreadPoolExecutorMBean; +import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.db.ColumnFamilyStoreMBean; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.db.compaction.CompactionManagerMBean; import org.apache.cassandra.db.compaction.OperationType; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.locator.EndpointSnitchInfoMBean; - -import org.apache.cassandra.metrics.ColumnFamilyMetrics.Sampler; - +import org.apache.cassandra.metrics.CassandraMetricsRegistry; +import org.apache.cassandra.metrics.ColumnFamilyMetrics; import org.apache.cassandra.net.MessagingServiceMBean; import org.apache.cassandra.repair.messages.RepairOption; import org.apache.cassandra.repair.RepairParallelism; @@ -60,7 +58,7 @@ import org.apache.cassandra.utils.EstimatedHistogram; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.JVMStabilityInspector; -import org.apache.commons.lang3.ArrayUtils; +import static org.apache.cassandra.metrics.ColumnFamilyMetrics.Sampler; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Throwables.getStackTraceAsString; @@ -739,8 +737,8 @@ public class NodeTool for (ColumnFamilyStoreMBean cfstore : columnFamilies) { String cfName = cfstore.getColumnFamilyName(); - long writeCount = ((JmxReporter.TimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "WriteLatency")).getCount(); - long readCount = ((JmxReporter.TimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "ReadLatency")).getCount(); + long writeCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "WriteLatency")).getCount(); + long readCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "ReadLatency")).getCount(); if (readCount > 0) { @@ -821,12 +819,12 @@ public class NodeTool System.out.println("\t\tMemtable data size: " + format((Long) probe.getColumnFamilyMetric(keyspaceName, cfName, "MemtableLiveDataSize"), humanReadable)); System.out.println("\t\tMemtable off heap memory used: " + format(memtableOffHeapSize, humanReadable)); System.out.println("\t\tMemtable switch count: " + probe.getColumnFamilyMetric(keyspaceName, cfName, "MemtableSwitchCount")); - System.out.println("\t\tLocal read count: " + ((JmxReporter.TimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "ReadLatency")).getCount()); - double localReadLatency = ((JmxReporter.TimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "ReadLatency")).getMean() / 1000; + System.out.println("\t\tLocal read count: " + ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "ReadLatency")).getCount()); + double localReadLatency = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "ReadLatency")).getMean() / 1000; double localRLatency = localReadLatency > 0 ? localReadLatency : Double.NaN; System.out.printf("\t\tLocal read latency: %01.3f ms%n", localRLatency); - System.out.println("\t\tLocal write count: " + ((JmxReporter.TimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "WriteLatency")).getCount()); - double localWriteLatency = ((JmxReporter.TimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "WriteLatency")).getMean() / 1000; + System.out.println("\t\tLocal write count: " + ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "WriteLatency")).getCount()); + double localWriteLatency = ((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "WriteLatency")).getMean() / 1000; double localWLatency = localWriteLatency > 0 ? localWriteLatency : Double.NaN; System.out.printf("\t\tLocal write latency: %01.3f ms%n", localWLatency); System.out.println("\t\tPending flushes: " + probe.getColumnFamilyMetric(keyspaceName, cfName, "PendingFlushes")); @@ -840,10 +838,10 @@ public class NodeTool System.out.println("\t\tCompacted partition minimum bytes: " + format((Long) probe.getColumnFamilyMetric(keyspaceName, cfName, "MinRowSize"), humanReadable)); System.out.println("\t\tCompacted partition maximum bytes: " + format((Long) probe.getColumnFamilyMetric(keyspaceName, cfName, "MaxRowSize"), humanReadable)); System.out.println("\t\tCompacted partition mean bytes: " + format((Long) probe.getColumnFamilyMetric(keyspaceName, cfName, "MeanRowSize"), humanReadable)); - JmxReporter.HistogramMBean histogram = (JmxReporter.HistogramMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "LiveScannedHistogram"); + CassandraMetricsRegistry.JmxHistogramMBean histogram = (CassandraMetricsRegistry.JmxHistogramMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "LiveScannedHistogram"); System.out.println("\t\tAverage live cells per slice (last five minutes): " + histogram.getMean()); System.out.println("\t\tMaximum live cells per slice (last five minutes): " + histogram.getMax()); - histogram = (JmxReporter.HistogramMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "TombstoneScannedHistogram"); + histogram = (CassandraMetricsRegistry.JmxHistogramMBean) probe.getColumnFamilyMetric(keyspaceName, cfName, "TombstoneScannedHistogram"); System.out.println("\t\tAverage tombstones per slice (last five minutes): " + histogram.getMean()); System.out.println("\t\tMaximum tombstones per slice (last five minutes): " + histogram.getMax()); @@ -945,7 +943,7 @@ public class NodeTool @Option(name = "-k", description = "Number of the top partitions to list (Default: 10)") private int topCount = 10; @Option(name = "-a", description = "Comma separated list of samplers to use (Default: all)") - private String samplers = join(Sampler.values(), ','); + private String samplers = join(ColumnFamilyMetrics.Sampler.values(), ','); @Override public void execute(NodeProbe probe) { @@ -1083,9 +1081,9 @@ public class NodeTool } String[] percentiles = new String[]{"50%", "75%", "95%", "98%", "99%", "Min", "Max"}; - double[] readLatency = probe.metricPercentilesAsArray((JmxReporter.HistogramMBean) probe.getColumnFamilyMetric(keyspace, cfname, "ReadLatency")); - double[] writeLatency = probe.metricPercentilesAsArray((JmxReporter.TimerMBean) probe.getColumnFamilyMetric(keyspace, cfname, "WriteLatency")); - double[] sstablesPerRead = probe.metricPercentilesAsArray((JmxReporter.HistogramMBean) probe.getColumnFamilyMetric(keyspace, cfname, "SSTablesPerReadHistogram")); + double[] readLatency = probe.metricPercentilesAsArray((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspace, cfname, "ReadLatency")); + double[] writeLatency = probe.metricPercentilesAsArray((CassandraMetricsRegistry.JmxTimerMBean) probe.getColumnFamilyMetric(keyspace, cfname, "WriteLatency")); + double[] sstablesPerRead = probe.metricPercentilesAsArray((CassandraMetricsRegistry.JmxHistogramMBean) probe.getColumnFamilyMetric(keyspace, cfname, "SSTablesPerReadHistogram")); System.out.println(format("%s/%s histograms", keyspace, cfname)); System.out.println(format("%-10s%10s%18s%18s%18s%18s", @@ -2565,19 +2563,15 @@ public class NodeTool { System.out.printf("%-25s%10s%10s%15s%10s%18s%n", "Pool Name", "Active", "Pending", "Completed", "Blocked", "All time blocked"); - Iterator> threads = probe.getThreadPoolMBeanProxies(); - while (threads.hasNext()) + for (Stage stage : Stage.jmxEnabledStages()) { - Map.Entry thread = threads.next(); - String poolName = thread.getKey(); - JMXEnabledThreadPoolExecutorMBean threadPoolProxy = thread.getValue(); System.out.printf("%-25s%10s%10s%15s%10s%18s%n", - poolName, - threadPoolProxy.getActiveCount(), - threadPoolProxy.getPendingTasks(), - threadPoolProxy.getCompletedTasks(), - threadPoolProxy.getCurrentlyBlockedTasks(), - threadPoolProxy.getTotalBlockedTasks()); + stage.getJmxName(), + probe.getThreadPoolMetric(stage, "ActiveTasks"), + probe.getThreadPoolMetric(stage, "PendingTasks"), + probe.getThreadPoolMetric(stage, "CompletedTasks"), + probe.getThreadPoolMetric(stage, "CurrentlyBlockedTasks"), + probe.getThreadPoolMetric(stage, "TotalBlockedTasks")); } System.out.printf("%n%-20s%10s%n", "Message type", "Dropped"); diff --git a/src/java/org/apache/cassandra/transport/RequestThreadPoolExecutor.java b/src/java/org/apache/cassandra/transport/RequestThreadPoolExecutor.java index 9cac645948..4ecd6a77f2 100644 --- a/src/java/org/apache/cassandra/transport/RequestThreadPoolExecutor.java +++ b/src/java/org/apache/cassandra/transport/RequestThreadPoolExecutor.java @@ -26,7 +26,7 @@ import io.netty.util.concurrent.Future; import org.apache.cassandra.concurrent.TracingAwareExecutorService; import org.apache.cassandra.config.DatabaseDescriptor; -import static org.apache.cassandra.concurrent.JMXEnabledSharedExecutorPool.SHARED; +import static org.apache.cassandra.concurrent.SharedExecutorPool.SHARED; public class RequestThreadPoolExecutor extends AbstractEventExecutor { @@ -34,8 +34,8 @@ public class RequestThreadPoolExecutor extends AbstractEventExecutor private final static String THREAD_FACTORY_ID = "Native-Transport-Requests"; private final TracingAwareExecutorService wrapped = SHARED.newExecutor(DatabaseDescriptor.getNativeTransportMaxThreads(), MAX_QUEUED_REQUESTS, - THREAD_FACTORY_ID, - "transport"); + "transport", + THREAD_FACTORY_ID); public boolean isShuttingDown() { diff --git a/src/java/org/apache/cassandra/utils/StatusLogger.java b/src/java/org/apache/cassandra/utils/StatusLogger.java index acd923d2dc..a1d5e18114 100644 --- a/src/java/org/apache/cassandra/utils/StatusLogger.java +++ b/src/java/org/apache/cassandra/utils/StatusLogger.java @@ -19,15 +19,14 @@ package org.apache.cassandra.utils; import java.lang.management.ManagementFactory; import java.util.Set; -import javax.management.JMX; -import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; +import javax.management.*; import com.google.common.collect.Iterables; import org.apache.cassandra.cache.*; +import org.apache.cassandra.concurrent.Stage; +import org.apache.cassandra.metrics.ThreadPoolMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,34 +42,25 @@ public class StatusLogger { private static final Logger logger = LoggerFactory.getLogger(StatusLogger.class); + public static void log() { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); // everything from o.a.c.concurrent logger.info(String.format("%-25s%10s%10s%15s%10s%18s", "Pool Name", "Active", "Pending", "Completed", "Blocked", "All Time Blocked")); - Set request, internal; - try + + for (Stage stage : Stage.jmxEnabledStages()) { - request = server.queryNames(new ObjectName("org.apache.cassandra.request:type=*"), null); - internal = server.queryNames(new ObjectName("org.apache.cassandra.internal:type=*"), null); - } - catch (MalformedObjectNameException e) - { - throw new RuntimeException(e); - } - for (ObjectName objectName : Iterables.concat(request, internal)) - { - String poolName = objectName.getKeyProperty("type"); - JMXEnabledThreadPoolExecutorMBean threadPoolProxy = JMX.newMBeanProxy(server, objectName, JMXEnabledThreadPoolExecutorMBean.class); - logger.info(String.format("%-25s%10s%10s%15s%10s%18s", - poolName, - threadPoolProxy.getActiveCount(), - threadPoolProxy.getPendingTasks(), - threadPoolProxy.getCompletedTasks(), - threadPoolProxy.getCurrentlyBlockedTasks(), - threadPoolProxy.getTotalBlockedTasks())); + System.out.printf("%-25s%10s%10s%15s%10s%18s%n", + stage.getJmxName(), + ThreadPoolMetrics.getJmxMetric(server, stage.getJmxType(), stage.getJmxName(), "ActiveTasks"), + ThreadPoolMetrics.getJmxMetric(server, stage.getJmxType(), stage.getJmxName(), "PendingTasks"), + ThreadPoolMetrics.getJmxMetric(server, stage.getJmxType(), stage.getJmxName(), "CompletedTasks"), + ThreadPoolMetrics.getJmxMetric(server, stage.getJmxType(), stage.getJmxName(), "CurrentlyBlockedTasks"), + ThreadPoolMetrics.getJmxMetric(server, stage.getJmxType(), stage.getJmxName(), "TotalBlockedTasks")); } + // one offs logger.info(String.format("%-25s%10s%10s", "CompactionManager", CompactionManager.instance.getActiveCompactions(), CompactionManager.instance.getPendingTasks())); @@ -114,7 +104,7 @@ public class StatusLogger { logger.info(String.format("%-25s%20s", cfs.keyspace.getName() + "." + cfs.name, - cfs.getMemtableColumnsCount() + "," + cfs.getMemtableDataSize())); + cfs.metric.memtableColumnsCount.getValue() + "," + cfs.metric.memtableLiveDataSize.getValue())); } } } diff --git a/src/java/org/apache/cassandra/utils/concurrent/WaitQueue.java b/src/java/org/apache/cassandra/utils/concurrent/WaitQueue.java index df3d38d71c..be271b6dbd 100644 --- a/src/java/org/apache/cassandra/utils/concurrent/WaitQueue.java +++ b/src/java/org/apache/cassandra/utils/concurrent/WaitQueue.java @@ -18,13 +18,13 @@ */ package org.apache.cassandra.utils.concurrent; -import com.yammer.metrics.core.TimerContext; - import java.util.Iterator; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.locks.LockSupport; +import com.codahale.metrics.Timer; + /** *

A relatively easy to use utility for general purpose thread signalling.

*

Usage on a thread awaiting a state change using a WaitQueue q is:

@@ -96,7 +96,7 @@ public final class WaitQueue * or the waiting thread is interrupted. * @return */ - public Signal register(TimerContext context) + public Signal register(Timer.Context context) { assert context != null; RegisteredSignal signal = new TimedSignal(context); @@ -389,9 +389,9 @@ public final class WaitQueue */ private final class TimedSignal extends RegisteredSignal { - private final TimerContext context; + private final Timer.Context context; - private TimedSignal(TimerContext context) + private TimedSignal(Timer.Context context) { this.context = context; } diff --git a/test/long/org/apache/cassandra/concurrent/LongSharedExecutorPoolTest.java b/test/long/org/apache/cassandra/concurrent/LongSharedExecutorPoolTest.java index 3453261599..fe464c7804 100644 --- a/test/long/org/apache/cassandra/concurrent/LongSharedExecutorPoolTest.java +++ b/test/long/org/apache/cassandra/concurrent/LongSharedExecutorPoolTest.java @@ -116,7 +116,7 @@ public class LongSharedExecutorPoolTest final ExecutorService[] executors = new ExecutorService[executorCount]; for (int i = 0 ; i < executors.length ; i++) { - executors[i] = JMXEnabledSharedExecutorPool.SHARED.newExecutor(threadCount, maxQueued, "test" + i, "test" + i); + executors[i] = SharedExecutorPool.SHARED.newExecutor(threadCount, maxQueued, "test" + i, "test" + i); threadCounts[i] = threadCount; workCount[i] = new WeibullDistribution(2, maxQueued); threadCount *= 2; diff --git a/test/long/org/apache/cassandra/db/LongFlushMemtableTest.java b/test/long/org/apache/cassandra/db/LongFlushMemtableTest.java index fd23f330af..24993c82d2 100644 --- a/test/long/org/apache/cassandra/db/LongFlushMemtableTest.java +++ b/test/long/org/apache/cassandra/db/LongFlushMemtableTest.java @@ -78,7 +78,7 @@ public class LongFlushMemtableTest for (ColumnFamilyStore cfs : ColumnFamilyStore.all()) { if (cfs.name.startsWith("_CF")) - flushes += cfs.getMemtableSwitchCount(); + flushes += cfs.metric.memtableSwitchCount.getCount(); } assert flushes > 0; } diff --git a/test/long/org/apache/cassandra/utils/LongBTreeTest.java b/test/long/org/apache/cassandra/utils/LongBTreeTest.java index eb7567150a..133c6c5621 100644 --- a/test/long/org/apache/cassandra/utils/LongBTreeTest.java +++ b/test/long/org/apache/cassandra/utils/LongBTreeTest.java @@ -46,10 +46,10 @@ import com.google.common.util.concurrent.ListenableFutureTask; import org.junit.Assert; import org.junit.Test; -import com.yammer.metrics.Metrics; -import com.yammer.metrics.core.Timer; -import com.yammer.metrics.core.TimerContext; -import com.yammer.metrics.stats.Snapshot; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Snapshot; +import com.codahale.metrics.Timer; import org.apache.cassandra.concurrent.NamedThreadFactory; import org.apache.cassandra.utils.btree.BTree; import org.apache.cassandra.utils.btree.BTreeSearchIterator; @@ -60,8 +60,9 @@ import org.apache.cassandra.utils.btree.UpdateFunction; public class LongBTreeTest { - private static final Timer BTREE_TIMER = Metrics.newTimer(BTree.class, "BTREE", TimeUnit.NANOSECONDS, TimeUnit.NANOSECONDS); - private static final Timer TREE_TIMER = Metrics.newTimer(BTree.class, "TREE", TimeUnit.NANOSECONDS, TimeUnit.NANOSECONDS); + private static final MetricRegistry metrics = new MetricRegistry(); + private static final Timer BTREE_TIMER = metrics.timer(MetricRegistry.name(BTree.class, "BTREE")); + private static final Timer TREE_TIMER = metrics.timer(MetricRegistry.name(BTree.class, "TREE")); private static final ExecutorService MODIFY = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory("MODIFY")); private static final ExecutorService COMPARE = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory("COMPARE")); private static final RandomAbort SPORADIC_ABORT = new RandomAbort<>(new Random(), 0.0001f); @@ -236,7 +237,7 @@ public class LongBTreeTest } mods -= c; } - TimerContext ctxt; + Timer.Context ctxt; ctxt = TREE_TIMER.time(); canon.putAll(buffer); ctxt.stop(); diff --git a/test/unit/org/apache/cassandra/db/ColumnFamilyMetricTest.java b/test/unit/org/apache/cassandra/db/ColumnFamilyMetricTest.java index f09eb9366c..45ed56ecd4 100644 --- a/test/unit/org/apache/cassandra/db/ColumnFamilyMetricTest.java +++ b/test/unit/org/apache/cassandra/db/ColumnFamilyMetricTest.java @@ -53,8 +53,8 @@ public class ColumnFamilyMetricTest store.truncateBlocking(); - assertEquals(0, store.metric.liveDiskSpaceUsed.count()); - assertEquals(0, store.metric.totalDiskSpaceUsed.count()); + assertEquals(0, store.metric.liveDiskSpaceUsed.getCount()); + assertEquals(0, store.metric.totalDiskSpaceUsed.getCount()); for (int j = 0; j < 10; j++) { @@ -72,14 +72,14 @@ public class ColumnFamilyMetricTest } // size metrics should show the sum of all SSTable sizes - assertEquals(size, store.metric.liveDiskSpaceUsed.count()); - assertEquals(size, store.metric.totalDiskSpaceUsed.count()); + assertEquals(size, store.metric.liveDiskSpaceUsed.getCount()); + assertEquals(size, store.metric.totalDiskSpaceUsed.getCount()); store.truncateBlocking(); // after truncate, size metrics should be down to 0 - assertEquals(0, store.metric.liveDiskSpaceUsed.count()); - assertEquals(0, store.metric.totalDiskSpaceUsed.count()); + assertEquals(0, store.metric.liveDiskSpaceUsed.getCount()); + assertEquals(0, store.metric.totalDiskSpaceUsed.getCount()); store.enableAutoCompaction(); } diff --git a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java index 7c086f78ba..5679ba5975 100644 --- a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java +++ b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java @@ -81,6 +81,7 @@ import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.io.sstable.metadata.MetadataCollector; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.locator.SimpleStrategy; +import org.apache.cassandra.metrics.ClearableHistogram; import org.apache.cassandra.service.ActiveRepairService; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.thrift.SlicePredicate; @@ -178,9 +179,9 @@ public class ColumnFamilyStoreTest rm.applyUnsafe(); cfs.forceBlockingFlush(); - cfs.getRecentSSTablesPerReadHistogram(); // resets counts + ((ClearableHistogram)cfs.metric.sstablesPerReadHistogram.cf).clear(); // resets counts cfs.getColumnFamily(Util.namesQueryFilter(cfs, Util.dk("key1"), "Column1")); - assertEquals(1, cfs.getRecentSSTablesPerReadHistogram()[0]); + assertEquals(1, cfs.metric.sstablesPerReadHistogram.cf.getCount()); } @Test diff --git a/test/unit/org/apache/cassandra/db/KeyspaceTest.java b/test/unit/org/apache/cassandra/db/KeyspaceTest.java index 01038c4290..4c97ea2c76 100644 --- a/test/unit/org/apache/cassandra/db/KeyspaceTest.java +++ b/test/unit/org/apache/cassandra/db/KeyspaceTest.java @@ -45,6 +45,7 @@ import org.apache.cassandra.db.marshal.CompositeType; import org.apache.cassandra.db.marshal.IntegerType; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.locator.SimpleStrategy; +import org.apache.cassandra.metrics.ClearableHistogram; import org.apache.cassandra.utils.WrappedRunnable; import static org.apache.cassandra.Util.column; import static org.apache.cassandra.Util.expiringColumn; @@ -481,18 +482,18 @@ public class KeyspaceTest rm.applyUnsafe(); cfStore.forceBlockingFlush(); } - cfStore.metric.sstablesPerReadHistogram.cf.clear(); + ((ClearableHistogram)cfStore.metric.sstablesPerReadHistogram.cf).clear(); ColumnFamily cf = cfStore.getColumnFamily(key, Composites.EMPTY, cellname("col1499"), false, 1000, System.currentTimeMillis()); - assertEquals(cfStore.metric.sstablesPerReadHistogram.cf.max(), 5, 0.1); + assertEquals(cfStore.metric.sstablesPerReadHistogram.cf.getSnapshot().getMax(), 5, 0.1); int i = 0; for (Cell c : cf.getSortedColumns()) { assertEquals(ByteBufferUtil.string(c.name().toByteBuffer()), "col" + (1000 + i++)); } assertEquals(i, 500); - cfStore.metric.sstablesPerReadHistogram.cf.clear(); + ((ClearableHistogram)cfStore.metric.sstablesPerReadHistogram.cf).clear(); cf = cfStore.getColumnFamily(key, cellname("col1500"), cellname("col2000"), false, 1000, System.currentTimeMillis()); - assertEquals(cfStore.metric.sstablesPerReadHistogram.cf.max(), 5, 0.1); + assertEquals(cfStore.metric.sstablesPerReadHistogram.cf.getSnapshot().getMax(), 5, 0.1); for (Cell c : cf.getSortedColumns()) { @@ -501,9 +502,9 @@ public class KeyspaceTest assertEquals(i, 1000); // reverse - cfStore.metric.sstablesPerReadHistogram.cf.clear(); + ((ClearableHistogram)cfStore.metric.sstablesPerReadHistogram.cf).clear(); cf = cfStore.getColumnFamily(key, cellname("col2000"), cellname("col1500"), true, 1000, System.currentTimeMillis()); - assertEquals(cfStore.metric.sstablesPerReadHistogram.cf.max(), 5, 0.1); + assertEquals(cfStore.metric.sstablesPerReadHistogram.cf.getSnapshot().getMax(), 5, 0.1); i = 500; for (Cell c : cf.getSortedColumns()) { @@ -550,13 +551,13 @@ public class KeyspaceTest } Composite start = type.builder().add(ByteBufferUtil.bytes("a5")).add(ByteBufferUtil.bytes(85)).build(); Composite finish = type.builder().add(ByteBufferUtil.bytes("a5")).build().end(); - cfs.metric.sstablesPerReadHistogram.cf.clear(); + ((ClearableHistogram)cfs.metric.sstablesPerReadHistogram.cf).clear(); ColumnFamily cf = cfs.getColumnFamily(key, start, finish, false, 1000, System.currentTimeMillis()); int colCount = 0; for (Cell c : cf) colCount++; assertEquals(2, colCount); - assertEquals(2, cfs.metric.sstablesPerReadHistogram.cf.max(), 0.1); + assertEquals(2, cfs.metric.sstablesPerReadHistogram.cf.getSnapshot().getMax(), 0.1); } private void validateSliceLarge(ColumnFamilyStore cfStore) throws IOException diff --git a/test/unit/org/apache/cassandra/db/RowCacheTest.java b/test/unit/org/apache/cassandra/db/RowCacheTest.java index 3d5617fabb..a4b7514432 100644 --- a/test/unit/org/apache/cassandra/db/RowCacheTest.java +++ b/test/unit/org/apache/cassandra/db/RowCacheTest.java @@ -185,8 +185,8 @@ public class RowCacheTest Keyspace keyspace = Keyspace.open(KEYSPACE_CACHED); String cf = "CachedIntCF"; ColumnFamilyStore cachedStore = keyspace.getColumnFamilyStore(cf); - long startRowCacheHits = cachedStore.metric.rowCacheHit.count(); - long startRowCacheOutOfRange = cachedStore.metric.rowCacheHitOutOfRange.count(); + long startRowCacheHits = cachedStore.metric.rowCacheHit.getCount(); + long startRowCacheOutOfRange = cachedStore.metric.rowCacheHitOutOfRange.getCount(); // empty the row cache CacheService.instance.invalidateRowCache(); @@ -206,31 +206,31 @@ public class RowCacheTest Composites.EMPTY, Composites.EMPTY, false, 10, System.currentTimeMillis())); - assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.count()); + assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.getCount()); // do another query, limit is 20, which is < 100 that we cache, we should get a hit and it should be in range cachedStore.getColumnFamily(QueryFilter.getSliceFilter(dk, cf, Composites.EMPTY, Composites.EMPTY, false, 20, System.currentTimeMillis())); - assertEquals(++startRowCacheHits, cachedStore.metric.rowCacheHit.count()); - assertEquals(startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.count()); + assertEquals(++startRowCacheHits, cachedStore.metric.rowCacheHit.getCount()); + assertEquals(startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.getCount()); // get a slice from 95 to 105, 95->99 are in cache, we should not get a hit and then row cache is out of range cachedStore.getColumnFamily(QueryFilter.getSliceFilter(dk, cf, CellNames.simpleDense(ByteBufferUtil.bytes(95)), CellNames.simpleDense(ByteBufferUtil.bytes(105)), false, 10, System.currentTimeMillis())); - assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.count()); - assertEquals(++startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.count()); + assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.getCount()); + assertEquals(++startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.getCount()); // get a slice with limit > 100, we should get a hit out of range. cachedStore.getColumnFamily(QueryFilter.getSliceFilter(dk, cf, Composites.EMPTY, Composites.EMPTY, false, 101, System.currentTimeMillis())); - assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.count()); - assertEquals(++startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.count()); + assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.getCount()); + assertEquals(++startRowCacheOutOfRange, cachedStore.metric.rowCacheHitOutOfRange.getCount()); CacheService.instance.invalidateRowCache(); @@ -240,7 +240,7 @@ public class RowCacheTest Composites.EMPTY, Composites.EMPTY, false, 105, System.currentTimeMillis())); - assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.count()); + assertEquals(startRowCacheHits, cachedStore.metric.rowCacheHit.getCount()); // validate the stuff in cache; ColumnFamily cachedCf = (ColumnFamily)CacheService.instance.rowCache.get(rck); assertEquals(cachedCf.getColumnCount(), 100); diff --git a/test/unit/org/apache/cassandra/db/compaction/AntiCompactionTest.java b/test/unit/org/apache/cassandra/db/compaction/AntiCompactionTest.java index 018d6431ca..60c5e6994c 100644 --- a/test/unit/org/apache/cassandra/db/compaction/AntiCompactionTest.java +++ b/test/unit/org/apache/cassandra/db/compaction/AntiCompactionTest.java @@ -143,8 +143,8 @@ public class AntiCompactionTest long sum = 0; for (SSTableReader x : cfs.getSSTables()) sum += x.bytesOnDisk(); - assertEquals(sum, cfs.metric.liveDiskSpaceUsed.count()); - assertEquals(origSize, cfs.metric.liveDiskSpaceUsed.count(), 100000); + assertEquals(sum, cfs.metric.liveDiskSpaceUsed.getCount()); + assertEquals(origSize, cfs.metric.liveDiskSpaceUsed.getCount(), 100000); } diff --git a/test/unit/org/apache/cassandra/io/sstable/SSTableReaderTest.java b/test/unit/org/apache/cassandra/io/sstable/SSTableReaderTest.java index 25f906fb79..8aa402a05c 100644 --- a/test/unit/org/apache/cassandra/io/sstable/SSTableReaderTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/SSTableReaderTest.java @@ -200,7 +200,7 @@ public class SSTableReaderTest store.forceBlockingFlush(); clearAndLoad(store); - assert store.getMaxRowSize() != 0; + assert store.metric.maxRowSize.getValue() != 0; } private void clearAndLoad(ColumnFamilyStore cfs) diff --git a/test/unit/org/apache/cassandra/io/sstable/SSTableRewriterTest.java b/test/unit/org/apache/cassandra/io/sstable/SSTableRewriterTest.java index 97625f4d06..c823215b20 100644 --- a/test/unit/org/apache/cassandra/io/sstable/SSTableRewriterTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/SSTableRewriterTest.java @@ -250,7 +250,7 @@ public class SSTableRewriterTest extends SchemaLoader SSTableReader s = writeFile(cfs, 1000); cfs.addSSTable(s); - long startStorageMetricsLoad = StorageMetrics.load.count(); + long startStorageMetricsLoad = StorageMetrics.load.getCount(); Set compacting = Sets.newHashSet(s); SSTableRewriter.overrideOpenInterval(10000000); SSTableRewriter rewriter = new SSTableRewriter(cfs, compacting, 1000, false); @@ -268,8 +268,8 @@ public class SSTableRewriterTest extends SchemaLoader rewriter.switchWriter(getWriter(cfs, s.descriptor.directory)); files++; assertEquals(cfs.getSSTables().size(), files); // we have one original file plus the ones we have switched out. - assertEquals(s.bytesOnDisk(), cfs.metric.liveDiskSpaceUsed.count()); - assertEquals(s.bytesOnDisk(), cfs.metric.totalDiskSpaceUsed.count()); + assertEquals(s.bytesOnDisk(), cfs.metric.liveDiskSpaceUsed.getCount()); + assertEquals(s.bytesOnDisk(), cfs.metric.totalDiskSpaceUsed.getCount()); } } @@ -279,13 +279,13 @@ public class SSTableRewriterTest extends SchemaLoader long sum = 0; for (SSTableReader x : cfs.getSSTables()) sum += x.bytesOnDisk(); - assertEquals(sum, cfs.metric.liveDiskSpaceUsed.count()); - assertEquals(startStorageMetricsLoad - s.bytesOnDisk() + sum, StorageMetrics.load.count()); + assertEquals(sum, cfs.metric.liveDiskSpaceUsed.getCount()); + assertEquals(startStorageMetricsLoad - s.bytesOnDisk() + sum, StorageMetrics.load.getCount()); assertEquals(files, sstables.size()); assertEquals(files, cfs.getSSTables().size()); Thread.sleep(1000); // tmplink and tmp files should be gone: - assertEquals(sum, cfs.metric.totalDiskSpaceUsed.count()); + assertEquals(sum, cfs.metric.totalDiskSpaceUsed.getCount()); assertFileCounts(s.descriptor.directory.list(), 0, 0); validateCFS(cfs); } @@ -340,7 +340,7 @@ public class SSTableRewriterTest extends SchemaLoader SSTableReader s = writeFile(cfs, 1000); cfs.addSSTable(s); - long startSize = cfs.metric.liveDiskSpaceUsed.count(); + long startSize = cfs.metric.liveDiskSpaceUsed.getCount(); DecoratedKey origFirst = s.first; DecoratedKey origLast = s.last; Set compacting = Sets.newHashSet(s); @@ -365,7 +365,7 @@ public class SSTableRewriterTest extends SchemaLoader } rewriter.abort(); Thread.sleep(1000); - assertEquals(startSize, cfs.metric.liveDiskSpaceUsed.count()); + assertEquals(startSize, cfs.metric.liveDiskSpaceUsed.getCount()); assertEquals(1, cfs.getSSTables().size()); assertFileCounts(s.descriptor.directory.list(), 0, 0); assertEquals(cfs.getSSTables().iterator().next().first, origFirst); diff --git a/test/unit/org/apache/cassandra/metrics/CQLMetricsTest.java b/test/unit/org/apache/cassandra/metrics/CQLMetricsTest.java index 88104a6e98..a357d24d73 100644 --- a/test/unit/org/apache/cassandra/metrics/CQLMetricsTest.java +++ b/test/unit/org/apache/cassandra/metrics/CQLMetricsTest.java @@ -64,9 +64,9 @@ public class CQLMetricsTest extends SchemaLoader @Test public void testPreparedStatementsCount() { - assertEquals(0, (int) QueryProcessor.metrics.preparedStatementsCount.value()); + assertEquals(0, (int) QueryProcessor.metrics.preparedStatementsCount.getValue()); metricsStatement = session.prepare("INSERT INTO junit.metricstest (id, val) VALUES (?, ?)"); - assertEquals(1, (int) QueryProcessor.metrics.preparedStatementsCount.value()); + assertEquals(1, (int) QueryProcessor.metrics.preparedStatementsCount.getValue()); } @Test @@ -74,14 +74,14 @@ public class CQLMetricsTest extends SchemaLoader { clearMetrics(); - assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.count()); - assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.count()); + assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.getCount()); + assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.getCount()); for (int i = 0; i < 10; i++) session.execute(String.format("INSERT INTO junit.metricstest (id, val) VALUES (%d, '%s')", i, "val" + i)); - assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.count()); - assertEquals(10, QueryProcessor.metrics.regularStatementsExecuted.count()); + assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.getCount()); + assertEquals(10, QueryProcessor.metrics.regularStatementsExecuted.getCount()); } @Test @@ -89,14 +89,14 @@ public class CQLMetricsTest extends SchemaLoader { clearMetrics(); - assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.count()); - assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.count()); + assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.getCount()); + assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.getCount()); for (int i = 0; i < 10; i++) session.execute(metricsStatement.bind(i, "val" + i)); - assertEquals(10, QueryProcessor.metrics.preparedStatementsExecuted.count()); - assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.count()); + assertEquals(10, QueryProcessor.metrics.preparedStatementsExecuted.getCount()); + assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.getCount()); } @Test @@ -104,22 +104,22 @@ public class CQLMetricsTest extends SchemaLoader { clearMetrics(); - assertEquals(Double.NaN, QueryProcessor.metrics.preparedStatementsRatio.value()); + assertEquals(Double.NaN, QueryProcessor.metrics.preparedStatementsRatio.getValue()); for (int i = 0; i < 10; i++) session.execute(metricsStatement.bind(i, "val" + i)); - assertEquals(1.0, QueryProcessor.metrics.preparedStatementsRatio.value()); + assertEquals(1.0, QueryProcessor.metrics.preparedStatementsRatio.getValue()); for (int i = 0; i < 10; i++) session.execute(String.format("INSERT INTO junit.metricstest (id, val) VALUES (%d, '%s')", i, "val" + i)); - assertEquals(0.5, QueryProcessor.metrics.preparedStatementsRatio.value()); + assertEquals(0.5, QueryProcessor.metrics.preparedStatementsRatio.getValue()); } private void clearMetrics() { - QueryProcessor.metrics.preparedStatementsExecuted.clear(); - QueryProcessor.metrics.regularStatementsExecuted.clear(); - QueryProcessor.metrics.preparedStatementsEvicted.clear(); + QueryProcessor.metrics.preparedStatementsExecuted.dec(QueryProcessor.metrics.preparedStatementsExecuted.getCount()); + QueryProcessor.metrics.regularStatementsExecuted.dec(QueryProcessor.metrics.regularStatementsExecuted.getCount()); + QueryProcessor.metrics.preparedStatementsEvicted.dec(QueryProcessor.metrics.preparedStatementsEvicted.getCount()); } } diff --git a/test/unit/org/apache/cassandra/metrics/LatencyMetricsTest.java b/test/unit/org/apache/cassandra/metrics/LatencyMetricsTest.java index e5f080416b..ae4b733842 100644 --- a/test/unit/org/apache/cassandra/metrics/LatencyMetricsTest.java +++ b/test/unit/org/apache/cassandra/metrics/LatencyMetricsTest.java @@ -43,7 +43,7 @@ public class LatencyMetricsTest for (int i = 0; i < 10000; i++) { - Double recent = l.getRecentLatency(); + Double recent = l.latency.getOneMinuteRate(); assertFalse(recent.equals(Double.POSITIVE_INFINITY)); } }