perf: single-pass contiguous DELETE + BTree separator-delete fix - #361
Merged
Conversation
added 3 commits
September 3, 2026 09:18
…+ fix BTree.Delete separator corruption B9: Table.TryBulkDeleteContiguousFixedWidth mirrors the UPDATE fast path: strictly ascending pk=literal DELETEs on a plaintext fixed-width columnar table with physically adjacent records remove every PK/hash-index entry in one pass (one contiguous range read, indexed columns decoded from raw slots, no full-row deserialization). Same narrow gate as the UPDATE path; any mismatch falls back to the generic loop before anything is removed. Latent correctness bug found while validating: BTree.Delete removed separator keys from INTERNAL nodes without fixing the child-pointer mapping, so after bulk deletes whole ranges became unreachable (search could not descend to them). Internal separators are now replaced by their in-order successor from the right subtree's leftmost leaf (leaf underflow is harmless); empty-neighbour fallbacks keep the pointer count consistent. This also fixes the same hazard for the generic per-row delete path.
…ly every row) Locks in the fix for the large descending-batch UPDATE no-op observed while building the contiguous fast paths: the fast path refuses descending keys and the generic per-row loop now applies all rows.
…nd DELETE fast paths Extracts TryReadContiguousFixedWidthRecords (PK adjacency verification + one range read + length-prefix check) so the B8 UPDATE and B9 DELETE bulk paths no longer duplicate the probe (Sonar duplication gate).
|
This was referenced Sep 3, 2026
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.



Summary
B9: single-pass contiguous DELETE fast path — the DELETE mirror of #360's UPDATE fast path. Strictly ascending
pk = <literal>DELETEs on a plaintext fixed-width columnar table with physically adjacent records remove every PK/hash-index entry in one pass: one contiguous range read + indexed columns decoded from the raw slots (no per-row pread, no full-row deserialization). Same narrow gate; any mismatch falls back to the generic loop before anything is removed.Latent correctness fix (found while validating)
BTree.Deleteremoved separator keys from internal nodes without repairing the child-pointer ↔ separator mapping. After bulk deletes, whole ranges became unreachable — a generic-path hazard too (deleting internal separators corrupts traversal for any sizable delete, not just the new fast path). Fix: internal separators are now replaced by their in-order successor from the right subtree's leftmost leaf (leaf underflow is harmless), with empty-neighbour fallbacks that keep the child count consistent.Measured (AppendOnly
--pk, default = fixed-width)Tests
Full suite 1652/1652 (+2
FixedWidthBulkDeleteTests): contiguous DELETE engages twice (two batches over 1-2000) and removes rows from PK and hash-index paths (including a TEXT hash index, decoded through the overflow arena); gapped/odd-id DELETEs fall back to the generic loop and stay correct.BulkContiguousDeleteBatchesdiagnostics prove engagement.Note: a pre-existing
SELECT COUNT(*)-after-DELETE discrepancy (generic and bulk alike) surfaced while testing; the tests assert index-path correctness (per-key point + hash lookups) rather than COUNT(*). Separately tracked.