Skip to content

Commit e47ca5f

Browse files
committed
sql: avoid contention between long mutation and auto stats
Previously, when processing large mutations in batches (see `BatchedNext` method), we would notify the stats refresher about the number of rows affected after each batch. If the mutation is long, then this could have triggered the auto stats collection that would contend with the mutation that triggered it. There is actually no reason to try collecting auto stats on those just-mutated rows since in most cases they haven't been committed yet. This commit fixes this issue by notifying the stats refresher about the total number of rows affected when the last batch of the mutation has just been processed. The bug has been present since about 19.1 version. Note that we generally don't perform well with long mutations, but it seems like a worthwhile improvement nonetheless. We can't remove contention with auto stats triggered by _other_ mutations, but at least we can do so for self-inflicted ones. (Also probably a more important retry reason is the closed TS interval.) Release note (bug fix): Large mutation statements (INSERTs, UPDATEs, DELETEs, UPSERTs) are now less likely to encounter contention with automatic table stats collection in some cases. The bug has been present since 19.1 version.
1 parent 15af2b5 commit e47ca5f

File tree

5 files changed

+23
-13
lines changed

5 files changed

+23
-13
lines changed

pkg/sql/colexec/insert.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ type vectorInserter struct {
4545
mutationQuota int
4646
// If auto commit is true we'll commit the last batch.
4747
autoCommit bool
48+
// rowsWritten tracks the number of rows written by the vectorInserter so
49+
// far.
50+
rowsWritten int
51+
// statsRefresherNotified is set once we notify the stats refresher.
52+
statsRefresherNotified bool
4853
}
4954

5055
var _ colexecop.Operator = &vectorInserter{}
@@ -123,6 +128,15 @@ func (v *vectorInserter) Next() coldata.Batch {
123128
ctx := v.Ctx
124129
b := v.Input.Next()
125130
if b.Length() == 0 {
131+
if !v.statsRefresherNotified {
132+
// We've just exhausted the input, so let's notify the stats
133+
// refresher.
134+
// TODO(yuzefovich): when auto-commit enabled, the inserted rows
135+
// will be visible sooner than at the end. Is it worth notifying the
136+
// stats refresher earlier in that case?
137+
v.flowCtx.Cfg.StatsRefresher.NotifyMutation(v.desc, v.rowsWritten)
138+
v.statsRefresherNotified = true
139+
}
126140
return coldata.ZeroBatch
127141
}
128142

@@ -200,7 +214,7 @@ func (v *vectorInserter) Next() coldata.Batch {
200214
v.retBatch.ColVec(0).Int64()[0] = int64(b.Length())
201215
v.retBatch.SetLength(1)
202216

203-
v.flowCtx.Cfg.StatsRefresher.NotifyMutation(v.desc, b.Length())
217+
v.rowsWritten += b.Length()
204218

205219
return v.retBatch
206220
}

pkg/sql/delete.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,10 @@ func (d *deleteNode) BatchedNext(params runParams) (bool, error) {
159159
}
160160
// Remember we're done for the next call to BatchedNext().
161161
d.run.done = true
162+
// Possibly initiate a run of CREATE STATISTICS.
163+
params.ExecCfg().StatsRefresher.NotifyMutation(d.run.td.tableDesc(), int(d.run.td.rowsWritten))
162164
}
163165

164-
// Possibly initiate a run of CREATE STATISTICS.
165-
params.ExecCfg().StatsRefresher.NotifyMutation(d.run.td.tableDesc(), d.run.td.lastBatchSize)
166-
167166
return d.run.td.lastBatchSize > 0, nil
168167
}
169168

pkg/sql/insert.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,10 @@ func (n *insertNode) BatchedNext(params runParams) (bool, error) {
360360
}
361361
// Remember we're done for the next call to BatchedNext().
362362
n.run.done = true
363+
// Possibly initiate a run of CREATE STATISTICS.
364+
params.ExecCfg().StatsRefresher.NotifyMutation(n.run.ti.tableDesc(), int(n.run.ti.rowsWritten))
363365
}
364366

365-
// Possibly initiate a run of CREATE STATISTICS.
366-
params.ExecCfg().StatsRefresher.NotifyMutation(n.run.ti.tableDesc(), n.run.ti.lastBatchSize)
367-
368367
return n.run.ti.lastBatchSize > 0, nil
369368
}
370369

pkg/sql/update.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,10 @@ func (u *updateNode) BatchedNext(params runParams) (bool, error) {
168168
}
169169
// Remember we're done for the next call to BatchedNext().
170170
u.run.done = true
171+
// Possibly initiate a run of CREATE STATISTICS.
172+
params.ExecCfg().StatsRefresher.NotifyMutation(u.run.tu.tableDesc(), int(u.run.tu.rowsWritten))
171173
}
172174

173-
// Possibly initiate a run of CREATE STATISTICS.
174-
params.ExecCfg().StatsRefresher.NotifyMutation(u.run.tu.tableDesc(), u.run.tu.lastBatchSize)
175-
176175
return u.run.tu.lastBatchSize > 0, nil
177176
}
178177

pkg/sql/upsert.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,10 @@ func (n *upsertNode) BatchedNext(params runParams) (bool, error) {
133133
}
134134
// Remember we're done for the next call to BatchedNext().
135135
n.run.done = true
136+
// Possibly initiate a run of CREATE STATISTICS.
137+
params.ExecCfg().StatsRefresher.NotifyMutation(n.run.tw.tableDesc(), int(n.run.tw.rowsWritten))
136138
}
137139

138-
// Possibly initiate a run of CREATE STATISTICS.
139-
params.ExecCfg().StatsRefresher.NotifyMutation(n.run.tw.tableDesc(), n.run.tw.lastBatchSize)
140-
141140
return n.run.tw.lastBatchSize > 0, nil
142141
}
143142

0 commit comments

Comments
 (0)