Skip to content

[PR] fix(storage): a live second writer no longer blocks startup — sharing a local-disk index.db is a supported topology (rev | fix(storage): 活着的第二写者不再阻止启动——共享一个本地盘 index.db 是受支持拓扑(撤回 #5213 的拒绝策略) - #5294

Merged
ranxianglei merged 1 commit into
masterfrom
fix/2026-10-31_second-writer-no-block
Oct 31, 2026

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

这是什么问题

2026-10-31,机器上多个 bili 实例(不同 profile / inline 车道)共享同一个默认 sessions 目录时,新起的进程直接拒绝启动(例如 bili pi -r 恢复会话),报错 "another bili process already writes index.db … REFUSED TO START until isolated"。机器上已经有十几个常驻 bili 时,这条拒绝等于把「恢复会话 / 重启」这条路整个堵死。该拒绝策略来自 PR #5213 引入的 second-writer refuse-to-start。

根本原因

拒绝逻辑的判断是「只有对端 lane ≠ 自己的 lane 才允许共存」。但 inline 车道从不在实例登记簿里注册,旧版本进程写的登记标记里也没有 lane 字段——于是「对端 lane 未知」被一律当成危险双写形状 → 拒启。而事实上:

owner 决定(2026-10-31):无论什么 lane 情况,活着的第二写者都不再阻止启动——只提示。

改了什么

  • src/storage/store.ts:UnifiedStore.open 删除 secondWriterPolicy:"degrade" 选项和 crossLaneSafe 判断。任何活着的对端持有者现在只产生一行 info 日志(写明 lane/instanceId)+ 状态线记录(web/状态面板可见 peer pid),随后照常打开数据库、照常执行 lockfile 纪律(死 pid 依旧按 stale lock 接管)。
  • src/storage/shadow.ts / src/storage/runtime-status.ts / src/storage-maintenance.ts:删除三处拒绝分支及对应 degrade/status 文案。full-VACUUM 的在线护栏原样保留(storage gc --vacuum full 在有活进程持库时仍然拒绝,走 runGc 内自己的 guard)。
  • StorageSecondWriterError 保留导出并标注 @deprecated(外部代码可能还在 import/catch 它;本仓库内部不再有 throw 路径)。
  • 文档:CONFIGURATION.md(EN/ZH seed + 生成物重新生成,check-coverage 通过)、README「Running multiple instances / Single machine, multiple installs」段落按新政策改写。
  • 测试重写:tests/issue5046-second-writer-degrade.test.ts 换成 never-block 契约,其中「同 lane 活持有者」「无 lane 字段的旧版标记」这两个曾经会触发拒启的形状各留一条回归钉子;tests/multi-instance-live.test.ts 同步更新。

测试 / 验证

  • typecheck 干净(tsc --noEmit exit 0)。
  • 定向套件全绿:issue5046-second-writer-degrade + multi-instance-live = 7/7;issue4612-gc-online-guard + storage-maintenance-cli = 11/11。
  • 提交前另起了一组约 50 个存储相关测试文件的后台批跑,结果出来补在这里。
  • 真机验证:全局安装的 bili 已切到本提交构建,此前卡死的 bili pi -r 恢复路径实测可正常启动,日志只多一行 info。

能否合并

可以合。性质是策略回退 + 文档/测试跟进:无 schema 变更、无协议变化、唯一的行为差异就是 second-writer 条件从「拒启」降为「提示」,且提示信息比原来更全(web 状态线也带上 peer)。真正的分裂脑防护(#3923 per-session 告警)不受影响。合并等 owner 操作(非人类不合并)。

Relates to #5046, #5082, #5091, #3923, #5213.

…decision 2026-10-31)

Sharing one local-disk index.db IS the supported topology (#5082/#5091):
SQLite WAL is multi-process safe locally, so a peer holding <db>.lock is
informational-only now (info log + web/status wire) whatever its lane —
inline lanes never register, so 'unknown lane' was routinely mistaken for
risky and bricked resumes (the 2026-10-31 `bili pi -r` boot failure,
#5213's refuse-to-start withdrawn). Withdrawn call sites: UnifiedStore.open
secondWriterPolicy:'degrade' opt-in (option removed; crossLaneSafe math
deleted), shadow.ts open-catch refusal + recordSidecarOpenFailure line,
runtime-status degradeReason verbatim branch, storage-maintenance gc catch
(the full-VACUUM starvation guard in runGc via store.unifiedActiveWriter()
is untouched). StorageSecondWriterError stays exported (@deprecated) so
external import/catch sites keep compiling. Genuine hazard unchanged: two
instances walking the SAME session still alarm via the split-brain contest
(#3923). Tests rewritten to the never-block contract incl. regression
pins for both formerly-refused shapes (same-lane, unknown-lane fleet).
Docs: CONFIGURATION EN/ZH seed + README multi-instance section updated.
@ranxianglei ranxianglei changed the title fix(storage): 活着的第二写者不再阻止启动——共享一个本地盘 index.db 是受支持拓扑(撤回 #5213 的拒绝策略) [PR] fix(storage): 活着的第二写者不再阻止启动——共享一个本地盘 index.db 是受支持拓扑(撤回 #5213 的拒绝策略) Oct 31, 2026

@ranxianglei ranxianglei left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查结论:代码通过,无阻塞问题。CI 绿后即可人工合并(owner 操作)。

按「政策撤回是否撤干净」逐面核过:

1. 核心机制自洽(src/storage/store.ts)

  • UnifiedStore.open 不再接收 secondWriterPolicy,任何 lane 形状(同 lane / 跨 lane / 无 lane 旧标记)下活着的第二写者只产生一行 info 日志 + recordSecondProcessOwner 状态线,随后照常开库、照常走 lockfile 纪律——死 pid 仍按 stale lock 接管。这正是 #5082/#5091 确立的受支持拓扑该有的行为。
  • 与「在任何情况下不降级」(2026-10-30 政策)没有冲突:那条政策管的是环境类打开失败(no-engine/CANTOPEN/readonly/DSN),这些分支在 shadow.ts 里原样保留、仍然拒启;被撤回的只是 second-writer 这一个条件,CONFIGURATION.md 的 QUICK_CHECK 条目(EN/ZH/生成物)也同步把它移出了拒绝清单,口径一致。

2. 悬挂引用清零(全库 grep 验证)

  • StorageSecondWriterError 现在只剩:类定义 + @deprecated 说明(保留导出给外部 import/catch)、测试钉、注释历史引用。仓库内部无 throw、无 catch、无 secondWriterPolicy 调用点。
  • runtime-status.ts 删掉的 second-writer: 前缀透传分支对应的唯一写入源已不存在,删除正确。

3. full-VACUUM 在线护栏未被误伤

  • storage-maintenance.ts 删掉的只是 constructor 抛错时的特判分支;护栏本体在 runGc 内(store.unifiedActiveWriter(),现 L221),活写者持库时 --vacuum full 照旧拒执行。定向测试 #4612: CLI 'storage gc --vacuum full' exits 1 with actionable refusal while a writer is live 本地绿。

4. 测试重写是契约翻转而非放松

  • 两个曾经会拒启的形状(同 lane 活持有者、无 lane 字段的混版本标记)各留一条回归钉子,断言从「必须 THROW + 锁不被偷 + 可操作错误」翻转为「必须打开 + info 提示在 + 锁接管行为不变 + 状态线带 peer pid」——把「永不阻断」和「依然响亮」两侧都钉死了。
  • multi-instance-live.e2e 恢复了 #394/#405/#2401 的 serve/guard/converge 交织(两个 full-mode 实例共享同一个活 index.db),本地 2.2s 通过。头注释里「证明什么/不证明什么/残留 pin 在哪」的边界写得清楚。

5. 同步状态与本地验证

  • 分支落后 master 11 个提交(#5253/#5292 两个 merge),文件零重叠;本地临时合并后 tsc 干净、9 个存储相关套件 54 例全绿(issue5046-second-writer-degrade / multi-instance-live / issue4612-gc-online-guard / storage-maintenance-cli / storage-corpus / storage-review-fixes / split-brain-surface / storage-engine-proofs / corrupt-index 系;其中 storage-engine-proofs 首轮 1 失败为本地 kernel/dist 陈旧的环境坑,重建后 12/12)。
  • 无 kernel 改动,不涉及版本门;PR body 里提到的 ~50 文件后台批跑以 CI 为准即可。

一个纯观察(非阻塞):NFS/RWX 共享盘不支持多写的限制在 README 新段落末尾被明确保留了,这是这次改写里容易被顺手丢掉的一句话,值得留。

@github-actions

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: fix/2026-10-31_second-writer-no-block (3a6ab62)

Latest build — install this exact version

npm install -g billion-context@0.0.7337-pr.5294.5730e76

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Recent builds (commit → version)

commit version build
3a6ab62 billion-context@0.0.7337-pr.5294.5730e76 build

Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr5294.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei ranxianglei changed the title [PR] fix(storage): 活着的第二写者不再阻止启动——共享一个本地盘 index.db 是受支持拓扑(撤回 #5213 的拒绝策略) [PR] fix(storage): a live second writer no longer blocks startup — sharing a local-disk index.db is a supported topology (rev | fix(storage): 活着的第二写者不再阻止启动——共享一个本地盘 index.db 是受支持拓扑(撤回 #5213 的拒绝策略) Oct 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📦 Merged Build Published

This merge to master was published as 0.0.938-master.491ac1d (rolling master tag).

npm install -g billion-context@0.0.938-master.491ac1d  # this exact build
npm install -g billion-context@master  # always the newest merged build

npm view billion-context@master prints the current version at any time.

This was referenced Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant