Quiesce workers before dropping RocksDB tables - #2206
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements a robust table drop quiescence mechanism to safely coordinate table drops across multiple worker threads. It ensures that all in-flight operations, active write transactions, and read iterators against the target table are fully drained and closed before any destructive column family drops occur. This is achieved by introducing strict inter-thread communication (ITC) barriers and tracking process instance IDs to handle restarts safely. Comprehensive unit tests are added to verify this quiescence behavior. The review feedback suggests improving the robustness of the test teardown blocks in the new test file by declaring worker variables outside the try blocks and guarding their cleanup in finally blocks with existence checks and error catching to prevent flakiness or error masking during partial startups.
|
Reviewed; no blockers found. |
c1384db to
890164b
Compare
| entriesScanned++; | ||
| await rest(); | ||
| if (reverseScanned >= limit) break; | ||
| if (isRocksDB && droppingTable) throw tableDroppingError(); |
There was a problem hiding this comment.
Suggestion (non-blocking): This isRocksDB && guard (added across 7 mid-scan cancellation points in this push, e.g. lines 4980, 5020, 5347, 5358, 5398, 5409, 5432) correctly restores pre-existing LMDB behavior — good fix. But unitTests/resources/dropTableQuiescence.test.js skips entirely under HARPER_STORAGE_ENGINE=lmdb (line 117), and no other test exercises "an LMDB table scan continues uninterrupted while a drop is in progress." Since this exact regression (RocksDB-only quiescence checks leaking into the LMDB scan path) was just found and fixed here, a small regression test running an LMDB scan concurrently with dropTable() and asserting it completes without ERR_TABLE_DROPPING would guard against it recurring.
890164b to
d29ccf8
Compare
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Track transaction-less table scans and clears so drop waits for their native handles. Gate strict worker broadcasts on ITC readiness and reject malformed events instead of acknowledging them. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Cancel client-paced and abandoned direct scans when a RocksDB table begins dropping, while retaining labeled drain tokens for diagnostics. Scope ITC validation to owned events and publish worker readiness through an atomic signal so coordinator observation cannot lag store opening. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Reject a post-drop next() before the generator can re-enter its closed native iterator, and document the tracked-iterator attribution invariant. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Avoid sending the internal readiness envelope to raw Node workers, where it can be mistaken for an application reply. Keep resource-test worker fixtures safe when Mocha imports them without a parent port. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
d29ccf8 to
b558fb1
Compare
Problem
#1381 — Cross-worker write can race RocksDB table drop and poison catalog cleanup is an ordering failure, not an already-absent-column-family cleanup failure.
The failing integration run showed one worker dropping a table while another worker committed a background source-cache write through a retained handle. RocksDB rejected the late batch with
Invalid column family specified in write batch; that shared write-path failure then prevented catalog cleanup.The fix must establish this invariant before
dropSync()runs:Fix
This PR adds a generation-stamped, cross-worker quiescence barrier for shared RocksDB table drops.
dropping,dropGeneration,dropQuiesced, and a random process-incarnation marker in the primary catalog row.update-attributeslock, revalidate the generation, setdropQuiesced, synchronously drop the column families, then remove only the matching catalog generation.The supporting transaction hardening closes the drain boundary:
Human review decisions
LOCK_TIMEOUTWorkerexit counts as quiesced; an ambiguous siblingMessagePortclose NACKsERR_TABLE_DROPPINGrejects any write to the dropping store, including source-applied transactionsAsyncLocalStoragecontextworkerData/environmentTwo bounded review notes remain: a stuck-drop preparation intentionally retains its table class until the mandated restart, and write transactions pay one
WeakRef/Setenrollment so the drain can find writes staged before the drop began. Gating enrollment only after a drop starts would miss exactly those pre-existing writes.Validation
unitTests/resources/dropTableQuiescence.test.js: real worker-thread coverage for local/remote reads, staged writes, source-cache writes, coordinator failure, NACK, worker exit, readiness, aliases, interrupted recovery, and every direct operation token.integrationTests/apiTests/blob.test.mjs: 22 passing.integrationTests/apiTests/delete.test.mjs: 76 passing, including the numeric-string table drop from the original CI failure.node_modules/package-lock.jsonselectstructon@1.0.7, while the committed branch requires coherentstructon@1.0.8. The protected local lockfile change is not committed or pushed.npm run build,npm run lint:required,npm run format:check, andgit diff --check: pass.c1384db15dc4passed for Node 22/24/26, Bun, Windows, uWS, and the downstream Next.js adapter. Two unrelated Windows dependency-child hangs and one apt-mirror stall passed on bounded isolated reruns; no task-related CI failure remains.Review coverage
GPT-5 Codex authored the changes. Nineteen pre-push rounds used an independent Claude graded reviewer plus Harper-domain adjudication; major fixes restarted whole-diff review, and the exact
c1384db15dc4engine-scope delta was re-reviewed successfully. The LMDB-scan finding is resolved. The remaining major labels are the explicit restart/timeout and source-apply policy decisions above. Gemini and both Cursor legs were pruned from the final exact-SHA run.Related work
This supersedes #2168 — Fix interrupted RocksDB table drops, which addresses the same race through a different design. Its dependency on rocksdb-js #787 — Serialize database destruction with concurrent opens concerns database destroy/open serialization; this PR handles live column-family handles before an individual table drop.
This also narrows #1276 — Follow-up: residual partial-failure risks in table-drop hardening by generation-guarding catalog cleanup. It does not claim to solve every broader create/drop rollback policy.
Refs #1381
Supersedes #2168
Generated by GPT-5 Codex.
Review-Coverage: authored=unknown; ran=none; rounds=1 @ d29ccf8
Human-Review-Need: 4 @ d29ccf8