perf(replace): reduce allocations and vector copies in REPLACE INTO hot path - #24389
Merged
Conversation
- Narrow OldColCaptureList to only downstream-required columns (RowID, PK, index keys) instead of capturing every main-table column. This reduces initCaptureBuffers pre-allocation, probe-side Copy calls, and finalize UnionBatch work proportionally to (total_cols - needed_cols). - Cache hashmap iterator in DedupJoin container to avoid per-batch allocation of 4 internal slices (values/zValues/keys/strHashStates). - Pre-size maps in bind_replace plan generation to eliminate rehashing. - Reuse Packer in LockOp.Prepare instead of allocating a new one on every pipeline reset. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- insert_table: skip UnionBatch copy when InsertCols form a contiguous mapping; build a zero-copy reference batch sharing input vectors. - LockOp: deduplicate lock rows (by encoded key) before calling lockService.Lock, avoiding redundant lock requests and redundant hasNewVersionInRange checks on identical PK values. - DedupJoin finalize: replace per-row UnionOne loop with batch-grouped vector.Union calls (unionSelsByBatch), reducing vector grow/realloc. - DedupJoin finalize (matched==0): transfer build-batch vector ownership to output instead of copying; emit pre-filled NULL for capture columns when no probe hit occurred. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
ouyuanning
approved these changes
May 15, 2026
Contributor
Merge Queue Status
This pull request spent 1 hour 42 seconds in the queue, including 1 hour 3 seconds running CI. Required conditions to merge
|
This was referenced May 25, 2026
mergify Bot
pushed a commit
that referenced
this pull request
May 25, 2026
…24564) Fixes a correctness regression introduced by #24389 in the DedupJoin `matched.Count()==0` fast path (`pkg/sql/colexec/dedupjoin/join.go`). That fast path transfers ownership of build-batch vectors to the output (steal the vector, then `bat.Vecs[rp.Pos] = nil`), which assumes each non-capture build `ResultPos` is unique. But `constructDedupJoin` copies `node.ProjectList` into `ap.Result` without dedup, and the REPLACE planner can alias multiple projections onto the same build column, so a non-capture build position may be referenced more than once. With duplicates, the first projection steals the vector and the second reads `nil`, corrupting the output batch / panicking downstream (`vector.(*Vector).Size`). **Fix:** count references per non-capture build position before the loop and transfer ownership only when a position is referenced exactly once; otherwise copy via `GetUnionAllFunction` so every reference gets its own valid vector. **Test:** adds `TestDedupJoinFinalizeMatchedZero_DuplicateBuildPos`, which projects the same build column twice in the `matched==0` path. It panics on the pre-fix code and passes with the fix. Approved by: @aunjgr
mergify Bot
pushed a commit
that referenced
this pull request
May 25, 2026
…ot path (cherry-pick #24389) (#24560) Cherry-pick of #24389 to 4.0-dev. Three targeted optimizations to reduce overhead in the REPLACE INTO execution path (DedupJoin → LockOp → MultiUpdate): **1. Cache hashmap iterator in DedupJoin probe loop** (`dedupjoin/join.go`) - `ctr.mp.NewIterator()` was called on every batch; now cached in `ctr.cachedItr` and reused across batches, avoiding repeated allocation. **2. Reduce vector copies in DedupJoin finalize** (`dedupjoin/join.go`) - Non-capture build columns: transfer vector ownership (`bat.Vecs[rp.Pos] = nil`) instead of `GetUnionAllFunction` full copy. - Capture columns with no matched rows: emit a pre-filled NULL vector via `AppendMultiFixed` instead of copying from `capturedVecs`. - Replace per-row `UnionOne` loop with `unionSelsByBatch`, which groups selections by build-batch index and issues one `Union` call per group (with a ≤16-sels fast path to avoid grouping overhead for small inputs). **3. Reduce lock and insert overhead** (`lockop/lock_op.go`, `multi_update/insert.go`, `multi_update/types.go`) - `dedupLockRows`: replace map-based dedup with sort + adjacent-compare dedup, eliminating string allocations per lock row. - Reuse `types.NewPacker()` across `Prepare` calls instead of allocating a new one each time. - `insert_table`: detect contiguous `InsertCols` mappings at init time (`isContiguous`); when true, point a cached `refBatch` at input vectors directly (zero-copy) instead of `UnionBatch`-copying into `insertBatch`. **Benchmark (YCSB REPLACE INTO, standalone MO, 100k rows, batchsize=1000):** | threads | main | this PR | vs main | |---------|------|---------|----------| | 32 | 1982.85 ops/s | 2154.52 ops/s | **+8.66%** | | 64 | 1924.70 ops/s | 2042.75 ops/s | **+6.14%** | Approved by: @XuPeng-SH, @aunjgr, @ouyuanning
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What type of PR is this?
Which issue(s) this PR fixes:
issue #23946
What this PR does / why we need it:
Three targeted optimizations to reduce overhead in the REPLACE INTO execution path (DedupJoin → LockOp → MultiUpdate):
1. Cache hashmap iterator in DedupJoin probe loop (
dedupjoin/join.go)ctr.mp.NewIterator()was called on every batch; now cached inctr.cachedItrand reused across batches, avoiding repeated allocation.2. Reduce vector copies in DedupJoin finalize (
dedupjoin/join.go)bat.Vecs[rp.Pos] = nil) instead ofGetUnionAllFunctionfull copy.AppendMultiFixedinstead of copying fromcapturedVecs.UnionOneloop withunionSelsByBatch, which groups selections by build-batch index and issues oneUnioncall per group (with a ≤16-sels fast path to avoid grouping overhead for small inputs).3. Reduce lock and insert overhead (
lockop/lock_op.go,multi_update/insert.go,multi_update/types.go)dedupLockRows: replace map-based dedup with sort + adjacent-compare dedup, eliminating string allocations per lock row.types.NewPacker()acrossPreparecalls instead of allocating a new one each time.insert_table: detect contiguousInsertColsmappings at init time (isContiguous); when true, point a cachedrefBatchat input vectors directly (zero-copy) instead ofUnionBatch-copying intoinsertBatch.Benchmark (YCSB REPLACE INTO, standalone MO, 100k rows, batchsize=1000):