Enforce compaction capacity invariants - #474
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
This run has an inline finding on the stale error-string match in test/scripts/vacuum_concurrent_merge.sh (the tolerated race message no longer matches merge.c's updated ereport text). Beyond that, I also looked at the new unmatched HOLD_INTERRUPTS() added to tp_release_index_lock (src/index/state.c:1286, no RESUME_INTERRUPTS() anywhere in the tree) and at force-merge's handling of single-segment (nonzero) level_counts in src/segment/merge.c — both were examined and ruled out as issues in this run's verification.
Extended reasoning...
One confirmed finding (stale error string in test/scripts/vacuum_concurrent_merge.sh, no longer matching the updated merge.c ereport text) is being posted inline, and additional verified findings from this run are not being surfaced in this review. Given findings are present, per the review protocol I should not approve. I independently re-examined two of the candidate concerns already investigated by the hunting pipeline — the new unmatched HOLD_INTERRUPTS() in tp_release_index_lock (confirmed via grep that no RESUME_INTERRUPTS() call exists anywhere in the codebase, consistent with the pipeline's description) and force-merge's treatment of nonzero single-segment level_counts in merge.c — and found nothing beyond what the pipeline already recorded as ruled out. Given the confirmed and dropped verified findings, a human should review this PR alongside the inline comment.
Summary
Make segment compaction, memtable spill, and force-merge operations fail
before changing physical index state when the current level layout cannot
accept another promoted segment.
This establishes the storage invariants required by later background
compaction work, but contains no scheduler, transaction-dispatch, or
pg_durable integration.
Why
BM25 indexes store immutable segments across levels L0 through L7. A merge
removes segments from one level and promotes a replacement segment to the
next level; L7 is terminal.
These page changes are WAL-logged with
GenericXLog, so they are physicalchanges that do not roll back with the surrounding SQL transaction. Capacity
or terminal-level failures therefore must be detected before the first page
mutation. Discovering an impossible layout partway through a spill or
force-merge could otherwise leave a partially changed index.
Within the current
forceMerge(1)contract, force merge also needs aproduction-enforced result: an empty or all-dead index may have zero segments,
while any other successful force merge finishes with exactly one. Success is
conditional on the live corpus fitting one representable segment.
What changed
increments.
promote because its destination is full, compact the blocking destination
first.
level-count transitions before any physical mutation.
segment with lower-level segments, or an L7 segment plus a term-bearing
memtable that would create another segment.
whether the memtable will actually create an L0 segment before writing it.
accounting is finalized without creating a new segment.
before the force-merge plan executes.
production errors, not assertion-only checks.
exercised without constructing 65,535 segments.
User-visible behavior
Ranking and query results are unchanged. Valid compaction layouts continue to
merge normally. Layouts that cannot make progress now fail atomically with a
specific capacity error instead of failing after partial physical mutation.
bm25_force_merge()succeeds with:segment.
This PR covers segment-count capacity and force-merge execution invariants. It
does not make every large index force-mergeable: #473
tracks consistent fail-safe enforcement of the existing per-segment format
limits, and #456
tracks bounded/split merges or a wider format for corpora that cannot fit in
one segment.
Test coverage
spills.
document accounting and query results.
capacity races.
regressing into assertion-only checks.
Stack position
This is the bottom, independently mergeable layer of GitHub stack #479.
Later layers add the public APIs, transaction dispatch, and optional
pg_durable integration.