-
Notifications
You must be signed in to change notification settings - Fork 21
CNDB-15508: Query planner metrics #2130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,7 +115,7 @@ public void record(QueryContext context, ReadCommand command) | |
| { | ||
| final long queryLatencyMicros = TimeUnit.NANOSECONDS.toMicros(snapshot.totalQueryTimeNs); | ||
|
|
||
| if (snapshot.filterSortOrder == QueryContext.FilterSortOrder.SEARCH_THEN_ORDER) | ||
| if (snapshot.queryPlanInfo != null && snapshot.queryPlanInfo.searchExecutedBeforeOrder) | ||
| { | ||
| Tracing.trace("Index query accessed memtable indexes, {}, and {}, selected {} before ranking, " + | ||
| "post-filtered {} in {}, and took {} microseconds.", | ||
|
|
@@ -199,6 +199,11 @@ public static class PerTable extends AbstractQueryMetrics | |
| public final Counter totalRowTombstonesFetched; | ||
| public final Counter totalQueriesCompleted; | ||
|
|
||
| public final Counter totalRowsToReturnEstimated; | ||
| public final Counter totalRowsToFetchEstimated; | ||
| public final Counter totalKeysToIterateEstimated; | ||
| public final Counter totalCostEstimated; | ||
|
|
||
| public final Counter sortThenFilterQueriesCompleted; | ||
| public final Counter filterThenSortQueriesCompleted; | ||
|
|
||
|
|
@@ -220,6 +225,10 @@ public PerTable(TableMetadata table, QueryKind queryKind, Predicate<ReadCommand> | |
| totalRowTombstonesFetched = Metrics.counter(createMetricName("TotalRowTombstonesFetched")); | ||
| totalQueriesCompleted = Metrics.counter(createMetricName("TotalQueriesCompleted")); | ||
| totalQueryTimeouts = Metrics.counter(createMetricName("TotalQueryTimeouts")); | ||
| totalRowsToReturnEstimated = Metrics.counter(createMetricName("TotalRowsToReturnEstimated")); | ||
| totalRowsToFetchEstimated = Metrics.counter(createMetricName("TotalRowsToFetchEstimated")); | ||
| totalKeysToIterateEstimated = Metrics.counter(createMetricName("TotalKeysToIterateEstimated")); | ||
| totalCostEstimated = Metrics.counter(createMetricName("TotalCostEstimated")); | ||
|
|
||
| sortThenFilterQueriesCompleted = Metrics.counter(createMetricName("SortThenFilterQueriesCompleted")); | ||
| filterThenSortQueriesCompleted = Metrics.counter(createMetricName("FilterThenSortQueriesCompleted")); | ||
|
|
@@ -243,10 +252,19 @@ public void record(QueryContext.Snapshot snapshot) | |
| totalRowsReturned.inc(snapshot.rowsReturned); | ||
| totalRowTombstonesFetched.inc(snapshot.rowTombstonesFetched); | ||
|
|
||
| if (snapshot.filterSortOrder == QueryContext.FilterSortOrder.SCAN_THEN_FILTER) | ||
| sortThenFilterQueriesCompleted.inc(); | ||
| else if (snapshot.filterSortOrder == QueryContext.FilterSortOrder.SEARCH_THEN_ORDER) | ||
| filterThenSortQueriesCompleted.inc(); | ||
| QueryContext.PlanInfo queryPlanInfo = snapshot.queryPlanInfo; | ||
| if (queryPlanInfo != null) | ||
| { | ||
| totalCostEstimated.inc(Math.round(queryPlanInfo.costEstimated)); | ||
| totalRowsToReturnEstimated.inc(Math.round(queryPlanInfo.rowsToReturnEstimated)); | ||
| totalRowsToFetchEstimated.inc(Math.round(queryPlanInfo.rowsToFetchEstimated)); | ||
| totalKeysToIterateEstimated.inc(Math.round(queryPlanInfo.keysToIterateEstimated)); | ||
|
|
||
| if (queryPlanInfo.filterExecutedAfterOrderedScan) | ||
| sortThenFilterQueriesCompleted.inc(); | ||
| if (queryPlanInfo.searchExecutedBeforeOrder) | ||
| filterThenSortQueriesCompleted.inc(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -293,6 +311,34 @@ public static class PerQuery extends AbstractQueryMetrics | |
| */ | ||
| public final Timer annGraphSearchLatency; | ||
|
|
||
| /** Query execution cost as estimated by the planner */ | ||
| public final Histogram costEstimated; | ||
pkolaczk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** Number of rows to be returned from the query as estimated by the planner */ | ||
| public final Histogram rowsToReturnEstimated; | ||
|
|
||
| /** Number of rows to be fetched by the query as estimated by the planner */ | ||
| public final Histogram rowsToFetchEstimated; | ||
|
|
||
| /** Number of keys to be iterated by the query as estimated by the planner */ | ||
| public final Histogram keysToIterateEstimated; | ||
|
|
||
| /** | ||
| * Negative decimal logarithm of selectivity of the query, before applying the LIMIT clause. | ||
| * We use logarithm because selectivity values can be very small (e.g. 10^-9). | ||
| */ | ||
| public final Histogram logSelectivityEstimated; | ||
|
|
||
| /** | ||
| * Number of indexes referenced by the optimized query plan. | ||
| * The same index referenced from unrelated query clauses, | ||
| * leading to separate index searches, are counted separately. | ||
| */ | ||
| public final Histogram indexReferencesInPlan; | ||
|
|
||
| /** Number of indexes referenced by the original query plan before optimization (as stated in the query text) */ | ||
| public final Histogram indexReferencesInQuery; | ||
|
|
||
| /** | ||
| * @param table the table to measure metrics for | ||
| * @param queryKind an identifier for the kind of query which metrics are being recorded for | ||
|
|
@@ -323,6 +369,14 @@ public PerQuery(TableMetadata table, QueryKind queryKind, Predicate<ReadCommand> | |
|
|
||
| // Key vector metrics that translate to performance | ||
| annGraphSearchLatency = Metrics.timer(createMetricName("ANNGraphSearchLatency")); | ||
|
|
||
| costEstimated = Metrics.histogram(createMetricName("CostEstimated"), false); | ||
| rowsToReturnEstimated = Metrics.histogram(createMetricName("RowsToReturnEstimated"), true); | ||
| rowsToFetchEstimated = Metrics.histogram(createMetricName("RowsToFetchEstimated"), true); | ||
| keysToIterateEstimated = Metrics.histogram(createMetricName("KeysToIterateEstimated"), true); | ||
| logSelectivityEstimated = Metrics.histogram(createMetricName("LogSelectivityEstimated"), true); | ||
| indexReferencesInPlan = Metrics.histogram(createMetricName("IndexReferencesInPlan"), true); | ||
| indexReferencesInQuery = Metrics.histogram(createMetricName("IndexReferencesInQuery"), false); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -362,6 +416,19 @@ public void record(QueryContext.Snapshot snapshot) | |
| { | ||
| annGraphSearchLatency.update(snapshot.annGraphSearchLatency, TimeUnit.NANOSECONDS); | ||
| } | ||
|
|
||
| QueryContext.PlanInfo queryPlanInfo = snapshot.queryPlanInfo; | ||
| if (queryPlanInfo != null) | ||
| { | ||
| costEstimated.update(Math.round(queryPlanInfo.costEstimated)); | ||
| rowsToReturnEstimated.update(Math.round(queryPlanInfo.rowsToReturnEstimated)); | ||
| rowsToFetchEstimated.update(Math.round(queryPlanInfo.rowsToFetchEstimated)); | ||
| keysToIterateEstimated.update(Math.round(queryPlanInfo.keysToIterateEstimated)); | ||
| double logSelectivity = -Math.log10(queryPlanInfo.selectivityEstimated); | ||
| logSelectivityEstimated.update((int) (Math.min(20, Math.floor(logSelectivity)))); | ||
|
Comment on lines
+427
to
+428
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a big deal, but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we could do the same for the quicker calls to public final long costEstimated;
public final long rowsToReturnEstimated;
public final long rowsToFetchEstimated;
public final long keysToIterateEstimated;
public final int logSelectivityEstimated;
...
public PlanInfo(@Nonnull Plan.RowsIteration originalPlan, @Nonnull Plan.RowsIteration optimizedPlan)
{
this.costEstimated = Math.round(optimizedPlan.fullCost());
this.rowsToReturnEstimated = Math.round(optimizedPlan.expectedRows());
this.rowsToFetchEstimated = Math.round(optimizedPlan.estimatedRowsToFetch());
this.keysToIterateEstimated = Math.round(optimizedPlan.estimatedKeysToIterate());
this.logSelectivityEstimated = (Math.min(20, (int) Math.floor(-Math.log10(optimizedPlan.selectivity()))));That was the intention of having a |
||
| indexReferencesInQuery.update(queryPlanInfo.indexReferencesInQuery); | ||
| indexReferencesInPlan.update(queryPlanInfo.indexReferencesInPlan); | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indexReferencesInQueryandindexReferencesInPlanhave associated histograms inTableQueryMetrics, but not counters.searchExecutedBeforeOrderandfilterExecutedAfterOrderedScanhave counters but not histograms. Is that intentional?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it breaks the consistency, I am wondering whether it is this way because:
Maybe we add some note to make clear the rationale? Wondering whether that would be helpful or it may become outdated doc in time
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for booleans - I think nothing technically stops us from having a boolean histogram (but we'd have to convert it to 0 and 1 as currently histograms are based on longs); but I feel it wouldn't be very useful in this case. We get 100% of the same information from the per-table counters.
As for the other histograms - well, it is hard to represent them accurately as per table counters. Like, I feel it doesn't make much sense to sum all index references from all queries... So we have counters in per table for things which sum events naturally (like rows returned - it is quite easy to understand what a total number of rows returned is), but per-query histograms for other properties of queries which are not "summable" in that way.
Note there are many other histograms in perQuery which don't have (a) corresponding PerTable counter(s), so I don't think it breaks consistency. Anyway, we really need to expose those histograms in CNDB somehow, but let's live it for a followup ticket.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes perfect sense to me, thanks.