This is an automated email from the ASF dual-hosted git repository.
smiklosovic pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new 6cdf0fc38b Add prepared statement cache stats to nodetool info
6cdf0fc38b is described below
commit 6cdf0fc38baca39aa2e24231a09ae763fc600b60
Author: Arvind Kandpal <[email protected]>
AuthorDate: Wed Aug 5 20:04:40 2026 +0530
Add prepared statement cache stats to nodetool info
patch by Arvind Kandpal; reviewed by Maxwell Guo, Stefan Miklosovic for
CASSANDRA-14366
---
CHANGES.txt | 1 +
.../org/apache/cassandra/metrics/CQLMetrics.java | 2 +
src/java/org/apache/cassandra/tools/NodeProbe.java | 32 +++++++++++
.../org/apache/cassandra/tools/nodetool/Info.java | 17 ++++++
.../mock/nodetool/InternalNodeProbe.java | 6 +++
.../apache/cassandra/tools/nodetool/InfoTest.java | 62 ++++++++++++++++++++++
6 files changed, 120 insertions(+)
diff --git a/CHANGES.txt b/CHANGES.txt
index 497e278978..ef37224d5f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
7.0
+ * Add prepared statement cache stats to nodetool info (CASSANDRA-14366)
* Don't increment client metrics on messaging service connection unpause
(CASSANDRA-21491)
* Add nodetool getreplicas (CASSANDRA-17665)
* Implementation of CEP-49: Hardware-accelerated compression (CASSANDRA-20975)
diff --git a/src/java/org/apache/cassandra/metrics/CQLMetrics.java
b/src/java/org/apache/cassandra/metrics/CQLMetrics.java
index e7b6ec57d1..4a134c9a42 100644
--- a/src/java/org/apache/cassandra/metrics/CQLMetrics.java
+++ b/src/java/org/apache/cassandra/metrics/CQLMetrics.java
@@ -39,6 +39,7 @@ public class CQLMetrics
public final Gauge<Integer> preparedStatementsCount;
public final Gauge<Double> preparedStatementsRatio;
public final Gauge<Long> preparedStatementsCacheSize;
+ public final Gauge<Long> preparedStatementsCacheCapacity;
public CQLMetrics()
{
@@ -67,5 +68,6 @@ public class CQLMetrics
}
});
preparedStatementsCacheSize =
Metrics.register(factory.createMetricName("PreparedStatementsCacheSize"),
QueryProcessor::preparedStatementsCacheMemoryUsedBytes);
+ preparedStatementsCacheCapacity =
Metrics.register(factory.createMetricName("PreparedStatementsCacheCapacity"),
() -> QueryProcessor.PREPARED_STATEMENT_CACHE_SIZE_BYTES);
}
}
diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java
b/src/java/org/apache/cassandra/tools/NodeProbe.java
index c60e0427e5..c6e774f5aa 100644
--- a/src/java/org/apache/cassandra/tools/NodeProbe.java
+++ b/src/java/org/apache/cassandra/tools/NodeProbe.java
@@ -44,11 +44,16 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.annotation.Nullable;
+import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
import javax.management.JMX;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
+import javax.management.ReflectionException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.TabularData;
@@ -112,7 +117,9 @@ import
org.apache.cassandra.locator.DynamicEndpointSnitchMBean;
import org.apache.cassandra.locator.EndpointSnitchInfoMBean;
import org.apache.cassandra.locator.LocationInfoMBean;
import org.apache.cassandra.metrics.CIDRAuthorizerMetrics;
+import org.apache.cassandra.metrics.CQLMetrics;
import org.apache.cassandra.metrics.CassandraMetricsRegistry;
+import org.apache.cassandra.metrics.DefaultNameFactory;
import org.apache.cassandra.metrics.StorageMetrics;
import org.apache.cassandra.metrics.TableMetrics;
import org.apache.cassandra.metrics.ThreadPoolMetrics;
@@ -1939,6 +1946,31 @@ public class NodeProbe implements AutoCloseable
}
}
+ /**
+ * Retrieve a CQL metric value by name. Works generically for any metric
registered under
+ * {@code org.apache.cassandra.metrics:type=CQL,name=<metricName>} by
inspecting the MBean
+ * attributes at runtime, without requiring knowledge of the underlying
metric type.
+ */
+ public Object getCQLMetric(String metricName)
+ {
+ try
+ {
+ ObjectName objectName = new
ObjectName(DefaultNameFactory.GROUP_NAME + ":type=" + CQLMetrics.TYPE_NAME +
",name=" + metricName);
+ for (MBeanAttributeInfo attr :
mbeanServerConn.getMBeanInfo(objectName).getAttributes())
+ {
+ String name = attr.getName();
+ if ("Value".equals(name) || "Count".equals(name))
+ return mbeanServerConn.getAttribute(objectName, name);
+ }
+ throw new RuntimeException("No readable value attribute for CQL
metric: " + metricName);
+ }
+ catch (MalformedObjectNameException | InstanceNotFoundException |
IntrospectionException |
+ ReflectionException | AttributeNotFoundException |
MBeanException | IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
private static Multimap<String, String>
getJmxThreadPools(MBeanServerConnection mbeanServerConn)
{
try
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Info.java
b/src/java/org/apache/cassandra/tools/nodetool/Info.java
index b0b2dff019..f216493be9 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Info.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Info.java
@@ -119,6 +119,23 @@ public class Info extends AbstractCommand
probe.getCacheMetric("CounterCache", "HitRate"),
cacheService.getCounterCacheSavePeriodInSeconds());
+ // Prepared Stmt Cache: entries, size, capacity, executions, evictions
+ try
+ {
+ out.printf("%-23s: entries %d, size %s, capacity %s, %d
executions, %d evictions%n",
+ "Prepared Stmt Cache",
+ probe.getCQLMetric("PreparedStatementsCount"),
+ FileUtils.stringifyFileSize((long)
probe.getCQLMetric("PreparedStatementsCacheSize")),
+ FileUtils.stringifyFileSize((long)
probe.getCQLMetric("PreparedStatementsCacheCapacity")),
+ probe.getCQLMetric("PreparedStatementsExecuted"),
+ probe.getCQLMetric("PreparedStatementsEvicted"));
+ }
+ catch (RuntimeException e)
+ {
+ if (!(e.getCause() instanceof InstanceNotFoundException))
+ throw e;
+ }
+
// Chunk Cache: Hits, Requests, RecentHitRate, SavePeriodInSeconds
try
{
diff --git
a/test/distributed/org/apache/cassandra/distributed/mock/nodetool/InternalNodeProbe.java
b/test/distributed/org/apache/cassandra/distributed/mock/nodetool/InternalNodeProbe.java
index 62e0dad3ea..78183f7d2b 100644
---
a/test/distributed/org/apache/cassandra/distributed/mock/nodetool/InternalNodeProbe.java
+++
b/test/distributed/org/apache/cassandra/distributed/mock/nodetool/InternalNodeProbe.java
@@ -174,6 +174,12 @@ public class InternalNodeProbe extends NodeProbe
throw new UnsupportedOperationException();
}
+ @Override
+ public Object getCQLMetric(String metricName)
+ {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public Object getClientMetric(String metricName)
{
diff --git a/test/unit/org/apache/cassandra/tools/nodetool/InfoTest.java
b/test/unit/org/apache/cassandra/tools/nodetool/InfoTest.java
new file mode 100644
index 0000000000..73a8a2828b
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/nodetool/InfoTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.tools.nodetool;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.datastax.driver.core.PreparedStatement;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class InfoTest extends CQLTester
+{
+ private static final Pattern PREPARED_STATEMENT_CACHE_PATTERN =
+ Pattern.compile("Prepared Stmt Cache\\s+: entries (\\d+), size ([^,]+),
capacity ([^,]+), (\\d+) executions, (\\d+) evictions");
+
+ @BeforeClass
+ public static void setup() throws Exception
+ {
+ requireNetwork();
+ startJMXServer();
+ }
+
+ @Test
+ public void testInfoContainsPreparedStatementCache()
+ {
+ createTable("CREATE TABLE %s (id int PRIMARY KEY, val text)");
+ PreparedStatement preparedStatement = sessionNet().prepare("INSERT
INTO " + KEYSPACE + '.' + currentTable() + " (id, val) VALUES (?, ?)");
+ sessionNet().execute(preparedStatement.bind(1, "value1"));
+
+ ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("info");
+ tool.assertOnCleanExit();
+ String stdout = tool.getStdout();
+ assertThat(stdout).contains("Prepared Stmt Cache");
+ Matcher matcher = PREPARED_STATEMENT_CACHE_PATTERN.matcher(stdout);
+ assertThat(matcher.find()).isTrue();
+ assertThat(Integer.parseInt(matcher.group(1))).isGreaterThan(0);
+ assertThat(matcher.group(2)).isNotEqualTo("0 bytes");
+ assertThat(Integer.parseInt(matcher.group(4))).isGreaterThan(0);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]