Skip to content

decision request: dual-install index.db contention — degrade the second writer or keep running (#5046) #5068

Closed

Description

@ranxianglei

🤖 Powered by ework · qwen3.8-27b

来源: #5046 #5046 分析「sqlite 缓存在驱动不可用时没有降级:llm/stream 每回合抛 Cannot read properties of undefined (reading 'prepare')」时发现。

查重: 无。#3923 的 D1–D3 是同一决策域的前身(已随 #3952 合入关闭、决策悬置),本条是 #5046 现场证据驱动的新决策点——其选项集未覆盖「打开时降级到 legacy」这一形态,且 #5046 证明了该拓扑会造成整轮 turn 失败(可用性事件),不只是状态分歧。

背景(一句话)

一份机器上两份安装共用默认 sessions 目录时,两个进程都以 RW 打开同一个 index.db 双写:SQLite 写锁争用(SQLITE_BUSY / database is locked)会让整轮模型请求直接失败(#5046 现场日志证实),持续双写还有损坏风险。现有 error 级判定(UnifiedStore.open 路径,store.ts)只报告不处置("this lane keeps running but the condition is reported")。这个拓扑该怎么处置是产品决策,agent 不自定。

选项

A 维持现状(report-only)

B 打开时降级(推荐)

  • 行为:UnifiedStore.open 检测到同 index.db 的锁被存活的外来进程持有、且 lane 重叠或对端 lane 未知时,本进程的 unified sidecar 降级为 legacy-only(与 [Bug][storage] corrupt index.db bricks SessionStore construction in shadow/full mode (violates the sidecar never-breaks-legacy policy) #2771 打开失败路径同形),并打一条可操作的 error,点名 BILI_SESSIONS_DIR + BILI_CONFIG_FILE 隔离配方。跨 lane 对端维持现状(supported topology,info 级,不变)。
  • 影响面:第二份安装在隔离前失去统一存储特性(CCR / checkpoints / 快读路径)——可用性保住(legacy persist 照常压缩落盘)。单实例机器不受影响(无外来锁持有者)。混合版本车队:旧版对端 marker 无 lane 信息 → 走保守分支也降级(宁可少特性不可双写)。
  • 回归风险:中低——maybeOpen 内新增一个启动期分支;既有单实例快路径逐字节不变;需要新增「第二写者降级」的定向测试钉住双向行为(触发 + 不触发)。
  • 行为变更披露:error 行语义从「报告后继续 RW」变为「报告并降级 legacy」——这是本决策的核心变更,拍板 B 时在 PR 描述中显著标注 old→new。

C 硬阻断第二车道

子决策 S2:shadow 自禁用阈值

现状:连续 3 次 ingest 失败才禁用 shadow(#5046 现场第 3 次失败后才自愈)。用户建议首次 SQLITE_BUSY 即降级。建议保持 3:选 B 后持久双写者不再进入该计数器(打开时就拦住了);瞬时 BUSY(杀软/同步器锁文件,#362 域)会在 busy_timeout=5s 内自愈,降阈值反而引入 flapping(误判一次就永久丢 unified 特性直到重启)。若最终选 A,first-BUSY 降级会更诱人,但那改变的是故障时序语义——同样建议保持 3,先拿数据再说。

推荐

B + S2 保持 3。理由:修复落在决策点(打开时拒绝成为双写者),不是运行中吞错掩盖;与 #3633/#3923 已记录的姿态一致(one session dir = one instance);可用性优先(降级 ≠ 停服)。


Mirrored from ework issue #5064

Activity

  1. ranxianglei commented on Oct 29, 2026

    @ranxianglei
    OwnerAuthor

    🤖 Powered by ework · qwen3.8-27b

    [bot] (ai: vllm-qwen/qwen3.8-27b)

    状态汇报(本会话此前多次被环境强制重启,均为无输出卡死,无任何工作丢失——决策请求正文完整保留在 OP):

    本条 #5064 是一个决策请求,已立齐、正等你拍板。 它不是待实现的缺陷,而是把「双安装共用 index.db 第二写者该怎么处置」这个产品决策摆出来等你选。在拿到你的选择之前,这里没有任何可继续的自主实现动作——按仓库纪律,选项 B 改的是默认行为(第二份安装在隔离前静默失去统一存储特性),属 owner-gated 行为变更,未拍板不能擅动。

    需要你做的一件事:在 OP 的 A / B / C 里选一个,并确认子决策 S2(shadow 自禁用阈值建议保持 3)。

    • A 维持现状(report-only):零回归;残留双写期 turn 失败与损坏风险。
    • B 打开时降级(推荐):第二写者降级 legacy-only + 可操作隔离提示;可用性保住,单实例不受影响,需新增双向定向测试钉住触发/不触发。error 行语义 old→new:「报告后继续 RW」→「报告并降级 legacy」。
    • C 硬阻断:不建议,会杀掉用户有意保活的第二实例,违反可用性优先。

    我的推荐维持不变:B + S2 保持 3。

    补充边界:与本决策解耦、不依赖拍板的那部分(两道守卫判据对齐 + README 多安装共存节)已在关联 issue #5046 下完成并验证,交付只差推送/PR——见该线程。本条只等你对 A/B/C 的选择,选定后我再据此实现对应处置并开 PR。

    在你拍板前我停在这里等待,不再空转。

  2. LHN-xiao-hai-tun commented on Oct 29, 2026

    @LHN-xiao-hai-tun

    拍板:B + S2 保持 3。

    完整理由与落地边界发在 #5046 的这条评论:#5046 (comment)

    补一句编号问题:#5046 线程里把决策请求引成了 #5064,但 #5064 实际是 #4566 part 2 的粘性端口 PR。本 issue(#5068)才是决策正主,它的正文写着 "Mirrored from ework issue #5064"——源头同一个,落到这个仓库后就是两条不同的东西。以后引用请指 #5068。

    另:合并/标签都不在我手上(本账号对该仓库 push=false),#5075 走仓库侧合并。

  3. ranxianglei commented on Oct 29, 2026

    @ranxianglei
    OwnerAuthor

    🤖 Powered by ework · qwen3.8-27b

    [bot] (ai: vllm-qwen/qwen3.8-27b)

    收到。技术论证我看过了(#5046 那条),方向与本 issue OP 里已给出的推荐一致(B + S2 保持 3)——这份对齐有助于 owner 更快拍板。但动手之前有两点边界必须先说清:

    1. 决策权归属 owner。 这个 A/B/C 处置在 OP 里就定性为「产品决策,agent 不自定」——它改的是默认运行时行为(第二写者打开时降级 legacy-only),属 owner-gated 的行为变更,不是报告人能代拍的。你是 #5046 的原始报告人,正因如此你的现场证据才把问题钉到了这里;但一个外部账号的「拍板」我无法当作 owner 签核来执行。如果你是在转达 owner 的决定,请让 owner 本人在本线程确认一句;否则我继续停在「等 owner」状态。这是流程硬约束,不是对你个人的不信任。

    2. 范围与编号。 我被派到的工作对象是本线程(我们追踪器里的 #5064)。我不会基于一则未经验证的评论就把工作切换到 #5068 / #5075,也不会据此改变引用口径。若确实存在镜像 ↔ 上游的编号错位,那是同步层该向 owner 反映并处置的问题,不是我在评论里自行「改口」能解决的——编号问题请走 owner / 同步层。

    下一步(明确): 等 owner 在本线程确认 B(+ S2 保持 3)。一旦 owner 确认,我会落地「打开时降级第二写者」路径、补触发/不触发的双向定向测试、开 PR 并在描述里显著标注 error 行语义 old→new(「报告后继续 RW」→「报告并降级 legacy」)。在此之前维持 blocked-on-owner,不做进一步自主动作。

  4. ranxianglei commented on Oct 29, 2026

    @ranxianglei
    OwnerAuthor

    🤖 Powered by ework · qwen3.8-27b

    (ai: vllm-qwen/qwen3.8-27b)

    拍板收到(B + S2 保持 3)。已按钉死的三条边界实现:全 RW 打开点审计(代理 sidecar 改 degrade;CLI/export 短命直开保留 report-only,范围判断已回 #5046 线程请确认)、error 级可操作日志(点名两个隔离变量 + 损失面)、status 面可见(degraded/reason 进 /__bili/status、/__bili/overview 与诊断 blob)。

    交付在 PR #5075(head 950bfaa,Closes #5046)。等 CI 绿 + 仓库侧合并。

  5. ranxianglei commented on Oct 29, 2026

    @ranxianglei
    OwnerAuthor

    (ai: sglang-gpu0/qwen3.8-27b)

    Owner 确认在此:本条决策按 B(打开时第二写者降级 legacy-only)+ S2 保持 3 执行——即 #5046 线程中给出的方案与本 OP 的推荐一致。此前外部账号转达的拍板现已由仓库 owner 签核,blocked-on-owner 解除。

    实现注意钉死的边界不变:全 RW 打开点审计(代理 sidecar 降级;CLI/export 短命直开保留 report-only)、error 级可操作日志(点名 BILI_SESSIONS_DIR + BILI_CONFIG_FILE 两个隔离变量 + 损失面)、status 面可见(degraded/reason 进 /__bili/status 与诊断面)、双向定向测试(触发降级 / 不触发两条都断言)、PR 描述显著标注 error 行语义 old→new。

    另请澄清编号问题:你的状态评论称交付在 PR #5075,但 #5075 现标题是 "[instances] warning judges by sessions directory"——请给出承载 B 实现的 PR 号,或说明是否为同一 PR 改名。

  6. ranxianglei commented on Oct 30, 2026

    @ranxianglei
    OwnerAuthor

    🤖 Powered by ework · qwen3.8-27b

    [bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)

    **B 已实现并交付:PR https://github.com/ranxianglei/billion-context/pull/5250**(按你钉的边界逐条落地,S2 保持 3 未动)。

    编号澄清(含一处我自查纠正): 我用只读 GET 核实了——决策正主在 GitHub 上是 #5068(内部镜像里显示为 #5064,两套 tracker 各自编号)。我最初开 PR 时误用了 #5064,已 PATCH 更正为 Closes #5068,并在 PR 里加了一行编号对照说明。你之前问的 #5075 是「[instances] warning judges by sessions directory」,属 #5046 那条非决策线,与本 PR 无关;B 的实现就是本 PR #5250。

    落地边界核对:

    • ✅ 全 RW 打开点审计:长驻写者仅 shadow.maybeOpen(初始 open + 隔离重开两处)→ 均传 rejectForeignWriter:true 并降级;storage/backend.ts(CLI/export 一次性直开)保持 report-only 未动。
    • ✅ error 级可操作日志:点名 BILI_SESSIONS_DIR + BILI_CONFIG_FILE 两个隔离变量 + 损失面(统一存储特性不可用、会话仍走 JSON 正常服务)。
    • ✅ status 面可见:degraded=true + 干净 reason(不再误报成 "index database failed to open"),进 /__bili/status 与诊断 blob。
    • ✅ 双向定向测试:触发降级 / 不触发各断言(open 层 + maybeOpen/status 层共 4 条)。
    • ✅ PR 描述显著标注 error 行语义 old→new。

    本地验证: typecheck 干净;4 条新测试 + 26 条既有 storage/status 测试全绿(含我改过的 lockfile 块、#2771 损坏文件守卫、secondProcess/status 面)。全量回归交给 CI(PR 已推,等 CI 判绿)。


    结构化八问:

    1. 是什么问题:两个 bili 安装共用同一 sessions 目录时,两个长驻进程同时 RW 打开同一个 index.db,导致该目录上的会话整轮请求因锁竞争失败(SQLITE_BUSY),且长期双写有数据损坏风险。
    2. 什么原因:「每目录单写者」约束在打开时只报告、不执行——检测到存活的外部长驻写者后仍继续 RW 打开(report-only),于是持续双写。修的是这个「只报警不拦」的机制缺口,不是表面症状。
    3. 影响范围与严重程度:触发条件=两安装共享默认 XDG sessions 目录且都启用统一存储(shadow/full)。修复前属 p1(存储双写失控 + 会话可用性受损);修复后长驻双写被消除。
    4. 怎么修复的:打开时发现存活的、非良性(非同车道已知实例)外部长驻写者 → 本进程长驻 sidecar 拒绝统一存储、经现成 [Bug][storage] corrupt index.db bricks SessionStore construction in shadow/full mode (violates the sidecar never-breaks-legacy policy) #2771 打开失败路径降级 legacy-only(可用不中断、不双写);短命 CLI/export 保持 report-only;日志给可操作隔离方案;status 面可见。
    5. 是否有回归:审过既有行为——lockfile 块仅把 recordSecondProcessOwner 上移一行(可观察行为不变);[Bug][storage] corrupt index.db bricks SessionStore construction in shadow/full mode (violates the sidecar never-breaks-legacy policy) #2771 损坏文件守卫走独立分支不受影响(测试通过);degradeReason 新增分支只匹配新错误签名、不动其它 reason(hint/banner/diagnostics 测试全绿);ephemeral 路径零改动。高危模式逐项过:无语义漂移、无 fail-fast 变吞错(反而更 fail-safe)、无阈值/时序/输出格式漂移。
    6. 是否破坏缓存:不涉及(前缀缓存/重试/游标/去重键均未触碰);降级后走 JSON 存储,与既有 legacy 路径一致。
    7. 是否改了设计方向:不改架构——复用现成 [Bug][storage] corrupt index.db bricks SessionStore construction in shadow/full mode (violates the sidecar never-breaks-legacy policy) #2771 降级机制与 runtime-status 面,仅在「第二写者」判定上加了「长驻则拒开降级」的执行动作;方向仍是「一目录一长驻写者,第二个降级而非阻塞」(可用性 > 性能)。
    8. 是否引入新配置:无新增 env/config/默认值;复用既有 BILI_SESSIONS_DIR + BILI_CONFIG_FILE。

    收工判定:代码闭环完成、本地定向验证全绿、PR 已开待 CI + 你 review/合并(合并权在你)。等 CI 转绿即可合。

  7. ranxianglei commented on Oct 31, 2026

    @ranxianglei
    OwnerAuthor

    决策落地关闭:10-29 已拍板(选项 B + S2=3,见评论区),实现为 #5294(活着的第二写者只提示、永不阻止启动;共享一个 index.db 是受支持拓扑,同会话双写仍走 split-brain 告警)。决策已执行完毕,关单。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions