Skip to content

Commit e80179e

Browse files
craig[bot]yuzefovich
andcommitted
142248: sql: unify "distribution" in EXPLAIN and EXPLAIN ANALYZE r=yuzefovich a=yuzefovich Previously, it was possible to observe different `distribution` property between EXPLAIN and EXPLAIN ANALYZE output of the same query. The reason for that was that in the former we looked at the physical plan's distribution which indicates whether the physical plan is actually local or not, whereas for EXPLAIN ANALYZE we looked at presence of some plan flags which indicate the _recommendation_ about the physical plan, and the actual physical plan depends on the range placement. Concretely, it was possible to show `distribution: full` in the output of EXPLAIN ANALYZE when we only had a single flow on the gateway. This commit fixes that and unifies two EXPLAIN variants. It also removes `planFlagNotDistributed` plan flag since it's not used anywhere. Fixes: #128623. Release note (bug fix): EXPLAIN ANALYZE output could previously incorrectly show `distribution: full` when the physical plan only ran on the gateway node in some cases (meaning it should've shown `distribution: local`), and this is now fixed. Note that "vanilla" EXPLAIN showed the correct information. The bug has been present since before 23.1 version. 142671: logictest: fix rare flake around partial stats r=yuzefovich a=yuzefovich This commit applies the same fix as we had in 5707cf1 to be done after each attempt to collect partial stats. We were missing clearing of the stats cache in 3 places, and that is now fixed. Additionally, this commit moves the call to the builtin to be done right before collecting the partial stat to make it easier to see. Fixes: #141979. Release note: None Co-authored-by: Yahor Yuzefovich <[email protected]>
3 parents e617f97 + 2013fc4 + 5f4592b commit e80179e

File tree

8 files changed

+82
-41
lines changed

8 files changed

+82
-41
lines changed

pkg/sql/conn_executor_exec.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2966,8 +2966,6 @@ func (ex *connExecutor) dispatchToExecutionEngine(
29662966
planner.curPlan.flags.Set(planFlagFullyDistributed)
29672967
case physicalplan.PartiallyDistributedPlan:
29682968
planner.curPlan.flags.Set(planFlagPartiallyDistributed)
2969-
default:
2970-
planner.curPlan.flags.Set(planFlagNotDistributed)
29712969
}
29722970

29732971
ex.sessionTracing.TraceRetryInformation(ctx, int(ex.state.mu.autoRetryCounter), ex.state.mu.autoRetryReason)

