Repository navigation
[PR] fix: defer large-db integrity quick_check past listen so big stores fit the 60 s spawn budget (#4609) - #4619
ranxianglei wants to merge 12 commits into
Conversation
…ath (#4609) A 14 GB index.db took 260 s for PRAGMA quick_check inside UnifiedStore.open, always exceeding the launcher's hard 60 s spawn budget: the child was killed while still scanning and the host stayed on direct (uncompressed) for its whole lifetime, with every retry failing identically. Three-lane decision at open: - unchanged since a passing check (size+mtime fingerprint in index.db.quickcheck) -> skip; - <= 256 MiB -> inline check exactly as before (typed throw, shadow quarantine + retry-once); now stamps the fingerprint on pass; - > 256 MiB -> bind first, scan in a background worker thread with a readonly second connection (both engines are sync-only APIs; running it inline would move the freeze onto live traffic). A deferred failure disables unified reads for the process lifetime (JSON envelopes remain the recovery source), writes an index.db.quarantine-pending marker, and the NEXT open quarantines the file set before opening fresh (no live handle -> no Windows EBUSY). An inconclusive worker death records nothing and retries next open. BILI_STORAGE_QUICK_CHECK=0 still skips everything. No new config surface.
…rotocol) - sync lane passes and stamps the fingerprint; unchanged reopen skips (no new integrity record); malformed fingerprint re-checks; BILI_STORAGE_QUICK_CHECK=0 skips everything - deferred lane: open returns before the scan, verdict arrives via store.deferredQuickCheck; pass stamps cache + status wire - deferred failure: callback fires once, marker written, next open quarantines the file set (.corrupt-*) and opens fresh - shadow sink: maybeOpen flips stats.disabled with a lastError naming the failure, unified reads off, envelopes remain the recovery source - NOTADB file yields a genuine ok:false verdict (worker statement error), while a dead worker stays inconclusive (null, no marker, retry next open) The suite lowers the 256 MiB split point through the existing *ForTests seam so fixtures stay small; production threshold is untouched.
…n+zh The BILI_STORAGE_QUICK_CHECK row now states the actual behavior after #4609: fingerprint skip, <=256 MiB inline (unchanged failure protocol), >256 MiB deferred behind listen with the marker + sink quarantine path.
…te (#4609) The WORKER_SOURCE string is bundled VERBATIM into dist/index.js, where scripts/check-dist-external.mjs text-scans for quoted-require forms. The literal require("node:sqlite") inside it tripped the gate (node:sqlite is not in module.builtinModules on Node 22, so it is not skipped as a builtin). Route the specifier through a named const — the checker documents opaque specifiers as invisible by design. Same gate class as #3511/#3516/#4008.
) CONFIGURATION.md / CONFIGURATION.zh-CN.md are generated artifacts of tools/gen-config-docs.mjs — update the BILI_STORAGE_QUICK_CHECK seed entry (website/config-reference/env-only.yaml) to the three-lane behavior (skip by fingerprint / inline <=256 MiB / deferred-after-listen >256 MiB) and regenerate, keeping the docs-sync gate green.
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.6170-pr.4619.df5a24dThis exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4619.tgz
npm install -g packageInstall instructions are refreshed on each push. |
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) 查重: 不重复(与 PR 正文自查一致,我独立核过):后果家族同 #4515(启动 I/O 超启动器 60 s spawn 预算 → 子进程被杀 → 宿主静默直连),机制不同——本 PR 修的是 #4175 引入的整库 标签: CI 快照(head 正在做完整 review(方向/回归/代码/测试/diff)+ 定向测试(issue4609 + storage-durability + storage-full-flip),结构化八问结论随后补上。 |
(ai: vllm-qwen/qwen3.8-27b) CI 自动修复通道收尾(attempt 2/2):状态未变,维持「继承性失败」判定,本分支不推代码。 复核结果(与上条判定一致):
为什么不推单行去导出:ci.yml fail-fast 分层下,unused-exports 一红整个 test 层被 skip;若单独推掉这一行,test 矩阵随即撞上另一条 master 常红(LIVE_FILES 漏登记,#4604),PR 仍红,只白烧一整轮 CI。两条红都在 #4606 里,只有它合入才能一次转绿。 解除条件:#4606 合入 master → 本 PR 重触发 CI(CI 检查 merge ref,自动带上修复)→ 预期全绿。无需改动本分支,也无需 rebase(base 未动)。#4599 已留继承记录,不再重复。 |
(ai: vllm-qwen/qwen3.8-27b) PR review 结论(代码已逐段读完,本地定向验证已过) 八问
测试有效性(④):测试有牙——真实字节翻转损坏(文件中段三区域)+ 自验证前置( CI 状态:head dc43e97 唯一红 = 判定:代码质量达标、方向正确、本地定向验证全绿;当前不可合的唯一阻塞是继承性红。建议:先定第 5 问缺口处置(补 sink 或记残余),继承红清除后 CI 转绿即可合。我不关单、不合 PR,等 owner 验收。 |
…elp-merge sweep)
…elp-merge sweep)
…elp-merge sweep)
master window commits 293ae65/de5e03659 bumped kernel/package.json to 0.0.165 without regenerating the lockfiles; lock-sync re-resolution then rewrites the stale recorded versions (root 0.0.163, kernel 0.0.164) and fails. Pure recorded-version sync via npm install --package-lock-only; no dependency or semantic change.
Closes #4609
查重: 不重复 — 与 #4515 同属「启动 I/O 超过启动器硬性 60 s spawn 预算 → 子进程被杀 → 宿主静默直连」后果家族,但机制不同:#4515 是
persist.boot()解码 legacy JSON 语料(可缓存预热);本 issue 是 0.1.205 引入(#4175)的整库PRAGMA quick_check,与缓存无关(14 GB > RAM,每次开库全量重扫;0.1.204 同一库 0.75 s 即绑定)。与 #4125(端口占用)、#4516(直连回退缺少 oversized 拒绝,下游面)均不同。What was wrong
UnifiedStore.openranPRAGMA quick_checksynchronously BEFORE listen, on every open. Time ∝ index.db size: a 14 GB store took 260 s against the launcher's hard 60 s spawn budget (SPAWN_WAIT_MS=20000 + SLOW_START_GRACE_MS=40_000, src/launcher.ts) → child SIGKILLed mid-scan → bootstrap failed → the whole host process lifetime went direct (uncompressed), retrying every ~70 s with identical failure; restarts could not help because the verdict was never cached.Fix — three-lane decision at open
index.db.quickcheck): no scan at all. Malformed fingerprint → re-check (fail toward checking).StorageIntegrityErrorthrow → shadow quarantine + retry-once; behavior byte-identical); now stamps the fingerprint on pass.index.db.quarantine-pendingwritten — the NEXT open quarantines the file set PRE-open (no live handle → no Windows EBUSY, windows-22-1 lane randomly red: during test cleanup, unlink index.db hit an EBUSY external lock, couldn't survive tmp-rm | windows-22-1 车道随机红:测试清理期 unlink index.db 遇 EBUSY 外部锁,熬不过 tmp-rm 的 9.6 秒重试梯子(#2799/#2908 族复发) #4347) and opens fresh;BILI_STORAGE_QUICK_CHECK=0still skips everything. No new config surface (the config-key question stays open in-thread per Configuration Surface Discipline — not built here).Split point rationale: field-measured scan rate ≈ 54 MB/s (14 GB / 260 s) → worst-case inline freeze ≈ 5 s at 256 MiB, comfortably inside the budget with margin; below it the pre-#4609 synchronous guarantee (never serve from a corrupt index) is preserved exactly.
Behavior changes (old → new)
open(), before listen — O(db) blocks the bindindex.db.quickcheck(fingerprint), +index.db.quarantine-pending(marker, transient; deliberately extensionless — session-dir*.jsonwalks treat JSON files as session state)/__bili/statusintegrity fieldConfig surface
No change:
BILI_STORAGE_QUICK_CHECKkeeps its meaning (opt out of all checks); no new fields/env vars/CLI flags added.Tests
tests/issue4609-quick-check-defer.test.ts(9 tests, ~1.2 s) — decision matrix driven through the existing*ForTestsseam (production threshold untouched): skip / sync / env-opt-out lanes; deferred pass (open returns before the scan, verdict arrives later); deferred-failure protocol end-to-end (callback fires once → marker → next open quarantines.corrupt-*→ fresh db); shadow sink flip (stats.disabled+lastErrornaming the failure); negative controls both directions — NOTADB file = genuineok:falseverdict vs dead worker = inconclusivenull(no marker). Pre-existing #4175 pins stay green:tests/storage-durability.test.ts,tests/storage-full-flip.test.ts(one initial regression of mine there — the fingerprint used to be.json, which session-state dir walks count — fixed by making the sidecar extensionless). Full-suite verdict: CI on the pushed head.Related