From bbff4f8d181b0a8d7440a85556d5ef0d7482b07f Mon Sep 17 00:00:00 2001 From: walcp1 Date: Sun, 19 Apr 2026 20:24:16 +0200 Subject: [PATCH] Add rowsMutatedPerWriteHistogram metric to track rows mutated per write request patch by Piotrek Walczak; reviewed by Dmitry Konstantinov, Stefan Miklosovic for CASSANDRA-21320 --- CHANGES.txt | 1 + .../cassandra/db/ColumnFamilyStore.java | 4 ++- .../db/virtual/TableMetricTables.java | 1 + .../cassandra/metrics/KeyspaceMetrics.java | 3 +++ .../cassandra/metrics/TableMetrics.java | 3 +++ .../test/metric/TableMetricTest.java | 2 +- .../cassandra/metrics/TableMetricsTest.java | 26 +++++++++++++++++++ 7 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f89d347673..b575217259 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 6.0-alpha2 + * Add rowsMutatedPerWriteHistogram metric to track rows mutated per write request (CASSANDRA-21320) * Add TotalRowsRead and TotalRowsMutated counters to TableMetrics for accurate per-table row throughput tracking (CASSANDRA-21321) * Move exception handling of SPI startup checks when iterating over them (CASSANDRA-21409) * Avoid type lookup in SerializationHeader#getType if schema and SSTable are aligned (CASSANDRA-21402) diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index 8bc2e917b8..c691d96834 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -1530,7 +1530,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner metric.topWritePartitionSize.addSample(key.getKey(), update.dataSize()); StorageHook.instance.reportWrite(metadata.id, update); metric.writeLatency.addNano(nanoTime() - start); - metric.totalRowsMutated.inc(update.affectedRowCount()); + int affectedRows = update.affectedRowCount(); + metric.totalRowsMutated.inc(affectedRows); + metric.rowsMutatedPerWriteHistogram.update(affectedRows); // CASSANDRA-11117 - certain resolution paths on memtable put can result in very // large time deltas, either through a variety of sentinel timestamps (used for empty values, ensuring // a minimal write, etc). This limits the time delta to the max value the histogram diff --git a/src/java/org/apache/cassandra/db/virtual/TableMetricTables.java b/src/java/org/apache/cassandra/db/virtual/TableMetricTables.java index 0159431afc..3651cfc50b 100644 --- a/src/java/org/apache/cassandra/db/virtual/TableMetricTables.java +++ b/src/java/org/apache/cassandra/db/virtual/TableMetricTables.java @@ -78,6 +78,7 @@ public class TableMetricTables new HistogramTableMetric(name, "tombstones_per_read", t -> t.tombstoneScannedHistogram.cf), new HistogramTableMetric(name, "purgeable_tombstones_per_read", t -> t.purgeableTombstoneScannedHistogram.cf), new HistogramTableMetric(name, "rows_per_read", t -> t.liveScannedHistogram.cf), + new HistogramTableMetric(name, "rows_per_write", t -> t.rowsMutatedPerWriteHistogram.cf), new StorageTableMetric(name, "disk_usage", (TableMetrics t) -> t.totalDiskSpaceUsed), new StorageTableMetric(name, "max_partition_size", (TableMetrics t) -> t.maxPartitionSize), new StorageTableMetric(name, "max_sstable_size", (TableMetrics t) -> t.maxSSTableSize), diff --git a/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java b/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java index 4491d64e8b..f5690bb7df 100644 --- a/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java +++ b/src/java/org/apache/cassandra/metrics/KeyspaceMetrics.java @@ -89,6 +89,8 @@ public class KeyspaceMetrics public final Histogram purgeableTombstoneScannedHistogram; /** Live cells scanned in queries on this Keyspace */ public final Histogram liveScannedHistogram; + /** Rows mutated in writes on this Keyspace */ + public final Histogram rowsMutatedPerWriteHistogram; /** Column update time delta on this Keyspace */ public final Histogram colUpdateTimeDeltaHistogram; /** time taken acquiring the partition lock for materialized view updates on this keyspace */ @@ -258,6 +260,7 @@ public class KeyspaceMetrics tombstoneScannedHistogram = createKeyspaceHistogram("TombstoneScannedHistogram", false); purgeableTombstoneScannedHistogram = createKeyspaceHistogram("PurgeableTombstoneScannedHistogram", false); liveScannedHistogram = createKeyspaceHistogram("LiveScannedHistogram", false); + rowsMutatedPerWriteHistogram = createKeyspaceHistogram("RowsMutatedPerWriteHistogram", false); colUpdateTimeDeltaHistogram = createKeyspaceHistogram("ColUpdateTimeDeltaHistogram", false); viewLockAcquireTime = createKeyspaceTimer("ViewLockAcquireTime"); viewReadTime = createKeyspaceTimer("ViewReadTime"); diff --git a/src/java/org/apache/cassandra/metrics/TableMetrics.java b/src/java/org/apache/cassandra/metrics/TableMetrics.java index 5330c47776..bff2c5cd40 100644 --- a/src/java/org/apache/cassandra/metrics/TableMetrics.java +++ b/src/java/org/apache/cassandra/metrics/TableMetrics.java @@ -170,6 +170,8 @@ public class TableMetrics public final Counter totalRowsRead; /** Total number of rows mutated in writes to this CF (cumulative counter, suitable for rate/windowed calculations) */ public final Counter totalRowsMutated; + /** Rows mutated in writes on this CF */ + public final TableHistogram rowsMutatedPerWriteHistogram; /** Column update time delta on this CF */ public final TableHistogram colUpdateTimeDeltaHistogram; /** time taken acquiring the partition lock for materialized view updates for this table */ @@ -818,6 +820,7 @@ public class TableMetrics liveScannedHistogram = createTableHistogram("LiveScannedHistogram", cfs.keyspace.metric.liveScannedHistogram, false); totalRowsRead = createTableCounter("TotalRowsRead"); totalRowsMutated = createTableCounter("TotalRowsMutated"); + rowsMutatedPerWriteHistogram = createTableHistogram("RowsMutatedPerWriteHistogram", cfs.keyspace.metric.rowsMutatedPerWriteHistogram, false); colUpdateTimeDeltaHistogram = createTableHistogram("ColUpdateTimeDeltaHistogram", cfs.keyspace.metric.colUpdateTimeDeltaHistogram, false); coordinatorReadLatency = createTableTimer("CoordinatorReadLatency"); coordinatorScanLatency = createTableTimer("CoordinatorScanLatency"); diff --git a/test/distributed/org/apache/cassandra/distributed/test/metric/TableMetricTest.java b/test/distributed/org/apache/cassandra/distributed/test/metric/TableMetricTest.java index cfb34e7696..f327bb1575 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/metric/TableMetricTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/metric/TableMetricTest.java @@ -62,7 +62,7 @@ public class TableMetricTest extends TestBaseImpl ORG_APACHE_CASSANDRA_DISABLE_MBEAN_REGISTRATION.setBoolean(false); } private static volatile Map> SYSTEM_TABLES = null; - private static Set TABLE_METRIC_NAMES = ImmutableSet.of("WriteLatency"); + private static Set TABLE_METRIC_NAMES = ImmutableSet.of("WriteLatency", "RowsMutatedPerWriteHistogram"); /** * Makes sure that all system tables have the expected metrics diff --git a/test/unit/org/apache/cassandra/metrics/TableMetricsTest.java b/test/unit/org/apache/cassandra/metrics/TableMetricsTest.java index 0025650940..69d529c334 100644 --- a/test/unit/org/apache/cassandra/metrics/TableMetricsTest.java +++ b/test/unit/org/apache/cassandra/metrics/TableMetricsTest.java @@ -269,6 +269,32 @@ public class TableMetricsTest String.valueOf(counter.getCount()))); } + @Test + public void testRowsMutatedPerWriteHistogram() + { + ColumnFamilyStore cfs = recreateTable(); + assertEquals(0, cfs.metric.rowsMutatedPerWriteHistogram.cf.getCount()); + + session.execute(String.format("INSERT INTO %s.%s (id, val1, val2) VALUES (1, 'a', 'b')", KEYSPACE, TABLE)); + session.execute(String.format("INSERT INTO %s.%s (id, val1, val2) VALUES (1, 'c', 'd')", KEYSPACE, TABLE)); + + assertEquals(2, cfs.metric.rowsMutatedPerWriteHistogram.cf.getCount()); + assertGreaterThan(cfs.metric.rowsMutatedPerWriteHistogram.cf.getSnapshot().getMax(), 0); + + String histogramMedian = Double.toString(cfs.metric.rowsMutatedPerWriteHistogram.cf.getSnapshot().getMedian()); + + assertRowsContains(cluster, session.execute("SELECT * FROM system_metrics.table_group"), + row("org.apache.cassandra.metrics.Table.RowsMutatedPerWriteHistogram.junit.tablemetricstest", + "junit.tablemetricstest", + "histogram", + histogramMedian)); + assertRowsContains(cluster, session.execute("SELECT * FROM system_metrics.column_family_group"), + row("org.apache.cassandra.metrics.ColumnFamily.RowsMutatedPerWriteHistogram.junit.tablemetricstest", + "junit.tablemetricstest", + "histogram", + histogramMedian)); + } + @Test public void testLoggedPartitionsPerBatch() {