Skip to content

Commit eab9c32

Browse files
authored
Merge pull request #155123 from Uzair5162/blathers/backport-release-25.4-154370
release-25.4: sql: log when optimizer estimates for scans are inaccurate
2 parents 159bd93 + a2de67e commit eab9c32

34 files changed

+659
-43
lines changed

docs/generated/eventlog.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,37 @@ and the cluster setting `sql.log.all_statements.enabled` is set.
948948
| `BulkJobId` | The job id for bulk job (IMPORT/BACKUP/RESTORE). | no |
949949
| `StmtPosInTxn` | The statement's index in the transaction, starting at 1. | no |
950950

951+
### `scan_row_count_misestimate`
952+
953+
An event of type `scan_row_count_misestimate` is recorded when the optimizer's row count estimate
954+
for a logical scan differs significantly from the actual number of rows read,
955+
and cluster setting `sql.log.scan_row_count_misestimate.enabled` is set.
956+
957+
958+
| Field | Description | Sensitive |
959+
|--|--|--|
960+
| `TableName` | The fully qualified name of the table being scanned. | no |
961+
| `IndexName` | The name of the index being scanned. | no |
962+
| `EstimatedRowCount` | The optimizer's estimated row count for the scan. | no |
963+
| `ActualRowCount` | The actual number of rows read by all processors performing the scan. | no |
964+
| `NanosSinceStatsCollected` | Time in nanoseconds that have passed since full stats were collected on the table. | no |
965+
| `EstimatedStaleness` | Estimated fraction of stale rows in the table based on the time since stats were last collected. | no |
966+
967+
968+
#### Common fields
969+
970+
| Field | Description | Sensitive |
971+
|--|--|--|
972+
| `Timestamp` | The timestamp of the event. Expressed as nanoseconds since the Unix epoch. | no |
973+
| `EventType` | The type of the event. | no |
974+
| `Statement` | A normalized copy of the SQL statement that triggered the event. The statement string contains a mix of sensitive and non-sensitive details (it is redactable). | partially |
975+
| `Tag` | The statement tag. This is separate from the statement string, since the statement string can contain sensitive information. The tag is guaranteed not to. | no |
976+
| `User` | The user account that triggered the event. The special usernames `root` and `node` are not considered sensitive. | depends |
977+
| `DescriptorID` | The primary object descriptor affected by the operation. Set to zero for operations that don't affect descriptors. | no |
978+
| `ApplicationName` | The application name for the session where the event was emitted. This is included in the event to ease filtering of logging output by application. | no |
979+
| `PlaceholderValues` | The mapping of SQL placeholders to their values, for prepared statements. | yes |
980+
| `TxnReadTimestamp` | The current read timestamp of the transaction that triggered the event, if in a transaction. | no |
981+
951982
## SQL Logical Schema Changes
952983

953984
Events in this category pertain to DDL (Data Definition Language)

docs/generated/settings/settings-for-tenants.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ sql.insights.execution_insights_capacity integer 1000 the size of the per-node s
321321
sql.insights.high_retry_count.threshold integer 10 the number of retries a slow statement must have undergone for its high retry count to be highlighted as a potential problem application
322322
sql.insights.latency_threshold duration 100ms amount of time after which an executing statement is considered slow. Use 0 to disable. application
323323
sql.log.redact_names.enabled boolean false if set, schema object identifers are redacted in SQL statements that appear in event logs application
324+
sql.log.scan_row_count_misestimate.enabled boolean false when set to true, log a warning when a scan's actual row count differs significantly from the optimizer's estimate application
324325
sql.log.slow_query.experimental_full_table_scans.enabled boolean false when set to true, statements that perform a full table/index scan will be logged to the slow query log even if they do not meet the latency threshold. Must have the slow query log enabled for this setting to have any effect. application
325326
sql.log.slow_query.internal_queries.enabled boolean false when set to true, internal queries which exceed the slow query log threshold are logged to a separate log. Must have the slow query log enabled for this setting to have any effect. application
326327
sql.log.slow_query.latency_threshold duration 0s when set to non-zero, log statements whose service latency exceeds the threshold to a secondary logger on each node application

docs/generated/settings/settings.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@
276276
<tr><td><div id="setting-sql-insights-high-retry-count-threshold" class="anchored"><code>sql.insights.high_retry_count.threshold</code></div></td><td>integer</td><td><code>10</code></td><td>the number of retries a slow statement must have undergone for its high retry count to be highlighted as a potential problem</td><td>Basic/Standard/Advanced/Self-Hosted</td></tr>
277277
<tr><td><div id="setting-sql-insights-latency-threshold" class="anchored"><code>sql.insights.latency_threshold</code></div></td><td>duration</td><td><code>100ms</code></td><td>amount of time after which an executing statement is considered slow. Use 0 to disable.</td><td>Basic/Standard/Advanced/Self-Hosted</td></tr>
278278
<tr><td><div id="setting-sql-log-redact-names-enabled" class="anchored"><code>sql.log.redact_names.enabled</code></div></td><td>boolean</td><td><code>false</code></td><td>if set, schema object identifers are redacted in SQL statements that appear in event logs</td><td>Basic/Standard/Advanced/Self-Hosted</td></tr>
279+
<tr><td><div id="setting-sql-log-scan-row-count-misestimate-enabled" class="anchored"><code>sql.log.scan_row_count_misestimate.enabled</code></div></td><td>boolean</td><td><code>false</code></td><td>when set to true, log a warning when a scan&#39;s actual row count differs significantly from the optimizer&#39;s estimate</td><td>Basic/Standard/Advanced/Self-Hosted</td></tr>
279280
<tr><td><div id="setting-sql-log-slow-query-experimental-full-table-scans-enabled" class="anchored"><code>sql.log.slow_query.experimental_full_table_scans.enabled</code></div></td><td>boolean</td><td><code>false</code></td><td>when set to true, statements that perform a full table/index scan will be logged to the slow query log even if they do not meet the latency threshold. Must have the slow query log enabled for this setting to have any effect.</td><td>Basic/Standard/Advanced/Self-Hosted</td></tr>
280281
<tr><td><div id="setting-sql-log-slow-query-internal-queries-enabled" class="anchored"><code>sql.log.slow_query.internal_queries.enabled</code></div></td><td>boolean</td><td><code>false</code></td><td>when set to true, internal queries which exceed the slow query log threshold are logged to a separate log. Must have the slow query log enabled for this setting to have any effect.</td><td>Basic/Standard/Advanced/Self-Hosted</td></tr>
281282
<tr><td><div id="setting-sql-log-slow-query-latency-threshold" class="anchored"><code>sql.log.slow_query.latency_threshold</code></div></td><td>duration</td><td><code>0s</code></td><td>when set to non-zero, log statements whose service latency exceeds the threshold to a secondary logger on each node</td><td>Basic/Standard/Advanced/Self-Hosted</td></tr>

pkg/sql/colexec/colbuilder/execplan.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ func (r opResult) createAndWrapRowSource(
566566
core *execinfrapb.ProcessorCoreUnion,
567567
post *execinfrapb.PostProcessSpec,
568568
processorID int32,
569+
stageID int32,
569570
factory coldata.ColumnFactory,
570571
causeToWrap error,
571572
) error {
@@ -592,7 +593,7 @@ func (r opResult) createAndWrapRowSource(
592593
// here because when wrapping the processor, the materializer will
593594
// be its output, and it will be set up in wrapRowSources.
594595
proc, err := args.ProcessorConstructor(
595-
ctx, flowCtx, processorID, core, post, inputs, args.LocalProcessors,
596+
ctx, flowCtx, processorID, stageID, core, post, inputs, args.LocalProcessors,
596597
)
597598
if err != nil {
598599
return nil, err
@@ -819,7 +820,7 @@ func NewColOperator(
819820
post = &newPosts[1]
820821
err = result.createAndWrapRowSource(
821822
ctx, flowCtx, args, inputs, inputTypes, core,
822-
wrappingPost, spec.ProcessorID, factory, err,
823+
wrappingPost, spec.ProcessorID, spec.StageID, factory, err,
823824
)
824825
} else {
825826
switch {
@@ -969,7 +970,8 @@ func NewColOperator(
969970
if canUseDirectScan() {
970971
scanOp, resultTypes, err = colfetcher.NewColBatchDirectScan(
971972
ctx, colmem.NewAllocator(ctx, accounts[0], factory), accounts[1],
972-
flowCtx, spec.ProcessorID, core.TableReader, post, args.TypeResolver,
973+
flowCtx, spec.ProcessorID, spec.StageID, core.TableReader, post,
974+
args.TypeResolver,
973975
)
974976
if err != nil {
975977
return r, err
@@ -979,7 +981,8 @@ func NewColOperator(
979981
if scanOp == nil {
980982
scanOp, resultTypes, err = colfetcher.NewColBatchScan(
981983
ctx, colmem.NewAllocator(ctx, accounts[0], factory), accounts[1],
982-
flowCtx, spec.ProcessorID, core.TableReader, post, estimatedRowCount, args.TypeResolver,
984+
flowCtx, spec.ProcessorID, spec.StageID, core.TableReader, post,
985+
estimatedRowCount, args.TypeResolver,
983986
)
984987
if err != nil {
985988
return r, err
@@ -1027,7 +1030,7 @@ func NewColOperator(
10271030
result.ColumnTypes = spec.Input[0].ColumnTypes
10281031
result.Root = inputs[0].Root
10291032
if err := result.planAndMaybeWrapFilter(
1030-
ctx, flowCtx, args, spec.ProcessorID, core.Filterer.Filter, factory,
1033+
ctx, flowCtx, args, spec.ProcessorID, spec.StageID, core.Filterer.Filter, factory,
10311034
); err != nil {
10321035
return r, err
10331036
}
@@ -1309,7 +1312,7 @@ func NewColOperator(
13091312

13101313
if !core.HashJoiner.OnExpr.Empty() && core.HashJoiner.Type == descpb.InnerJoin {
13111314
if err = result.planAndMaybeWrapFilter(
1312-
ctx, flowCtx, args, spec.ProcessorID, core.HashJoiner.OnExpr, factory,
1315+
ctx, flowCtx, args, spec.ProcessorID, spec.StageID, core.HashJoiner.OnExpr, factory,
13131316
); err != nil {
13141317
return r, err
13151318
}
@@ -1354,7 +1357,7 @@ func NewColOperator(
13541357

13551358
if onExpr != nil {
13561359
if err = result.planAndMaybeWrapFilter(
1357-
ctx, flowCtx, args, spec.ProcessorID, *onExpr, factory,
1360+
ctx, flowCtx, args, spec.ProcessorID, spec.StageID, *onExpr, factory,
13581361
); err != nil {
13591362
return r, err
13601363
}
@@ -1797,7 +1800,8 @@ func NewColOperator(
17971800
}
17981801
err = ppr.planPostProcessSpec(ctx, flowCtx, args, post, factory, &r.Releasables, args.Spec.EstimatedRowCount)
17991802
if err != nil {
1800-
err = result.wrapPostProcessSpec(ctx, flowCtx, args, post, spec.ProcessorID, factory, err)
1803+
err = result.wrapPostProcessSpec(ctx, flowCtx, args, post,
1804+
spec.ProcessorID, spec.StageID, factory, err)
18011805
} else {
18021806
// The result can be updated with the post process result.
18031807
r.Root = ppr.Op
@@ -1845,7 +1849,8 @@ func NewColOperator(
18451849
post.RenderExprs[i].LocalExpr = tree.NewTypedOrdinalReference(i, args.Spec.ResultTypes[i])
18461850
}
18471851
}
1848-
if err = result.wrapPostProcessSpec(ctx, flowCtx, args, post, spec.ProcessorID, factory, errWrappedCast); err != nil {
1852+
if err = result.wrapPostProcessSpec(ctx, flowCtx, args, post,
1853+
spec.ProcessorID, spec.StageID, factory, errWrappedCast); err != nil {
18491854
return r, err
18501855
}
18511856
} else if numMismatchedTypes > 0 {
@@ -1909,6 +1914,7 @@ func (r opResult) planAndMaybeWrapFilter(
19091914
flowCtx *execinfra.FlowCtx,
19101915
args *colexecargs.NewColOperatorArgs,
19111916
processorID int32,
1917+
stageID int32,
19121918
filter execinfrapb.Expression,
19131919
factory coldata.ColumnFactory,
19141920
) error {
@@ -1928,7 +1934,7 @@ func (r opResult) planAndMaybeWrapFilter(
19281934
return r.createAndWrapRowSource(
19291935
ctx, flowCtx, args, []colexecargs.OpWithMetaInfo{inputToMaterializer},
19301936
[][]*types.T{r.ColumnTypes}, filtererCore, &execinfrapb.PostProcessSpec{},
1931-
processorID, factory, err,
1937+
processorID, stageID, factory, err,
19321938
)
19331939
}
19341940
return nil
@@ -1945,6 +1951,7 @@ func (r opResult) wrapPostProcessSpec(
19451951
args *colexecargs.NewColOperatorArgs,
19461952
post *execinfrapb.PostProcessSpec,
19471953
processorID int32,
1954+
stageID int32,
19481955
factory coldata.ColumnFactory,
19491956
causeToWrap error,
19501957
) error {
@@ -1956,7 +1963,8 @@ func (r opResult) wrapPostProcessSpec(
19561963
// createAndWrapRowSource updates r.ColumnTypes accordingly.
19571964
return r.createAndWrapRowSource(
19581965
ctx, flowCtx, args, []colexecargs.OpWithMetaInfo{inputToMaterializer},
1959-
[][]*types.T{r.ColumnTypes}, noopCore, post, processorID, factory, causeToWrap,
1966+
[][]*types.T{r.ColumnTypes}, noopCore, post, processorID, stageID, factory,
1967+
causeToWrap,
19601968
)
19611969
}
19621970

pkg/sql/colexec/values_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ func BenchmarkValues(b *testing.B) {
150150
var core execinfrapb.ProcessorCoreUnion
151151
core.Values = spec
152152
proc, err := rowexec.NewProcessor(
153-
ctx, &flowCtx, 0 /* processorID */, &core, &post, nil /* inputs */, nil, /* localProcessors */
153+
ctx, &flowCtx, 0 /* processorID */, 0 /* stageID */, &core, &post,
154+
nil /* inputs */, nil, /* localProcessors */
154155
)
155156
if err != nil {
156157
b.Fatal(err)

pkg/sql/colfetcher/colbatch_direct_scan.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ func (s *ColBatchDirectScan) DrainMeta() []execinfrapb.ProducerMetadata {
127127
meta.Metrics = execinfrapb.GetMetricsMeta()
128128
meta.Metrics.BytesRead = s.GetBytesRead()
129129
meta.Metrics.RowsRead = s.GetRowsRead()
130+
meta.Metrics.StageID = s.stageID
130131
trailingMeta = append(trailingMeta, *meta)
131132
return trailingMeta
132133
}
@@ -181,12 +182,13 @@ func NewColBatchDirectScan(
181182
kvFetcherMemAcc *mon.BoundAccount,
182183
flowCtx *execinfra.FlowCtx,
183184
processorID int32,
185+
stageID int32,
184186
spec *execinfrapb.TableReaderSpec,
185187
post *execinfrapb.PostProcessSpec,
186188
typeResolver *descs.DistSQLTypeResolver,
187189
) (*ColBatchDirectScan, []*types.T, error) {
188190
base, bsHeader, tableArgs, err := newColBatchScanBase(
189-
ctx, kvFetcherMemAcc, flowCtx, processorID, spec, post, typeResolver,
191+
ctx, kvFetcherMemAcc, flowCtx, processorID, stageID, spec, post, typeResolver,
190192
)
191193
if err != nil {
192194
return nil, nil, err

pkg/sql/colfetcher/colbatch_scan.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type colBatchScanBase struct {
3838

3939
flowCtx *execinfra.FlowCtx
4040
processorID int32
41+
stageID int32
4142
limitHint rowinfra.RowLimit
4243
batchBytesLimit rowinfra.BytesLimit
4344
parallelize bool
@@ -122,6 +123,7 @@ func newColBatchScanBase(
122123
kvFetcherMemAcc *mon.BoundAccount,
123124
flowCtx *execinfra.FlowCtx,
124125
processorID int32,
126+
stageID int32,
125127
spec *execinfrapb.TableReaderSpec,
126128
post *execinfrapb.PostProcessSpec,
127129
typeResolver *descs.DistSQLTypeResolver,
@@ -186,6 +188,7 @@ func newColBatchScanBase(
186188
SpansWithCopy: s.SpansWithCopy,
187189
flowCtx: flowCtx,
188190
processorID: processorID,
191+
stageID: stageID,
189192
limitHint: limitHint,
190193
batchBytesLimit: batchBytesLimit,
191194
parallelize: spec.Parallelize,
@@ -254,6 +257,7 @@ func (s *ColBatchScan) DrainMeta() []execinfrapb.ProducerMetadata {
254257
meta.Metrics = execinfrapb.GetMetricsMeta()
255258
meta.Metrics.BytesRead = s.GetBytesRead()
256259
meta.Metrics.RowsRead = s.GetRowsRead()
260+
meta.Metrics.StageID = s.stageID
257261
trailingMeta = append(trailingMeta, *meta)
258262
return trailingMeta
259263
}
@@ -312,13 +316,14 @@ func NewColBatchScan(
312316
kvFetcherMemAcc *mon.BoundAccount,
313317
flowCtx *execinfra.FlowCtx,
314318
processorID int32,
319+
stageID int32,
315320
spec *execinfrapb.TableReaderSpec,
316321
post *execinfrapb.PostProcessSpec,
317322
estimatedRowCount uint64,
318323
typeResolver *descs.DistSQLTypeResolver,
319324
) (*ColBatchScan, []*types.T, error) {
320325
base, bsHeader, tableArgs, err := newColBatchScanBase(
321-
ctx, kvFetcherMemAcc, flowCtx, processorID, spec, post, typeResolver,
326+
ctx, kvFetcherMemAcc, flowCtx, processorID, stageID, spec, post, typeResolver,
322327
)
323328
if err != nil {
324329
return nil, nil, err

pkg/sql/conn_executor_exec.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3359,6 +3359,10 @@ func (ex *connExecutor) execWithDistSQLEngine(
33593359
}
33603360
err = ex.server.cfg.DistSQLPlanner.PlanAndRunAll(ctx, evalCtx, planCtx, planner, recv, evalCtxFactory)
33613361
}
3362+
3363+
if err == nil && res.Err() == nil {
3364+
recv.maybeLogMisestimates(ctx, planner)
3365+
}
33623366
return recv.stats, err
33633367
}
33643368

pkg/sql/distsql/columnar_utils_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func verifyColOperator(t *testing.T, args verifyColOperatorArgs) error {
115115
}
116116

117117
proc, err := rowexec.NewProcessor(
118-
ctx, flowCtx, 0, &args.pspec.Core, &args.pspec.Post, inputsProc, nil,
118+
ctx, flowCtx, 0, 0, &args.pspec.Core, &args.pspec.Post, inputsProc, nil,
119119
)
120120
if err != nil {
121121
return err

pkg/sql/distsql_physical_planner.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212
"reflect"
1313
"sort"
14+
"time"
1415

1516
"github.com/cockroachdb/cockroach/pkg/base"
1617
"github.com/cockroachdb/cockroach/pkg/cloud"
@@ -2258,6 +2259,7 @@ func (dsp *DistSQLPlanner) createTableReaders(
22582259
reverse: n.reverse,
22592260
parallelize: n.parallelize,
22602261
estimatedRowCount: n.estimatedRowCount,
2262+
statsCreatedAt: n.statsCreatedAt,
22612263
reqOrdering: n.reqOrdering,
22622264
finalizeLastStageCb: planCtx.associateWithPlanNode(n),
22632265
},
@@ -2276,6 +2278,7 @@ type tableReaderPlanningInfo struct {
22762278
reverse bool
22772279
parallelize bool
22782280
estimatedRowCount uint64
2281+
statsCreatedAt time.Time
22792282
reqOrdering ReqOrdering
22802283
finalizeLastStageCb func(*physicalplan.PhysicalPlan) // will be nil in the spec factory
22812284
}
@@ -2511,6 +2514,7 @@ func (dsp *DistSQLPlanner) planTableReaders(
25112514

25122515
corePlacement[i].SQLInstanceID = sp.SQLInstanceID
25132516
corePlacement[i].EstimatedRowCount = info.estimatedRowCount
2517+
corePlacement[i].StatsCreatedAt = info.statsCreatedAt
25142518
corePlacement[i].Core.TableReader = tr
25152519
}
25162520

0 commit comments

Comments
 (0)