Repository navigation
[PR] fix(storage): refuse gc full-VACUUM while another live process holds the db (#4612) - #4628
Conversation
…the db (#4612) Field report (bili 0.1.204, 14 GB index.db): an online 'storage gc' (vacuum=full) held the exclusive write lock across the whole O(db-size) VACUUM rewrite for 14 minutes with zero progress output, starving the running proxy until its unified shadow layer degraded (3 consecutive 'database is locked' failures -> legacy-only for the process lifetime). - UnifiedStore.activeForeignWriter(): union of the open-time captured #4175 lock holder (revalidated for liveness) and a fresh <db>.lock read (catches an opener that started after us); fail-open on unreadable file. - UnifiedStore.gc(): vacuum:'full' refuses up front when a live foreign writer is present (actionable error naming the pid); incremental / no-vacuum sweeps are short transactions and stay allowed. - Zero-ref sweep now commits in bounded batches (rowid subselect; SQLite DELETE takes no LIMIT) with per-batch progress lines; batch grows with the table so log volume stays bounded. Crash-safe: rows are garbage by definition, leftovers are collected by the next sweep. - Full-vacuum sizing logs: main/WAL size before, elapsed + size delta after. - CLI lane surfaces the refusal as exit 1 + actionable text; panel endpoint returns 409. Dry-run stays read-only.
Dummy live process (spawned node child) stands in for the running proxy: open-time holder capture, later-opener lockfile detection, liveness re-validation on exit, incremental/dry-run exemptions, batched zero-ref sweep outcome + progress lines, and the CLI refusal surface (code 1, actionable text, json fields).
README en+zh: 'storage gc --vacuum full' refuses to start while another live process holds the database (CLI exit 1 / panel HTTP 409); use --vacuum incremental for online-safe shrink.
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.5626-pr.4628.36238b1This exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4628.tgz
npm install -g packageInstall instructions are refreshed on each push. |
The new gc online-guard test spawns an idle node child as its writer fixture; the run-ci-tests SPAWN_RE tripwire requires every such suite in LIVE_FILES or it aborts all shards before any test runs (#4604 class).
…ted red) Unused-exports gate fails on every PR head rebased onto master tip 146b34f with the same line master itself fails on: INTERNAL_ONLY src/agent/client-build-note.ts: ClientBuildCheck (#4545 train left the type exported though nothing outside the file imports it; missed by the 4d34abf de-export sweep). One-word fix, identical to #4606 commit 1; drop this commit when rebasing after #4606 merges.
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复——本次是 CI 自动修复车道(attempt 1/2),失败签名 结论先行: 这条红是 master 继承性失败,不是本 PR 代码的问题。已在分支上以独立标注小修 commit 修掉(一词去 export),等 CI 重触发确认。 验证过程
处置
结构化八问
收工判定:本车道交付完成—— |
(ai: vllm-qwen/qwen3.8-27b) 查重结论:不重复。 #4628 是修复 #4612 的唯一 PR。相关但不同的条目:#4565(纯文档,给 website Commands 段补 先给初步核查(完整八问结论随后补,正在收尾验证):
按 CI 协调纪律(#4599):继承性的 master 红不在本 PR 里顺手修、也不为此新开 issue——我会在 #4599 留一行记录,等 master 侧修好后 rebase 本 PR 即自动转绿。 接下来我会:① 复核 master 的 GitHub CI run 坐实「master 也红」;② 过完 persist / admin / shadow / storage-maintenance / 测试文件五个 diff 做回归审查;③ 本地定向跑 |
(ai: vllm-qwen/qwen3.8-27b) 结论先行:#4612 的修复本身正确、完整、非回归,代码可合。当前 CI 红全部来自 master 自带的两处「born-red」CI 缺陷,不是本 PR 引入。 其一( 查重:不重复(见上一条回复)。 我这轮做了什么:分支已从 issue 正文的 4-commit 版推进到 head 结构化八问
CI 现状(head
给 owner 的决策请求(两选项对 #4612 的最终合入代码完全一致,只差 master born-red 在哪落地;差异在干净度 vs 速度)
要执行 B(补那一行)说一声,我马上推。 |
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复——本次转发指向旧 head 当前 head
结构化八问
收工判定:auto-fix 车道 2/2 用尽,交付完成——转发点名的 |
What & why
Field report (bili 0.1.204, 14 GB index.db, #4612): running
bili storage gconline while a proxy serves from the same sessions dir — the default--vacuum fullrewrites the whole database under an exclusive write lock for O(db size) with no intermediate progress (SQLite VACUUM is atomic). The user saw 14m25s of silence, and meanwhile the live proxy's unified shadow layer got 3-striked out (database is locked×3 → legacy-only for its whole process lifetime).This PR implements the issue's proposals 1+2+3:
UnifiedStore.gc()refuses to start a full VACUUM when another live process holds the db, reusing the existing Storage Reliability Trio: open-time self-check (quick_check) + online backup (VACUUM INTO) + second-process probe on the | 存储可靠性三件套:开库自检 quick_check + 在线备份 VACUUM INTO + 同库第二进程探测 #4175<db>.lockpid discipline: the holder captured at open (revalidated for liveness at gc time) ∪ a fresh lockfile read (catches an opener that started after us). The CLI exits 1 with an actionable message naming the pid; the panel endpoint returns HTTP 409. No new flags/env/config surface.gc zero-ref sweep: 12000/82856 rows (14%)); the batch grows with the table so log volume stays bounded. Full-vacuum start/finish lines report main+WAL size and elapsed time.--vacuum incrementalfor online-safe shrink.Behavior changes (old → new)
storage gc --vacuum fullwith a live foreign writerUnchanged by design:
--vacuum incrementaland no-vacuum sweeps stay online (short transactions, existing busy-timeout handling); dry-run stays read-only even with a writer present;sessions rm's graceful busy-skip of its shrink step; the lockfile protocol itself.Known residual (pre-existing, out of scope): the lockfile is single-slot last-writer-wins, so if an unrelated opener clobbers the file between the peer's start and our open, the peer is undetectable via this signal — same limitation #4175's open-time detection already accepts.
Tests
New
tests/issue4612-gc-online-guard.test.ts(a spawned node child stands in for the running proxy): open-time holder capture + refusal + incremental exemption + liveness re-validation on exit; later-opener detection via fresh lockfile read; batched sweep outcome + ≥2 progress lines ending at 100%; CLI code 1 / json fields / dry-run exemption / success after exit. Local: typecheck + build clean; targeted suites green (issue4612 + storage-maintenance-cli + storage-archive-gc + storage-backend-contract + storage-review-p2s = 29 pass, 1 env-gated skip). Full-suite verdict: CI.Closes #4612