pkg/sql/distsql_physical_planner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,7 @@ func getDefaultSaveFlowsFunc(
11151115
var explainVecVerbose []string
11161116
if planner.instrumentation.collectBundle && vectorized {
11171117
flowCtx := newFlowCtxForExplainPurposes(ctx, planner)
1118-
flowCtx.Local = !planner.curPlan.flags.IsDistributed()
1118+
flowCtx.Local = !planner.curPlan.flags.ShouldBeDistributed()
11191119
getExplain := func(verbose bool) []string {
11201120
gatewaySQLInstanceID := planner.extendedEvalCtx.DistSQLPlanner.gatewaySQLInstanceID
11211121
// When we're collecting the bundle, we're always recording the

pkg/sql/executor_statement_metrics.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func (ex *connExecutor) recordStatementSummary(
153153
recordedStmtStatsKey := appstatspb.StatementStatisticsKey{
154154
Query: stmt.StmtNoConstants,
155155
QuerySummary: stmt.StmtSummary,
156-
DistSQL: flags.IsDistributed(),
156+
DistSQL: flags.ShouldBeDistributed(),
157157
Vec: flags.IsSet(planFlagVectorized),
158158
ImplicitTxn: flags.IsSet(planFlagImplicitTxn),
159159
FullScan: fullScan,
@@ -299,7 +299,7 @@ func (ex *connExecutor) recordStatementLatencyMetrics(
299299

300300
m.StatementFingerprintCount.Add([]byte(stmt.StmtNoConstants))
301301

302-
if flags.IsDistributed() {
302+
if flags.ShouldBeDistributed() {
303303
if _, ok := stmt.AST.(*tree.Select); ok {
304304
m.DistSQLSelectCount.Inc(1)
305305
if flags.IsSet(planFlagDistributedExecution) {

pkg/sql/explain_test.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ func TestExplainKVInfo(t *testing.T) {
349349
}
350350

351351
scanQuery := "SELECT count(*) FROM ab"
352-
info := getKVInfo(t, r, scanQuery)
352+
info, fullOutput := getKVInfo(t, r, scanQuery)
353353

354354
assert.Equal(t, 1, info.counters[kvNodes])
355355
assert.Equal(t, 1000, info.counters[rowsRead])
@@ -358,9 +358,14 @@ func TestExplainKVInfo(t *testing.T) {
358358
assert.Equal(t, 1, info.counters[gRPCCalls])
359359
assert.Equal(t, 1000, info.counters[stepCount])
360360
assert.Equal(t, 1, info.counters[seekCount])
361+
// Additionally assert that correct distribution is shown.
362+
assert.Truef(
363+
t, strings.Contains(fullOutput, "distribution: local"),
364+
"expected local distribution, found\n\n%s", fullOutput,
365+
)
361366

362367
lookupJoinQuery := "SELECT count(*) FROM ab INNER LOOKUP JOIN bc ON ab.b = bc.b"
363-
info = getKVInfo(t, r, lookupJoinQuery)
368+
info, fullOutput = getKVInfo(t, r, lookupJoinQuery)
364369

365370
assert.Equal(t, 1, info.counters[kvNodes])
366371
assert.Equal(t, 1000, info.counters[rowsRead])
@@ -369,6 +374,10 @@ func TestExplainKVInfo(t *testing.T) {
369374
assert.Equal(t, 1, info.counters[gRPCCalls])
370375
assert.Equal(t, 0, info.counters[stepCount])
371376
assert.Equal(t, 1000, info.counters[seekCount])
377+
assert.Truef(
378+
t, strings.Contains(fullOutput, "distribution: local"),
379+
"expected local distribution\n\n%s", fullOutput,
380+
)
372381
}
373382
}
374383
}
@@ -406,7 +415,10 @@ func init() {
406415
// of the given query from the top-most operator in the plan (i.e. if there are
407416
// multiple operators exposing the scan stats, then the first info that appears
408417
// in the EXPLAIN output is used).
409-
func getKVInfo(t *testing.T, r *sqlutils.SQLRunner, query string) kvInfo {
418+
//
419+
// It additionally returns the full output of EXPLAIN ANALYZE of the given
420+
// query.
421+
func getKVInfo(t *testing.T, r *sqlutils.SQLRunner, query string) (_ kvInfo, fullOutput string) {
410422
rows := r.Query(t, "EXPLAIN ANALYZE (VERBOSE) "+query)
411423
var output strings.Builder
412424
var str string
@@ -442,7 +454,7 @@ func getKVInfo(t *testing.T, r *sqlutils.SQLRunner, query string) kvInfo {
442454
fmt.Println("Explain output:")
443455
fmt.Println(output.String())
444456
}
445-
return info
457+
return info, output.String()
446458
}
447459

448460
// TestExplainAnalyzeWarnings verifies that warnings are printed whenever the

pkg/sql/logictest/testdata/logic_test/distsql_stats

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,6 +2424,11 @@ INSERT INTO abcd VALUES
24242424
statement ok
24252425
INSERT INTO xy VALUES (-1, 9), (-2, 8), (5, 15), (6, 16)
24262426

2427+
# Clear the stat cache so that creating partial statistics has access to the
2428+
# latest full statistic.
2429+
statement ok
2430+
SELECT crdb_internal.clear_table_stats_cache();
2431+
24272432
statement error pgcode 0A000 creating partial statistics with a WHERE clause is not yet supported
24282433
CREATE STATISTICS abcd_a_partial ON a FROM abcd WHERE a > 1;
24292434

@@ -2539,14 +2544,14 @@ INSERT INTO a_null VALUES (NULL), (1), (2);
25392544
statement ok
25402545
CREATE STATISTICS a_null_stat ON a FROM a_null;
25412546

2547+
statement ok
2548+
INSERT INTO a_null VALUES (NULL), (NULL), (NULL);
2549+
25422550
# Clear the stat cache so that creating partial statistics has access to the
25432551
# latest full statistic.
25442552
statement ok
25452553
SELECT crdb_internal.clear_table_stats_cache();
25462554

2547-
statement ok
2548-
INSERT INTO a_null VALUES (NULL), (NULL), (NULL);
2549-
25502555
statement ok
25512556
CREATE STATISTICS a_null_stat_partial ON a FROM a_null USING EXTREMES;
25522557

@@ -2577,14 +2582,14 @@ INSERT INTO d_desc VALUES (1, 10), (2, 20), (3, 30), (4, 40);
25772582
statement ok
25782583
CREATE STATISTICS sd ON a FROM d_desc;
25792584

2585+
statement ok
2586+
INSERT INTO d_desc VALUES (0, 0), (5, 50);
2587+
25802588
# Clear the stat cache so that creating partial statistics has access to the
25812589
# latest full statistic.
25822590
statement ok
25832591
SELECT crdb_internal.clear_table_stats_cache();
25842592

2585-
statement ok
2586-
INSERT INTO d_desc VALUES (0, 0), (5, 50);
2587-
25882593
statement ok
25892594
CREATE STATISTICS sdp ON a FROM d_desc USING EXTREMES;
25902595

@@ -3016,6 +3021,11 @@ ANALYZE t130817;
30163021
statement ok
30173022
INSERT INTO t68254 (a, b, c) VALUES (5, '5', '{"foo": {"bar": {"baz": 5}}}')
30183023

3024+
# Clear the stat cache so that creating partial statistics has access to the
3025+
# latest full statistic.
3026+
statement ok
3027+
SELECT crdb_internal.clear_table_stats_cache();
3028+
30193029
statement ok
30203030
CREATE STATISTICS j6 ON d FROM t68254 USING EXTREMES
30213031

@@ -3081,10 +3091,12 @@ ALTER TABLE int_outer_buckets INJECT STATISTICS '[
30813091
]'
30823092

30833093
statement ok
3084-
SELECT crdb_internal.clear_table_stats_cache();
3094+
INSERT INTO int_outer_buckets SELECT generate_series(-10, -1) UNION ALL SELECT generate_series(10000, 10009);
30853095

3096+
# Clear the stat cache so that creating partial statistics has access to the
3097+
# latest full statistic.
30863098
statement ok
3087-
INSERT INTO int_outer_buckets SELECT generate_series(-10, -1) UNION ALL SELECT generate_series(10000, 10009);
3099+
SELECT crdb_internal.clear_table_stats_cache();
30883100

30893101
statement ok
30903102
CREATE STATISTICS int_outer_buckets_partial ON a FROM int_outer_buckets USING EXTREMES;
@@ -3143,11 +3155,6 @@ INSERT INTO timestamp_outer_buckets VALUES
31433155
statement ok
31443156
CREATE STATISTICS timestamp_outer_buckets_full ON a FROM timestamp_outer_buckets;
31453157

3146-
# Clear the stat cache so that creating partial statistics has access to the
3147-
# latest full statistic.
3148-
statement ok
3149-
SELECT crdb_internal.clear_table_stats_cache();
3150-
31513158
let $hist_id_timestamp_outer_buckets_full
31523159
SELECT histogram_id FROM [SHOW STATISTICS FOR TABLE timestamp_outer_buckets] WHERE statistics_name = 'timestamp_outer_buckets_full'
31533160

@@ -3162,6 +3169,11 @@ INSERT INTO timestamp_outer_buckets VALUES
31623169
('2024-06-26 00:00:00'),
31633170
('2024-06-27 03:30:00');
31643171

3172+
# Clear the stat cache so that creating partial statistics has access to the
3173+
# latest full statistic.
3174+
statement ok
3175+
SELECT crdb_internal.clear_table_stats_cache();
3176+
31653177
statement ok
31663178
CREATE STATISTICS timestamp_outer_buckets_partial ON a FROM timestamp_outer_buckets USING EXTREMES;
31673179

@@ -3233,6 +3245,11 @@ ALTER TABLE timestamp_outer_buckets INJECT STATISTICS '[
32333245
statement ok
32343246
INSERT INTO timestamp_outer_buckets VALUES ('2024-06-28 01:00:00');
32353247

3248+
# Clear the stat cache so that creating partial statistics has access to the
3249+
# latest full statistic.
3250+
statement ok
3251+
SELECT crdb_internal.clear_table_stats_cache();
3252+
32363253
statement ok
32373254
CREATE STATISTICS timestamp_outer_buckets_partial ON a FROM timestamp_outer_buckets USING EXTREMES;
32383255

@@ -3615,6 +3632,11 @@ INSERT INTO pstat_allindex VALUES
36153632
(6, 6, 6, 6, 6, 6, '{"6": "6"}'),
36163633
(7, 7, 7, 7, 7, 7, '{"7": "7"}');
36173634

3635+
# Clear the stat cache so that creating partial statistics has access to the
3636+
# latest full statistic.
3637+
statement ok
3638+
SELECT crdb_internal.clear_table_stats_cache();
3639+
36183640
statement ok
36193641
CREATE STATISTICS pstat_allindex_partial FROM pstat_allindex USING EXTREMES;
36203642

pkg/sql/plan.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,16 @@ func (p *planTop) init(stmt *Statement, instrumentation *instrumentationHelper)
510510
func (p *planTop) savePlanInfo() {
511511
vectorized := p.flags.IsSet(planFlagVectorized)
512512
distribution := physicalplan.LocalPlan
513-
if p.flags.IsSet(planFlagFullyDistributed) {
514-
distribution = physicalplan.FullyDistributedPlan
515-
} else if p.flags.IsSet(planFlagPartiallyDistributed) {
516-
distribution = physicalplan.PartiallyDistributedPlan
513+
if p.flags.IsSet(planFlagDistributedExecution) {
514+
// Only show that the plan was distributed if we actually had
515+
// distributed execution. This matches the logic from explainPlanNode
516+
// where we use the actual physical plan's distribution rather than the
517+
// physical planning heuristic.
518+
if p.flags.IsSet(planFlagFullyDistributed) {
519+
distribution = physicalplan.FullyDistributedPlan
520+
} else if p.flags.IsSet(planFlagPartiallyDistributed) {
521+
distribution = physicalplan.PartiallyDistributedPlan
522+
}
517523
}
518524
containsMutation := p.flags.IsSet(planFlagContainsMutation)
519525
generic := p.flags.IsSet(planFlagGeneric)
@@ -610,17 +616,18 @@ const (
610616
planFlagOptCacheMiss
611617

612618
// planFlagFullyDistributed is set if the query is planned to use full
613-
// distribution.
619+
// distribution. This flag indicates that the query is such that it can be
620+
// distributed, and we think it's worth doing so; however, due to range
621+
// placement and other physical planning decisions, the plan might still end
622+
// up being local. See planFlagDistributedExecution if interested in whether
623+
// the physical plan actually ends up being distributed.
614624
planFlagFullyDistributed
615625

616-
// planFlagPartiallyDistributed is set if the query is planned to use partial
617-
// distribution (see physicalplan.PartiallyDistributedPlan).
626+
// planFlagPartiallyDistributed is set if the query is planned to use
627+
// partial distribution (see physicalplan.PartiallyDistributedPlan). Same
628+
// caveats apply as for planFlagFullyDistributed.
618629
planFlagPartiallyDistributed
619630

620-
// planFlagNotDistributed is set if the query is planned to not use
621-
// distribution.
622-
planFlagNotDistributed
623-
624631
// planFlagImplicitTxn marks that the plan was run inside of an implicit
625632
// transaction.
626633
planFlagImplicitTxn
@@ -707,8 +714,10 @@ func (pf *planFlags) Unset(flags planFlags) {
707714
*pf &^= flags
708715
}
709716

710-
// IsDistributed returns true if either the fully or the partially distributed
711-
// flags is set.
712-
func (pf planFlags) IsDistributed() bool {
717+
// ShouldBeDistributed returns true if either fully distributed or partially
718+
// distributed flag is set. In other words, it returns whether the plan should
719+
// be distributed (we might end up not distributing it though due to range
720+
// placement or moving the single flow to the gateway).
721+
func (pf planFlags) ShouldBeDistributed() bool {
713722
return pf&(planFlagFullyDistributed|planFlagPartiallyDistributed) != 0
714723
}

pkg/sql/testdata/telemetry_logging/logging/force_logging

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ SELECT * FROM t LIMIT 1;
3131
{
3232
"ApplicationName": "telemetry-logging-datadriven",
3333
"Database": "defaultdb",
34-
"Distribution": "full",
34+
"Distribution": "local",
3535
"EventType": "sampled_query",
3636
"PlanGist": "AgHQAQIAAAAAAg==",
3737
"ScanCount": 1,
@@ -164,7 +164,7 @@ SELECT * FROM t LIMIT 1;
164164
{
165165
"ApplicationName": "$ internal-console-app",
166166
"Database": "defaultdb",
167-
"Distribution": "full",
167+
"Distribution": "local",
168168
"EventType": "sampled_query",
169169
"PlanGist": "AgHQAQIAAAAAAg==",
170170
"ScanCount": 1,

pkg/sql/testdata/telemetry_logging/logging/transaction_mode

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ SELECT * FROM t LIMIT 2
208208
{
209209
"ApplicationName": "telemetry-logging-datadriven",
210210
"Database": "defaultdb",
211-
"Distribution": "full",
211+
"Distribution": "local",
212212
"EventType": "sampled_query",
213213
"PlanGist": "AgHQAQQAAAAAAg==",
214214
"ScanCount": 1,
@@ -246,7 +246,7 @@ BEGIN; SELECT * FROM t LIMIT 4; SELECT * FROM t LIMIT 5; COMMIT
246246
{
247247
"ApplicationName": "telemetry-logging-datadriven",
248248
"Database": "defaultdb",
249-
"Distribution": "full",
249+
"Distribution": "local",
250250
"EventType": "sampled_query",
251251
"PlanGist": "AgHQAQQAAAAAAg==",
252252
"ScanCount": 1,
@@ -260,7 +260,7 @@ BEGIN; SELECT * FROM t LIMIT 4; SELECT * FROM t LIMIT 5; COMMIT
260260
{
261261
"ApplicationName": "telemetry-logging-datadriven",
262262
"Database": "defaultdb",
263-
"Distribution": "full",
263+
"Distribution": "local",
264264
"EventType": "sampled_query",
265265
"PlanGist": "AgHQAQQAAAAAAg==",
266266
"ScanCount": 1,

0 commit comments

Comments
 (0)