This is an automated email from the ASF dual-hosted git repository.

maedhroz pushed a commit to branch cassandra-5.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/cassandra-5.0 by this push:
     new 79630fb42a Add selected SAI index state and query performance metrics 
to nodetool tablestats
79630fb42a is described below

commit 79630fb42ae9e42691f516590100c279f372ac89
Author: Sunil Ramchandra Pawar <[email protected]>
AuthorDate: Thu Jan 16 20:43:25 2025 +0530

    Add selected SAI index state and query performance metrics to nodetool 
tablestats
    
    patch by Sunil Ramchandra Pawar; reviewed by Caleb Rackliffe, Matt Byrd, 
and Maxim Muzafarov for CASSANDRA-20026
---
 CHANGES.txt                                        |  1 +
 .../index/sai/metrics/IndexGroupMetrics.java       |  3 +-
 .../index/sai/metrics/TableQueryMetrics.java       |  4 +-
 src/java/org/apache/cassandra/tools/NodeProbe.java | 71 +++++++++++++++++
 .../cassandra/tools/nodetool/TableStats.java       |  6 +-
 .../cassandra/tools/nodetool/stats/StatsTable.java | 10 +++
 .../tools/nodetool/stats/StatsTableComparator.java | 46 ++++++++++-
 .../tools/nodetool/stats/TableStatsHolder.java     | 59 +++++++++++++++
 .../tools/nodetool/stats/TableStatsPrinter.java    | 14 ++++
 .../cassandra/tools/nodetool/TableStatsTest.java   |  6 +-
 .../nodetool/stats/TableStatsPrinterTest.java      | 88 ++++++++++++++--------
 .../tools/nodetool/stats/TableStatsTestBase.java   | 24 ++++++
 12 files changed, 294 insertions(+), 38 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index feae546032..85c50621d1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 5.0.3
+ * Add selected SAI index state and query performance metrics to nodetool 
tablestats (CASSANDRA-20026)
  * Remove v30 and v3X from 5.x in-JVM upgrade tests (CASSANDRA-20103)
  * Avoid memory allocation in offheap_object's NativeCell.valueSize() and 
NativeClustering.dataSize() (CASSANDRA-20162)
  * Add flag to avoid invalidating key cache on sstable deletions 
(CASSANDRA-20068)
diff --git 
a/src/java/org/apache/cassandra/index/sai/metrics/IndexGroupMetrics.java 
b/src/java/org/apache/cassandra/index/sai/metrics/IndexGroupMetrics.java
index f6545760b7..fff6291ab0 100644
--- a/src/java/org/apache/cassandra/index/sai/metrics/IndexGroupMetrics.java
+++ b/src/java/org/apache/cassandra/index/sai/metrics/IndexGroupMetrics.java
@@ -25,9 +25,10 @@ import static 
org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
 
 public class IndexGroupMetrics extends AbstractMetrics
 {
+    public static final String INDEX_GROUP_METRICS_TYPE = "IndexGroupMetrics";
     public IndexGroupMetrics(TableMetadata table, StorageAttachedIndexGroup 
group)
     {
-        super(table.keyspace, table.name, "IndexGroupMetrics");
+        super(table.keyspace, table.name, INDEX_GROUP_METRICS_TYPE);
 
         Metrics.register(createMetricName("OpenIndexFiles"), (Gauge<Integer>) 
group::openIndexFiles);
         Metrics.register(createMetricName("DiskUsedBytes"), (Gauge<Long>) 
group::diskUsage);
diff --git 
a/src/java/org/apache/cassandra/index/sai/metrics/TableQueryMetrics.java 
b/src/java/org/apache/cassandra/index/sai/metrics/TableQueryMetrics.java
index 987c70ef75..bbfbe1d287 100644
--- a/src/java/org/apache/cassandra/index/sai/metrics/TableQueryMetrics.java
+++ b/src/java/org/apache/cassandra/index/sai/metrics/TableQueryMetrics.java
@@ -71,6 +71,8 @@ public class TableQueryMetrics extends AbstractMetrics
 
     public class PerQueryMetrics extends AbstractMetrics
     {
+        public static final String PER_QUERY_METRICS_TYPE = "PerQuery";
+
         private final Timer queryLatency;
 
         /**
@@ -99,7 +101,7 @@ public class TableQueryMetrics extends AbstractMetrics
 
         public PerQueryMetrics(TableMetadata table)
         {
-            super(table.keyspace, table.name, "PerQuery");
+            super(table.keyspace, table.name, PER_QUERY_METRICS_TYPE);
 
             queryLatency = Metrics.timer(createMetricName("QueryLatency"));
 
diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java 
b/src/java/org/apache/cassandra/tools/NodeProbe.java
index 93cb38128a..a917793ecc 100644
--- a/src/java/org/apache/cassandra/tools/NodeProbe.java
+++ b/src/java/org/apache/cassandra/tools/NodeProbe.java
@@ -90,6 +90,9 @@ import org.apache.cassandra.gms.Gossiper;
 import org.apache.cassandra.gms.GossiperMBean;
 import org.apache.cassandra.hints.HintsService;
 import org.apache.cassandra.hints.HintsServiceMBean;
+import org.apache.cassandra.index.sai.metrics.IndexGroupMetrics;
+import org.apache.cassandra.index.sai.metrics.TableQueryMetrics;
+import org.apache.cassandra.index.sai.metrics.TableStateMetrics;
 import org.apache.cassandra.locator.DynamicEndpointSnitchMBean;
 import org.apache.cassandra.locator.EndpointSnitchInfoMBean;
 import org.apache.cassandra.metrics.CIDRAuthorizerMetrics;
@@ -1867,6 +1870,74 @@ public class NodeProbe implements AutoCloseable
       }
     }
 
+    public Object getSaiMetric(String ks, String cf, String metricName)
+    {
+        try
+        {
+            String scope = getSaiMetricScope(metricName);
+            String objectNameStr = 
String.format("org.apache.cassandra.metrics:type=StorageAttachedIndex,keyspace=%s,table=%s,scope=%s,name=%s",ks,
 cf, scope, metricName);
+            ObjectName oName = new ObjectName(objectNameStr);
+
+            Set<ObjectName> matchingMBeans = mbeanServerConn.queryNames(oName, 
null);
+            if (matchingMBeans.isEmpty())
+                return null;
+
+            return getSaiMetricValue(metricName, oName);
+        }
+        catch (MalformedObjectNameException e)
+        {
+            throw new RuntimeException("Invalid ObjectName format: " + 
e.getMessage(), e);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Error accessing MBean server: " + 
e.getMessage(), e);
+        }
+    }
+
+    private Object getSaiMetricValue(String metricName, ObjectName oName) 
throws IOException
+    {
+        switch (metricName)
+        {
+            case "QueryLatency":
+                return JMX.newMBeanProxy(mbeanServerConn, oName, 
CassandraMetricsRegistry.JmxTimerMBean.class);
+            case "PostFilteringReadLatency":
+            case "SSTableIndexesHit":
+            case "IndexSegmentsHit":
+            case "RowsFiltered":
+                return JMX.newMBeanProxy(mbeanServerConn, oName, 
CassandraMetricsRegistry.JmxHistogramMBean.class);
+            case "DiskUsedBytes":
+            case "TotalIndexCount":
+            case "TotalQueryableIndexCount":
+                return JMX.newMBeanProxy(mbeanServerConn, oName, 
CassandraMetricsRegistry.JmxGaugeMBean.class).getValue();
+            case "TotalQueryTimeouts":
+                return JMX.newMBeanProxy(mbeanServerConn, oName, 
CassandraMetricsRegistry.JmxCounterMBean.class).getCount();
+            default:
+                throw new IllegalArgumentException("Unknown metric name: " + 
metricName);
+        }
+    }
+
+    private String getSaiMetricScope(String metricName)
+    {
+        switch (metricName)
+        {
+            case "QueryLatency":
+            case "SSTableIndexesHit":
+            case "IndexSegmentsHit":
+            case "RowsFiltered":
+                return 
TableQueryMetrics.PerQueryMetrics.PER_QUERY_METRICS_TYPE;
+            case "PostFilteringReadLatency":
+            case "TotalQueryTimeouts":
+                return TableQueryMetrics.TABLE_QUERY_METRIC_TYPE;
+            case "DiskUsedBytes":
+                return IndexGroupMetrics.INDEX_GROUP_METRICS_TYPE;
+            case "TotalIndexCount":
+            case "TotalQueryableIndexCount":
+                return TableStateMetrics.TABLE_STATE_METRIC_TYPE;
+            default:
+                throw new IllegalArgumentException("Unknown metric name: " + 
metricName);
+        }
+    }
+
     /**
      * Retrieve threadpool paths and names for threadpools with metrics.
      * @return Multimap from path (internal, request, etc.) to name
diff --git a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java 
b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
index fc641e7891..a347fd5f7d 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
@@ -60,8 +60,10 @@ public class TableStats extends NodeToolCmd
                         + "memtable_off_heap_memory_used, 
memtable_switch_count, number_of_partitions_estimate, "
                         + "off_heap_memory_used_total, pending_flushes, 
percent_repaired, read_latency, reads, "
                         + "space_used_by_snapshots_total, space_used_live, 
space_used_total, "
-                        + "sstable_compression_ratio, sstable_count, 
table_name, write_latency, writes, " +
-                          "max_sstable_size, local_read_write_ratio, 
twcs_max_duration)")
+                        + "sstable_compression_ratio, sstable_count, 
table_name, write_latency, writes, "
+                        + "max_sstable_size, local_read_write_ratio, 
twcs_max_duration, sai_local_query_latency_ms,"
+                        + "sai_post_filtering_read_latency, 
sai_disk_used_bytes, sai_sstable_indexes_hit, sai_index_segments_hit "
+                        + "sai_rows_filtered, sai_total_query_timeouts, 
sai_total_queryable_index_ratio)")
     private String sortKey = "";
 
     @Option(title = "top",
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java 
b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
index 680ebf649a..9272babf4b 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
@@ -82,4 +82,14 @@ public class StatsTable
     public double localReadWriteRatio;
     public Long twcsDurationInMillis;
     public String twcs;
+
+    public double saiQueryLatencyMs;
+    public double saiPostFilteringReadLatencyMs;
+    public String saiDiskUsedBytes;
+    public double saiSSTableIndexesHit;
+    public double saiIndexSegmentsHit;
+    public double saiRowsFiltered;
+    public long saiTotalQueryTimeouts;
+    public int saiTotalIndexCount;
+    public String saiTotalQueryableIndexRatio;
 }
diff --git 
a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTableComparator.java 
b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTableComparator.java
index c668829cdc..98d38120cc 100644
--- 
a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTableComparator.java
+++ 
b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTableComparator.java
@@ -66,7 +66,10 @@ public class StatsTableComparator implements 
Comparator<StatsTable>
                                                        
"space_used_by_snapshots_total", "space_used_live",
                                                        "space_used_total", 
"sstable_compression_ratio", "sstable_count",
                                                        "table_name", 
"write_latency", "writes", "max_sstable_size",
-                                                       
"local_read_write_ratio", "twcs_max_duration"};
+                                                       
"local_read_write_ratio", "twcs_max_duration", "sai_local_query_latency_ms",
+                                                       
"sai_post_filtering_read_latency","sai_disk_used_bytes","sai_sstable_indexes_hit",
+                                                       
"sai_index_segments_hit","sai_rows_filtered","sai_total_query_timeouts",
+                                                       
"sai_total_queryable_index_ratio"};
 
     public StatsTableComparator(String sortKey, boolean humanReadable)
     {
@@ -338,6 +341,47 @@ public class StatsTableComparator implements 
Comparator<StatsTable>
         {
             return sign * stx.tableName.compareTo(sty.tableName);
         }
+        else if(sortKey.equals("sai_local_query_latency_ms"))
+        {
+            result = compareDoubles(stx.saiQueryLatencyMs, 
sty.saiQueryLatencyMs);
+        }
+        else if(sortKey.equals("sai_post_filtering_read_latency"))
+        {
+            result = compareDoubles(stx.saiPostFilteringReadLatencyMs, 
sty.saiPostFilteringReadLatencyMs);
+        }
+        else if(sortKey.equals("sai_disk_used_bytes"))
+        {
+            result = compareFileSizes(stx.saiDiskUsedBytes,
+                                      sty.saiDiskUsedBytes);
+        }
+        else if(sortKey.equals("sai_sstable_indexes_hit"))
+        {
+            result = compareDoubles(stx.saiSSTableIndexesHit, 
sty.saiSSTableIndexesHit);
+        }
+        else if(sortKey.equals("sai_index_segments_hit"))
+        {
+            result = compareDoubles(stx.saiIndexSegmentsHit, 
sty.saiIndexSegmentsHit);
+        }
+        else if(sortKey.equals("sai_rows_filtered"))
+        {
+            result = compareDoubles(stx.saiRowsFiltered, sty.saiRowsFiltered);
+        }
+        else if(sortKey.equals("sai_total_query_timeouts"))
+        {
+            result = sign * Long.valueOf(stx.saiTotalQueryTimeouts)
+                                
.compareTo(Long.valueOf(sty.saiTotalQueryTimeouts));
+        }
+        else if(sortKey.equals("sai_total_queryable_index_ratio"))
+        {
+            if (stx.saiTotalQueryableIndexRatio == null && 
sty.saiTotalQueryableIndexRatio != null)
+                return sign * -1;
+            else if (stx.saiTotalQueryableIndexRatio != null && 
sty.saiTotalQueryableIndexRatio == null)
+                return sign;
+            else if (stx.saiTotalQueryableIndexRatio == null && 
sty.saiTotalQueryableIndexRatio == null)
+                return 0;
+
+            result = sign * 
stx.saiTotalQueryableIndexRatio.compareTo(sty.saiTotalQueryableIndexRatio);
+        }
         else
         {
             throw new IllegalStateException(String.format("Unsupported sort 
key: %s", sortKey));
diff --git 
a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java 
b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
index af5190e647..a225ccb88c 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
@@ -33,6 +33,7 @@ import 
org.apache.cassandra.db.compaction.TimeWindowCompactionStrategy;
 import org.apache.cassandra.db.compaction.TimeWindowCompactionStrategyOptions;
 import org.apache.cassandra.io.util.*;
 import org.apache.cassandra.metrics.*;
+import org.apache.cassandra.schema.SchemaConstants;
 import org.apache.cassandra.tools.*;
 
 public class TableStatsHolder implements StatsHolder
@@ -173,6 +174,16 @@ public class TableStatsHolder implements StatsHolder
         mpTable.put("top_tombstone_partitions", table.topTombstonePartitions);
         if (locationCheck)
             mpTable.put("sstables_in_correct_location", 
table.isInCorrectLocation);
+
+        mpTable.put("sai_local_query_latency_ms",String.format("%01.3f", 
table.saiQueryLatencyMs));
+        mpTable.put("sai_post_filtering_read_latency",String.format("%01.3f", 
table.saiPostFilteringReadLatencyMs));
+        mpTable.put("sai_disk_used_bytes",table.saiDiskUsedBytes);
+        mpTable.put("sai_sstable_indexes_hit",table.saiSSTableIndexesHit);
+        mpTable.put("sai_index_segments_hit",table.saiIndexSegmentsHit);
+        mpTable.put("sai_rows_filtered",table.saiRowsFiltered);
+        mpTable.put("sai_total_query_timeouts",table.saiTotalQueryTimeouts);
+        mpTable.put("sai_total_queryable_index_ratio", 
table.saiTotalQueryableIndexRatio);
+
         return mpTable;
     }
 
@@ -385,12 +396,60 @@ public class TableStatsHolder implements StatsHolder
                 if (table.getTopTombstonePartitionsLastUpdate() != null)
                     statsTable.topTombstonePartitionsLastUpdate = 
millisToDateString(table.getTopTombstonePartitionsLastUpdate());
 
+                if (!SchemaConstants.isSystemKeyspace(keyspaceName))
+                {
+                    Object totalIndexCount = probe.getSaiMetric(keyspaceName, 
tableName, "TotalIndexCount");
+                    statsTable.saiTotalIndexCount = (totalIndexCount != null) 
? (int) totalIndexCount : 0;
+
+                    if (statsTable.saiTotalIndexCount > 0)
+                    {
+                        Object queryLatencyMetric = 
probe.getSaiMetric(keyspaceName, tableName, "QueryLatency");
+                        double queryLatency = 
getMetricMean(queryLatencyMetric);
+                        statsTable.saiQueryLatencyMs = queryLatency > 0 ? 
queryLatency : Double.NaN;
+
+                        Object PostFilteringReadLatency = 
probe.getSaiMetric(keyspaceName, tableName, "PostFilteringReadLatency");
+                        double postfilteringreadlatency = 
getMetricMean(PostFilteringReadLatency);
+                        statsTable.saiPostFilteringReadLatencyMs = 
postfilteringreadlatency > 0 ? postfilteringreadlatency : Double.NaN;
+
+                        Object diskUsedBytes = 
probe.getSaiMetric(keyspaceName, tableName, "DiskUsedBytes");
+                        long saidiskusedbytes = (diskUsedBytes != null) ? 
(long) diskUsedBytes : 0L;
+                        statsTable.saiDiskUsedBytes = format(saidiskusedbytes, 
humanReadable);
+
+                        Object SSTableIndexesHit = 
probe.getSaiMetric(keyspaceName, tableName, "SSTableIndexesHit");
+                        statsTable.saiSSTableIndexesHit = 
getMetricMean(SSTableIndexesHit);
+
+                        Object IndexSegmentsHit = 
probe.getSaiMetric(keyspaceName, tableName, "IndexSegmentsHit");
+                        statsTable.saiIndexSegmentsHit = 
getMetricMean(IndexSegmentsHit);
+
+                        Object RowsFiltered = probe.getSaiMetric(keyspaceName, 
tableName, "RowsFiltered");
+                        statsTable.saiRowsFiltered = 
getMetricMean(RowsFiltered);
+
+                        Object totalQueryTimeouts = 
probe.getSaiMetric(keyspaceName, tableName, "TotalQueryTimeouts");
+                        statsTable.saiTotalQueryTimeouts = (totalQueryTimeouts 
!= null) ? (Long) totalQueryTimeouts : 0L;
+
+                        Object totalQueryableIndexCount = 
probe.getSaiMetric(keyspaceName, tableName, "TotalQueryableIndexCount");
+                        int saiTotalQueryableIndexCount = 
(totalQueryableIndexCount != null) ? (int) totalQueryableIndexCount : 0;
+
+                        statsTable.saiTotalQueryableIndexRatio = 
String.format("%d/%d", saiTotalQueryableIndexCount, 
statsTable.saiTotalIndexCount);
+                    }
+                }
+
                 statsKeyspace.tables.add(statsTable);
             }
             keyspaces.add(statsKeyspace);
         }
     }
 
+    private double getMetricMean(Object metricObject) {
+        if (metricObject instanceof CassandraMetricsRegistry.JmxTimerMBean) {
+            return ((CassandraMetricsRegistry.JmxTimerMBean) 
metricObject).getMean() / 1000;
+        }
+        if (metricObject instanceof 
CassandraMetricsRegistry.JmxHistogramMBean) {
+            return Math.round(((CassandraMetricsRegistry.JmxHistogramMBean) 
metricObject).getMean() * 100.0) / 100.0;
+        }
+        throw new IllegalArgumentException("Unsupported metric object type: " 
+ metricObject.getClass().getName());
+    }
+
     private void maybeAddTWCSWindowWithMaxDuration(StatsTable statsTable, 
NodeProbe probe, String keyspaceName, String tableName)
     {
         Map<String, String> compactionParameters = 
probe.getCfsProxy(statsTable.keyspaceName, statsTable.tableName)
diff --git 
a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java 
b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
index 02c0787a15..eb272d38dc 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
@@ -22,6 +22,7 @@ import java.io.PrintStream;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.cassandra.schema.SchemaConstants;
 import org.apache.cassandra.utils.FBUtilities;
 
 public class TableStatsPrinter<T extends StatsHolder>
@@ -163,6 +164,19 @@ public class TableStatsPrinter<T extends StatsHolder>
                 for (Map.Entry<String, Long> tombstonecnt : 
table.topTombstonePartitions.entrySet())
                     out.printf(indent + "  %-" + maxWidth + "s %s%n", 
tombstonecnt.getKey(), tombstonecnt.getValue());
             }
+
+            if (!SchemaConstants.isSystemKeyspace(table.keyspaceName) && 
table.saiTotalIndexCount > 0)
+            {
+                out.println(indent + "SAI local query latency (mean): " + 
String.format("%.3f ms", table.saiQueryLatencyMs));
+                out.println(indent + "SAI post-filtering latency (mean): " + 
String.format("%.3f ms",table.saiPostFilteringReadLatencyMs));
+                out.println(indent + "SAI space used (bytes): " + 
table.saiDiskUsedBytes);
+                out.println(indent + "SAI sstable indexes hit per query 
(mean): " + table.saiSSTableIndexesHit);
+                out.println(indent + "SAI index segments hit per query (mean): 
" + table.saiIndexSegmentsHit);
+                out.println(indent + "SAI rows filtered per query (mean): " + 
table.saiRowsFiltered);
+                out.println(indent + "SAI local query timeouts: " + 
table.saiTotalQueryTimeouts);
+                out.println(indent + "SAI queryable/total indexes: " + 
table.saiTotalQueryableIndexRatio);
+            }
+
             out.println("");
         }
     }
diff --git a/test/unit/org/apache/cassandra/tools/nodetool/TableStatsTest.java 
b/test/unit/org/apache/cassandra/tools/nodetool/TableStatsTest.java
index b4e9b90f4a..bfd7b8bee4 100644
--- a/test/unit/org/apache/cassandra/tools/nodetool/TableStatsTest.java
+++ b/test/unit/org/apache/cassandra/tools/nodetool/TableStatsTest.java
@@ -112,7 +112,11 @@ public class TableStatsTest extends CQLTester
                         "            read_latency, reads, 
space_used_by_snapshots_total, space_used_live,\n" + 
                         "            space_used_total, 
sstable_compression_ratio, sstable_count,\n" + 
                         "            table_name, write_latency, writes, 
max_sstable_size,\n" +
-                        "            local_read_write_ratio, 
twcs_max_duration)\n" +
+                        "            local_read_write_ratio, 
twcs_max_duration,\n" +
+                        "            
sai_local_query_latency_ms,sai_post_filtering_read_latency,\n" +
+                        "            sai_disk_used_bytes, 
sai_sstable_indexes_hit, sai_index_segments_hit\n" +
+                        "            sai_rows_filtered, 
sai_total_query_timeouts,\n" +
+                        "            sai_total_queryable_index_ratio)\n" +
                         "\n" + 
                         "        -t <top>, --top <top>\n" + 
                         "            Show only the top K tables for the sort 
key (specify the number K of\n" + 
diff --git 
a/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
 
b/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
index 80dcb97642..427c5e2a40 100644
--- 
a/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
+++ 
b/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
@@ -228,6 +228,14 @@ public class TableStatsPrinterTest extends 
TableStatsTestBase
         "\tAverage tombstones per slice (last five minutes): 4.01\n" +
         "\tMaximum tombstones per slice (last five minutes): 5\n" +
         "\tDroppable tombstone ratio: 0.55556\n" +
+        "\tSAI local query latency (mean): 10.000 ms\n" +
+        "\tSAI post-filtering latency (mean): 1.000 ms\n" +
+        "\tSAI space used (bytes): 40 bytes\n" +
+        "\tSAI sstable indexes hit per query (mean): 3.5\n" +
+        "\tSAI index segments hit per query (mean): 4.0\n" +
+        "\tSAI rows filtered per query (mean): 55.0\n" +
+        "\tSAI local query timeouts: 4\n" +
+        "\tSAI queryable/total indexes: 5/5\n" +
         "\n";
 
     public static final String expectedDefaultTable6Output =
@@ -412,48 +420,56 @@ public class TableStatsPrinterTest extends 
TableStatsTestBase
                                  "    \"write_latency_ms\" : null,\n" +
                                  "    \"tables\" : {\n" +
                                  "      \"table6\" : {\n" +
+                                 "        \"twcs\" : null,\n" +
+                                 "        \"sai_total_queryable_index_ratio\" 
: \"0/0\",\n" +
+                                 "        \"memtable_switch_count\" : 6,\n" +
+                                 "        \"sai_post_filtering_read_latency\" 
: \"NaN\",\n" +
+                                 "        \"speculative_retries\" : 0,\n" +
+                                 "        
\"average_live_cells_per_slice_last_five_minutes\" : 5.0,\n" +
+                                 "        \"local_read_latency_ms\" : 
\"1.000\",\n" +
+                                 "        \"sstable_count\" : 1000,\n" +
+                                 "        
\"compacted_partition_minimum_bytes\" : 6,\n" +
+                                 "        \"sstable_compression_ratio\" : 
0.68,\n" +
+                                 "        \"max_sstable_size\" : 0,\n" +
+                                 "        \"sai_index_segments_hit\" : 
\"NaN\",\n" +
+                                 "        \"memtable_off_heap_memory_used\" : 
\"161803398\",\n" +
+                                 "        \"bloom_filter_space_used\" : 
\"101112\",\n" +
+                                 "        
\"compacted_partition_maximum_bytes\" : 20,\n" +
+                                 "        \"sai_rows_filtered\" : \"NaN\",\n" +
+                                 "        \"droppable_tombstone_ratio\" : 
\"0.66667\",\n" +
+                                 "        
\"compression_metadata_off_heap_memory_used\" : \"1\",\n" +
+                                 "        \"bloom_filter_false_ratio\" : 
\"0.03000\",\n" +
+                                 "        \"percent_repaired\" : 0.0,\n" +
                                  "        
\"average_tombstones_per_slice_last_five_minutes\" : 6.0,\n" +
                                  "        \"top_tombstone_partitions\" : 
null,\n" +
+                                 "        \"sai_total_query_timeouts\" : 0,\n" 
+
                                  "        
\"bloom_filter_off_heap_memory_used\" : \"667408\",\n" +
-                                 "        \"twcs\" : null,\n" +
                                  "        \"bytes_pending_repair\" : 0,\n" +
-                                 "        \"memtable_switch_count\" : 6,\n" +
-                                 "        \"speculative_retries\" : 0,\n" +
                                  "        
\"maximum_tombstones_per_slice_last_five_minutes\" : 6,\n" +
                                  "        \"memtable_cell_count\" : 6666,\n" +
                                  "        \"memtable_data_size\" : 
\"1000000\",\n" +
-                                 "        
\"average_live_cells_per_slice_last_five_minutes\" : 5.0,\n" +
-                                 "        \"local_read_latency_ms\" : 
\"1.000\",\n" +
-                                 "        \"sstable_count\" : 1000,\n" +
                                  "        \"local_write_latency_ms\" : 
\"0.500\",\n" +
                                  "        \"pending_flushes\" : 66,\n" +
-                                 "        
\"compacted_partition_minimum_bytes\" : 6,\n" +
                                  "        \"local_read_count\" : 5,\n" +
-                                 "        \"sstable_compression_ratio\" : 
0.68,\n" +
-                                 "        \"max_sstable_size\" : 0,\n" +
                                  "        \"top_size_partitions\" : null,\n" +
                                  "        \"bloom_filter_false_positives\" : 
400,\n" +
                                  "        \"off_heap_memory_used_total\" : 
\"162470810\",\n" +
-                                 "        \"memtable_off_heap_memory_used\" : 
\"161803398\",\n" +
                                  "        
\"index_summary_off_heap_memory_used\" : \"3\",\n" +
-                                 "        \"bloom_filter_space_used\" : 
\"101112\",\n" +
+                                 "        \"sai_local_query_latency_ms\" : 
\"NaN\",\n" +
                                  "        \"sstables_in_each_level\" : [ ],\n" 
+
-                                 "        
\"compacted_partition_maximum_bytes\" : 20,\n" +
                                  "        \"sstable_bytes_in_each_level\" : [ 
],\n" +
                                  "        \"space_used_total\" : \"0\",\n" +
                                  "        \"local_write_count\" : 0,\n" +
-                                 "        \"droppable_tombstone_ratio\" : 
\"0.66667\",\n" +
-                                 "        
\"compression_metadata_off_heap_memory_used\" : \"1\",\n" +
+                                 "        \"sai_sstable_indexes_hit\" : 
\"NaN\",\n" +
                                  "        \"local_read_write_ratio\" : 
\"0.00000\",\n" +
                                  "        \"number_of_partitions_estimate\" : 
6,\n" +
+                                 "        \"sai_disk_used_bytes\" : 
\"NaN\",\n" +
                                  "        \"bytes_repaired\" : 0,\n" +
                                  "        
\"maximum_live_cells_per_slice_last_five_minutes\" : 2,\n" +
                                  "        \"space_used_live\" : \"666666\",\n" 
+
                                  "        \"compacted_partition_mean_bytes\" : 
3,\n" +
-                                 "        \"bloom_filter_false_ratio\" : 
\"0.03000\",\n" +
                                  "        \"old_sstable_count\" : 0,\n" +
                                  "        \"bytes_unrepaired\" : 0,\n" +
-                                 "        \"percent_repaired\" : 0.0,\n" +
                                  "        \"space_used_by_snapshots_total\" : 
\"0\"\n" +
                                  "      }\n" +
                                  "    },\n" +
@@ -481,48 +497,56 @@ public class TableStatsPrinterTest extends 
TableStatsTestBase
                                  "  write_latency_ms: .NaN\n" +
                                  "  tables:\n" +
                                  "    table6:\n" +
+                                 "      twcs: null\n" +
+                                 "      sai_total_queryable_index_ratio: 
0/0\n" +
+                                 "      memtable_switch_count: 6\n" +
+                                 "      sai_post_filtering_read_latency: 
NaN\n" +
+                                 "      speculative_retries: 0\n" +
+                                 "      
average_live_cells_per_slice_last_five_minutes: 5.0\n" +
+                                 "      local_read_latency_ms: '1.000'\n" +
+                                 "      sstable_count: 1000\n" +
+                                 "      compacted_partition_minimum_bytes: 
6\n" +
+                                 "      sstable_compression_ratio: 0.68\n" +
+                                 "      max_sstable_size: 0\n" +
+                                 "      sai_index_segments_hit: .NaN\n" +
+                                 "      memtable_off_heap_memory_used: 
'161803398'\n" +
+                                 "      bloom_filter_space_used: '101112'\n" +
+                                 "      compacted_partition_maximum_bytes: 
20\n" +
+                                 "      sai_rows_filtered: .NaN\n" +
+                                 "      droppable_tombstone_ratio: 
'0.66667'\n" +
+                                 "      
compression_metadata_off_heap_memory_used: '1'\n" +
+                                 "      bloom_filter_false_ratio: '0.03000'\n" 
+
+                                 "      percent_repaired: 0.0\n" +
                                  "      
average_tombstones_per_slice_last_five_minutes: 6.0\n" +
                                  "      top_tombstone_partitions: null\n" +
+                                 "      sai_total_query_timeouts: 0\n" +
                                  "      bloom_filter_off_heap_memory_used: 
'667408'\n" +
-                                 "      twcs: null\n" +
                                  "      bytes_pending_repair: 0\n" +
-                                 "      memtable_switch_count: 6\n" +
-                                 "      speculative_retries: 0\n" +
                                  "      
maximum_tombstones_per_slice_last_five_minutes: 6\n" +
                                  "      memtable_cell_count: 6666\n" +
                                  "      memtable_data_size: '1000000'\n" +
-                                 "      
average_live_cells_per_slice_last_five_minutes: 5.0\n" +
-                                 "      local_read_latency_ms: '1.000'\n" +
-                                 "      sstable_count: 1000\n" +
                                  "      local_write_latency_ms: '0.500'\n" +
                                  "      pending_flushes: 66\n" +
-                                 "      compacted_partition_minimum_bytes: 
6\n" +
                                  "      local_read_count: 5\n" +
-                                 "      sstable_compression_ratio: 0.68\n" +
-                                 "      max_sstable_size: 0\n" +
                                  "      top_size_partitions: null\n" +
                                  "      bloom_filter_false_positives: 400\n" +
                                  "      off_heap_memory_used_total: 
'162470810'\n" +
-                                 "      memtable_off_heap_memory_used: 
'161803398'\n" +
                                  "      index_summary_off_heap_memory_used: 
'3'\n" +
-                                 "      bloom_filter_space_used: '101112'\n" +
+                                 "      sai_local_query_latency_ms: NaN\n" +
                                  "      sstables_in_each_level: []\n" +
-                                 "      compacted_partition_maximum_bytes: 
20\n" +
                                  "      sstable_bytes_in_each_level: []\n" +
                                  "      space_used_total: '0'\n" +
                                  "      local_write_count: 0\n" +
-                                 "      droppable_tombstone_ratio: 
'0.66667'\n" +
-                                 "      
compression_metadata_off_heap_memory_used: '1'\n" +
+                                 "      sai_sstable_indexes_hit: .NaN\n" +
                                  "      local_read_write_ratio: '0.00000'\n" +
                                  "      number_of_partitions_estimate: 6\n" +
+                                 "      sai_disk_used_bytes: NaN\n" +
                                  "      bytes_repaired: 0\n" +
                                  "      
maximum_live_cells_per_slice_last_five_minutes: 2\n" +
                                  "      space_used_live: '666666'\n" +
                                  "      compacted_partition_mean_bytes: 3\n" +
-                                 "      bloom_filter_false_ratio: '0.03000'\n" 
+
                                  "      old_sstable_count: 0\n" +
                                  "      bytes_unrepaired: 0\n" +
-                                 "      percent_repaired: 0.0\n" +
                                  "      space_used_by_snapshots_total: '0'\n" +
                                  "  read_latency_ms: 0.0\n" +
                                  "  pending_flushes: 66\n" +
diff --git 
a/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsTestBase.java 
b/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsTestBase.java
index 317eff01f4..0e55e5a3d7 100644
--- 
a/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsTestBase.java
+++ 
b/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsTestBase.java
@@ -102,6 +102,14 @@ public class TableStatsTestBase
         template.maximumTombstonesPerSliceLastFiveMinutes = 0L;
         template.twcs = null;
         template.twcsDurationInMillis = 0L;
+        template.saiQueryLatencyMs = Double.NaN;
+        template.saiPostFilteringReadLatencyMs = Double.NaN;
+        template.saiDiskUsedBytes = "NaN";
+        template.saiSSTableIndexesHit = Double.NaN;
+        template.saiIndexSegmentsHit = Double.NaN;
+        template.saiRowsFiltered = Double.NaN;
+        template.saiTotalQueryTimeouts = 0L;
+        template.saiTotalQueryableIndexRatio = "0/0";
         return template;
     }
 
@@ -337,6 +345,18 @@ public class TableStatsTestBase
         table2.twcsDurationInMillis = 2000L;
         table4.twcsDurationInMillis = 1000L;
         table5.twcsDurationInMillis = null;
+
+        // Table5 with 5 SAI.
+        table5.saiQueryLatencyMs = 10.000D;
+        table5.saiPostFilteringReadLatencyMs = 1.000D;
+        table5.saiDiskUsedBytes = "40 bytes";
+        table5.saiSSTableIndexesHit = 3.5D;
+        table5.saiIndexSegmentsHit = 4.0D;
+        table5.saiRowsFiltered = 55.0D;
+        table5.saiTotalQueryTimeouts = 4L;
+        table5.saiTotalIndexCount = 5;
+        table5.saiTotalQueryableIndexRatio = "5/5";
+
         // create test keyspaces from templates
         testKeyspaces = new ArrayList<>();
         StatsKeyspace keyspace1 = createStatsKeyspaceTemplate("keyspace1");
@@ -395,6 +415,10 @@ public class TableStatsTestBase
         humanReadableTable4.memtableDataSize = "999 bytes";
         humanReadableTable5.memtableDataSize = "3.14 MiB";
         humanReadableTable6.memtableDataSize = "0 bytes";
+
+        // cretae human-readable SAI disk space used size:
+        humanReadableTable5.saiDiskUsedBytes = "40 bytes";
+
         // create human readable keyspaces from template
         humanReadableKeyspaces = new ArrayList<>();
         StatsKeyspace humanReadableKeyspace1 = 
createStatsKeyspaceTemplate("keyspace1");


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to