Skip to content

Commit 37f1fa3

Browse files
author
Piotr Kołaczkowski
committed
CNDB-15508: More planner metrics (inspired by CNDB-15946)
1 parent d77d0aa commit 37f1fa3

File tree

4 files changed

+77
-16
lines changed

4 files changed

+77
-16
lines changed

src/java/org/apache/cassandra/index/sai/QueryContext.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,9 @@ public static class PlanInfo
320320
public final boolean searchExecutedBeforeOrder;
321321
public final boolean filterExecutedAfterOrderedScan;
322322

323-
public final double rowsEstimated;
323+
public final double rowsToReturnEstimated;
324+
public final double rowsToFetchEstimated;
325+
public final double keysToIterateEstimated;
324326
public final double selectivityEstimated;
325327
public final double costEstimated;
326328

@@ -330,7 +332,9 @@ public static class PlanInfo
330332
public PlanInfo(@Nonnull Plan.RowsIteration originalPlan, @Nonnull Plan.RowsIteration optimizedPlan)
331333
{
332334
this.costEstimated = optimizedPlan.fullCost();
333-
this.rowsEstimated = optimizedPlan.expectedRows();
335+
this.rowsToReturnEstimated = optimizedPlan.expectedRows();
336+
this.rowsToFetchEstimated = optimizedPlan.estimatedRowsToFetch();
337+
this.keysToIterateEstimated = optimizedPlan.estimatedKeysToIterate();
334338
this.selectivityEstimated = optimizedPlan.selectivity();
335339
this.indexReferencesInQuery = originalPlan.referencedIndexCount();
336340
this.indexReferencesInPlan = optimizedPlan.referencedIndexCount();

src/java/org/apache/cassandra/index/sai/metrics/TableQueryMetrics.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ public static class PerTable extends AbstractQueryMetrics
194194
public final Counter totalRowsFiltered;
195195
public final Counter totalQueriesCompleted;
196196

197-
public final Counter totalRowsEstimated;
197+
public final Counter totalRowsToReturnEstimated;
198+
public final Counter totalRowsToFetchEstimated;
199+
public final Counter totalKeysToIterateEstimated;
198200
public final Counter totalCostEstimated;
199201

200202
public final Counter sortThenFilterQueriesCompleted;
@@ -213,7 +215,9 @@ public PerTable(TableMetadata table, QueryKind queryKind, Predicate<ReadCommand>
213215
totalRowsFiltered = Metrics.counter(createMetricName("TotalRowsFiltered"));
214216
totalQueriesCompleted = Metrics.counter(createMetricName("TotalQueriesCompleted"));
215217
totalQueryTimeouts = Metrics.counter(createMetricName("TotalQueryTimeouts"));
216-
totalRowsEstimated = Metrics.counter(createMetricName("TotalRowsEstimated"));
218+
totalRowsToReturnEstimated = Metrics.counter(createMetricName("TotalRowsToReturnEstimated"));
219+
totalRowsToFetchEstimated = Metrics.counter(createMetricName("TotalRowsToFetchEstimated"));
220+
totalKeysToIterateEstimated = Metrics.counter(createMetricName("TotalKeysToIterateEstimated"));
217221
totalCostEstimated = Metrics.counter(createMetricName("TotalCostEstimated"));
218222

219223
sortThenFilterQueriesCompleted = Metrics.counter(createMetricName("SortThenFilterQueriesCompleted"));
@@ -237,7 +241,9 @@ public void record(QueryContext.Snapshot snapshot)
237241
if (queryPlanInfo != null)
238242
{
239243
totalCostEstimated.inc(Math.round(queryPlanInfo.costEstimated));
240-
totalRowsEstimated.inc(Math.round(queryPlanInfo.rowsEstimated));
244+
totalRowsToReturnEstimated.inc(Math.round(queryPlanInfo.rowsToReturnEstimated));
245+
totalRowsToFetchEstimated.inc(Math.round(queryPlanInfo.rowsToFetchEstimated));
246+
totalKeysToIterateEstimated.inc(Math.round(queryPlanInfo.keysToIterateEstimated));
241247

242248
if (queryPlanInfo.filterExecutedAfterOrderedScan)
243249
sortThenFilterQueriesCompleted.inc();
@@ -291,8 +297,14 @@ public static class PerQuery extends AbstractQueryMetrics
291297
/** Query execution cost as estimated by the planner */
292298
public final Histogram costEstimated;
293299

294-
/** Number of rows returned by the query estimated by the planner */
295-
public final Histogram rowsEstimated;
300+
/** Number of rows to be returned from the query as estimated by the planner */
301+
public final Histogram rowsToReturnEstimated;
302+
303+
/** Number of rows to be fetched by the query as estimated by the planner */
304+
public final Histogram rowsToFetchEstimated;
305+
306+
/** Number of rows to be fetched by the query as estimated by the planner */
307+
public final Histogram keysToIterateEstimated;
296308

297309
/**
298310
* Negative deceimal logarithm of selectivity of the query, before applying the LIMIT clause.
@@ -340,7 +352,9 @@ public PerQuery(TableMetadata table, QueryKind queryKind, Predicate<ReadCommand>
340352
annGraphSearchLatency = Metrics.timer(createMetricName("ANNGraphSearchLatency"));
341353

342354
costEstimated = Metrics.histogram(createMetricName("CostEstimated"), false);
343-
rowsEstimated = Metrics.histogram(createMetricName("RowsEstimated"), true);
355+
rowsToReturnEstimated = Metrics.histogram(createMetricName("RowsToReturnEstimated"), true);
356+
rowsToFetchEstimated = Metrics.histogram(createMetricName("RowsToFetchEstimated"), true);
357+
keysToIterateEstimated = Metrics.histogram(createMetricName("KeysToIterateEstimated"), true);
344358
logSelectivityEstimated = Metrics.histogram(createMetricName("LogSelectivityEstimated"), true);
345359
indexReferencesInPlan = Metrics.histogram(createMetricName("IndexReferencesInPlan"), true);
346360
indexReferencesInQuery = Metrics.histogram(createMetricName("IndexReferencesInQuery"), false);
@@ -385,7 +399,9 @@ public void record(QueryContext.Snapshot snapshot)
385399
if (queryPlanInfo != null)
386400
{
387401
costEstimated.update(Math.round(queryPlanInfo.costEstimated));
388-
rowsEstimated.update(Math.round(queryPlanInfo.rowsEstimated));
402+
rowsToReturnEstimated.update(Math.round(queryPlanInfo.rowsToReturnEstimated));
403+
rowsToFetchEstimated.update(Math.round(queryPlanInfo.rowsToFetchEstimated));
404+
keysToIterateEstimated.update(Math.round(queryPlanInfo.keysToIterateEstimated));
389405
double logSelectivity = -Math.log10(queryPlanInfo.selectivityEstimated);
390406
logSelectivityEstimated.update((int) (Math.min(20, Math.floor(logSelectivity))));
391407
indexReferencesInQuery.update(queryPlanInfo.indexReferencesInQuery);

src/java/org/apache/cassandra/index/sai/plan/Plan.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import com.google.common.annotations.VisibleForTesting;
3030
import com.google.common.base.Preconditions;
31+
import org.apache.commons.lang3.mutable.MutableDouble;
3132
import org.apache.commons.lang3.mutable.MutableInt;
3233
import org.slf4j.Logger;
3334
import org.slf4j.LoggerFactory;
@@ -217,7 +218,7 @@ final <T extends Plan> List<T> nodesOfType(Class<T> nodeType)
217218
* If node of given type is not found, returns null.
218219
*/
219220
@SuppressWarnings("unchecked")
220-
final <T extends Plan> @Nullable T firstNodeOfType(Class<T> nodeType)
221+
public final <T extends Plan> @Nullable T firstNodeOfType(Class<T> nodeType)
221222
{
222223
Plan[] result = new Plan[] { null };
223224
forEach(node -> {
@@ -538,6 +539,32 @@ public final int referencedIndexCount()
538539
return count.intValue();
539540
}
540541

542+
/**
543+
* Returns the estimated number of rows to be fetched from storage.
544+
*/
545+
public final double estimatedRowsToFetch()
546+
{
547+
Fetch fetch = firstNodeOfType(Plan.Fetch.class);
548+
return fetch != null ? fetch.expectedRows() : 0.0;
549+
}
550+
551+
/**
552+
* Returns the estimated number of primary keys to be iterated by all index iterators.
553+
* This may be larger than the number of rows to fetch because of intersections.
554+
*/
555+
public final double estimatedKeysToIterate()
556+
{
557+
MutableDouble total = new MutableDouble(0.0);
558+
forEach(node -> {
559+
if (node instanceof IndexScan)
560+
{
561+
total.add(((IndexScan) node).expectedKeys());
562+
}
563+
return ControlFlow.Continue;
564+
});
565+
return total.doubleValue();
566+
}
567+
541568
protected interface Cost
542569
{
543570
/**

test/unit/org/apache/cassandra/index/sai/metrics/QueryMetricsTest.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -612,9 +612,17 @@ public void testQueryPlannerMetrics()
612612
UntypedResultSet rows = execute("SELECT k FROM %s WHERE lc = 0");
613613
assertEquals(numRows / 2, rows.size());
614614

615-
var rowsEstimatedMetric = objectNameNoIndex("RowsEstimated", KEYSPACE, table, PER_QUERY_METRIC_TYPE);
616-
waitForHistogramCountEquals(rowsEstimatedMetric, 1);
617-
waitForHistogramMeanBetween(rowsEstimatedMetric, numRows / 2.0 * 0.75, numRows / 2.0 * 1.25);
615+
var rowsToReturnEstimatedMetric = objectNameNoIndex("RowsToReturnEstimated", KEYSPACE, table, PER_QUERY_METRIC_TYPE);
616+
waitForHistogramCountEquals(rowsToReturnEstimatedMetric, 1);
617+
waitForHistogramMeanBetween(rowsToReturnEstimatedMetric, numRows / 2.0 * 0.75, numRows / 2.0 * 1.25);
618+
619+
var rowsToFetchEstimatedMetric = objectNameNoIndex("RowsToFetchEstimated", KEYSPACE, table, PER_QUERY_METRIC_TYPE);
620+
waitForHistogramCountEquals(rowsToFetchEstimatedMetric, 1);
621+
waitForHistogramMeanBetween(rowsToFetchEstimatedMetric, numRows / 2.0 * 0.75, numRows / 2.0 * 1.25);
622+
623+
var keysToIterateEstimatedMetric = objectNameNoIndex("KeysToIterateEstimated", KEYSPACE, table, PER_QUERY_METRIC_TYPE);
624+
waitForHistogramCountEquals(keysToIterateEstimatedMetric, 1);
625+
waitForHistogramMeanBetween(keysToIterateEstimatedMetric, numRows / 2.0 * 0.75, numRows / 2.0 * 1.25);
618626

619627
var objectName = objectNameNoIndex("CostEstimated", KEYSPACE, table, PER_QUERY_METRIC_TYPE);
620628
waitForHistogramCountEquals(objectName, 1);
@@ -632,7 +640,13 @@ public void testQueryPlannerMetrics()
632640
waitForHistogramCountEquals(objectName, 1);
633641
waitForHistogramMeanBetween(objectName, 1.0, 1.0);
634642

635-
objectName = objectNameNoIndex("TotalRowsEstimated", KEYSPACE, table, TABLE_QUERY_METRIC_TYPE);
643+
objectName = objectNameNoIndex("TotalRowsToReturnEstimated", KEYSPACE, table, TABLE_QUERY_METRIC_TYPE);
644+
waitForMetricValueBetween(objectName, (long)(numRows / 2.0 * 0.75), (long)(numRows / 2.0 * 1.25));
645+
646+
objectName = objectNameNoIndex("TotalRowsToFetchEstimated", KEYSPACE, table, TABLE_QUERY_METRIC_TYPE);
647+
waitForMetricValueBetween(objectName, (long)(numRows / 2.0 * 0.75), (long)(numRows / 2.0 * 1.25));
648+
649+
objectName = objectNameNoIndex("TotalKeysToIterateEstimated", KEYSPACE, table, TABLE_QUERY_METRIC_TYPE);
636650
waitForMetricValueBetween(objectName, (long)(numRows / 2.0 * 0.75), (long)(numRows / 2.0 * 1.25));
637651

638652
objectName = objectNameNoIndex("TotalCostEstimated", KEYSPACE, table, TABLE_QUERY_METRIC_TYPE);
@@ -654,11 +668,11 @@ public void testQueryPlannerMetrics()
654668

655669
// Check estimates are updated also for queries returning 0 rows
656670
// 0 is special, log selectivity would be -infinity, so we need to check if there is no overflow
657-
var oldRowsEstimated = getHistogramMean(rowsEstimatedMetric);
671+
var oldRowsEstimated = getHistogramMean(rowsToFetchEstimatedMetric);
658672
var oldLogSelectivityEstimated = getHistogramMean(logSelectivityEstimatedMetric);
659673
rows = execute("SELECT k FROM %s WHERE lc = -1");
660674
assertEquals(0, rows.size());
661-
var newRowsEstimated = getHistogramMean(rowsEstimatedMetric);
675+
var newRowsEstimated = getHistogramMean(rowsToFetchEstimatedMetric);
662676
assertTrue(newRowsEstimated < oldRowsEstimated);
663677
var newLogSelectivityEstimated = getHistogramMean(logSelectivityEstimatedMetric);
664678
assertTrue(Double.isFinite(newLogSelectivityEstimated));

0 commit comments

Comments
 (0)