Expose Metric for Prepared Statement Cache Size (in bytes)

Patch by Isaac Reath; Reviewed by Paulo Motta and guo Maxwell for
CASSANDRA-20864

Closes #4345
This commit is contained in:
Isaac Reath 2024-02-23 14:54:30 -05:00 committed by Paulo Motta
parent f53c051118
commit f41b625e48
5 changed files with 59 additions and 8 deletions

View File

@ -1,4 +1,5 @@
5.1
* Expose Metric for Prepared Statement Cache Size (in bytes) (CASSANDRA-20864)
* Add support for BEGIN TRANSACTION to allow mutations that touch multiple partitions (CASSANDRA-20857)
* AutoRepair: Safeguard Full repair against disk protection(CASSANDRA-20045)
* BEGIN TRANSACTION crashes if a mutation touches multiple rows (CASSANDRA-20844)

View File

@ -671,6 +671,8 @@ executed.
|PreparedStatementsRatio |Gauge<Double> |Percentage of statements that
are prepared vs unprepared.
|PreparedStatementsCacheSize | Gauge<Long> | The size of the prepared statements cache in bytes.
|===
[[dropped-metrics]]

View File

@ -42,6 +42,7 @@ import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import org.antlr.runtime.RecognitionException;
import org.apache.cassandra.concurrent.ImmediateExecutor;
import org.apache.cassandra.concurrent.ScheduledExecutors;
import org.apache.cassandra.config.DatabaseDescriptor;
@ -178,6 +179,16 @@ public class QueryProcessor implements QueryHandler
return preparedStatements.asMap().size();
}
public static long preparedStatementsCacheMemoryUsedBytes()
{
long preparedStatementsCacheMemoryUsedBytes = 0;
for (Map.Entry<MD5Digest, Prepared> entry : preparedStatements.asMap().entrySet())
{
preparedStatementsCacheMemoryUsedBytes += getSizeOfPreparedStatementForCache(entry.getKey(), entry.getValue());
}
return preparedStatementsCacheMemoryUsedBytes;
}
// Work around initialization dependency
private enum InternalStateInstance
{

View File

@ -37,6 +37,7 @@ public class CQLMetrics
public final Gauge<Integer> preparedStatementsCount;
public final Gauge<Double> preparedStatementsRatio;
public final Gauge<Long> preparedStatementsCacheSize;
public CQLMetrics()
{
@ -64,5 +65,6 @@ public class CQLMetrics
return regularStatementsExecuted.getCount() + preparedStatementsExecuted.getCount();
}
});
preparedStatementsCacheSize = Metrics.register(factory.createMetricName("PreparedStatementsCacheSize"), QueryProcessor::preparedStatementsCacheMemoryUsedBytes);
}
}

View File

@ -31,9 +31,12 @@ import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import org.apache.cassandra.ServerTestUtils;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.QueryHandler;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.EmbeddedCassandraService;
import org.apache.cassandra.utils.MD5Digest;
import static org.apache.cassandra.cql3.CQLTester.assertRowsContains;
import static org.apache.cassandra.cql3.CQLTester.row;
@ -95,18 +98,50 @@ public class CQLMetricsTest
}
@Test
public void testPreparedStatementsCount()
public void testPreparedStatementsCountWithUse()
{
int n = QueryProcessor.metrics.preparedStatementsCount.getValue();
// Use local test session to avoid interference with other tests due to CASSANDRA-17248
try (Session localSession = cluster.connect())
{
long useCountBefore = QueryProcessor.metrics.useStatementsExecuted.getCount();
localSession.execute("use junit");
Assert.assertEquals(useCountBefore + 1, QueryProcessor.metrics.useStatementsExecuted.getCount());
localSession.prepare("SELECT * FROM junit.metricstest WHERE id = ?");
assertEquals(n + 2, (int) QueryProcessor.metrics.preparedStatementsCount.getValue());
assertRowsContains(cluster, session.execute("SELECT * FROM system_metrics.cql_group"),
row("org.apache.cassandra.metrics.CQL.PreparedStatementsCount", METRIC_SCOPE_UNDEFINED, "gauge",
String.valueOf(QueryProcessor.metrics.preparedStatementsCount.getValue())));
}
}
@Test
public void testPreparedStatementsCountWithoutUse()
{
int n = QueryProcessor.metrics.preparedStatementsCount.getValue();
long useCountBefore = QueryProcessor.metrics.useStatementsExecuted.getCount();
session.execute("use junit");
Assert.assertEquals(useCountBefore + 1, QueryProcessor.metrics.useStatementsExecuted.getCount());
session.prepare("SELECT * FROM junit.metricstest WHERE id = ?");
assertEquals(n+2, (int) QueryProcessor.metrics.preparedStatementsCount.getValue());
session.prepare("SELECT * FROM junit.metricstest WHERE id = 101");
Assert.assertEquals(useCountBefore, QueryProcessor.metrics.useStatementsExecuted.getCount());
assertEquals(n+1, (int) QueryProcessor.metrics.preparedStatementsCount.getValue());
}
assertRowsContains(cluster, session.execute("SELECT * FROM system_metrics.cql_group"),
row("org.apache.cassandra.metrics.CQL.PreparedStatementsCount", METRIC_SCOPE_UNDEFINED, "gauge",
String.valueOf(QueryProcessor.metrics.preparedStatementsCount.getValue())));
@Test
public void testPreparedStatementCacheMemoryUsed()
{
long n = QueryProcessor.metrics.preparedStatementsCacheSize.getValue();
String rawQueryString = "SELECT id, val FROM junit.metricstest WHERE id = 100";
PreparedStatement s = session.prepare(rawQueryString);
// Compute the expected size of a prepared statement.
QueryHandler.Prepared expected = QueryProcessor.parseAndPrepare(rawQueryString, ClientState.forInternalCalls(), false);
MD5Digest hashValue = MD5Digest.compute(rawQueryString);
long expectedHashSize = hashValue.size();
long expectedStatementSize = expected.pstmntSize;
long expectedSize = expectedHashSize + expectedStatementSize;
long actualSize = QueryProcessor.metrics.preparedStatementsCacheSize.getValue() - n;
assertEquals(expectedSize, actualSize);
}
@Test