Repository navigation
[PR] fix: fork adoption no longer gives up permanently on 409 SNAPSHOT_UNAVAILABLE (#3011) - #3024
ranxianglei wants to merge 11 commits into
Conversation
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.8424-pr.3024.0b86228This exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr3024.tgz
npm install -g packageInstall instructions are refreshed on each push. |
23c40fc to
80ee312
Compare
…s gate) Review fix for #3011/#3024: forkSourceMessages is only referenced inside src/plugin.ts (forkSnapshot + handlePluginFork); no test or module imports it, so the leading export tripped scripts/check-unused-exports.mjs (INTERNAL_ONLY). Drop the keyword per the gate's own remediation — no behavior change.
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) PR #3024 审查结论(fix #3011:fork adoption 对 409 SNAPSHOT_UNAVAILABLE 永久放弃) 按八问逐项:
有意行为变更披露(旧 → 新):
验证过程:rebase 到当前 master(554c9394d)无冲突;本地 typecheck clean;定向套件 tests/{plugin-fork,pi-omp-fork-adopt,dsh-fork-adopt}.test.ts 98/98 过。审查中发现 结论:方向正确、双端各修其根因、fail-closed 不变量完整、diff 干净(7 文件 +242/-37,每行都与目的相关)。分支已 rebase 并补上门禁修复,head |
…s gate) Review fix for #3011/#3024: forkSourceMessages is only referenced inside src/plugin.ts (forkSnapshot + handlePluginFork); no test or module imports it, so the leading export tripped scripts/check-unused-exports.mjs (INTERNAL_ONLY). Drop the keyword per the gate's own remediation — no behavior change.
94100f6 to
9d44414
Compare
…s gate) Review fix for #3011/#3024: forkSourceMessages is only referenced inside src/plugin.ts (forkSnapshot + handlePluginFork); no test or module imports it, so the leading export tripped scripts/check-unused-exports.mjs (INTERNAL_ONLY). Drop the keyword per the gate's own remediation — no behavior change.
9d44414 to
9e6cac4
Compare
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) 审查更新 — 分支已更新到最新 master,此前唯一的红灯已在上游解决 分支已 rebase 到最新 master: 关键变化:
CI 正在新 head( 八问速览(细节见上一条结构化结论):
判定:可合。等新 head 的 CI 结果作最终确认。 |
[bot] 🏷 (ai: vllm-qeen/qwen3.8-27b) CI 最终确认:新 head 过程中唯一一次瞬时红灯说明:Windows codex overlay e2e 道在本次运行失败一次—— 补充:分支推送后 master 又合入了一个无关修复(#3019,nudge-decide × self-heal 死锁),PR 现显示 behind 状态;与本 PR 零文件重叠,不影响合并。 最终判定:可合。分支 |
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b) 更正上一条首行的笔误:模型标识应为 |
…s gate) Review fix for #3011/#3024: forkSourceMessages is only referenced inside src/plugin.ts (forkSnapshot + handlePluginFork); no test or module imports it, so the leading export tripped scripts/check-unused-exports.mjs (INTERNAL_ONLY). Drop the keyword per the gate's own remediation — no behavior change.
9e6cac4 to
e4e912d
Compare
…VAILABLE (#3011) Client (src/agent/fork-adopt.ts): classify degraded outcomes by structured verdict instead of a reason-string regex. The regex only matched 'error' / 'http 5xx', so the incident's 'snapshot http 409 SNAPSHOT_UNAVAILABLE' was treated as terminal after ONE attempt and the child's inheritance was permanently abandoned in-process. Each result now carries an explicit retryable flag: snapshot GET retries on network errors, HTTP >=500, and 409 code SNAPSHOT_UNAVAILABLE (a residency gap that self-heals on the parent's next request); 404 PARENT_NOT_FOUND and other 4xx stay terminal. Fork POST classification unchanged (network/5xx retryable). Give-up now logs an explicit 'permanently disabled' line distinct from the transient 'will retry' line. Server (src/plugin.ts): new forkSourceMessages() selects the raw source view. The retained copy (pluginSnapshot) stays primary; when it is missing (the residency gap: LRU eviction or host restart losing an unpersisted copy) the recent raw view session.lastMessages is served instead — it is persisted unconditionally and re-stamped by every parent request. A head-divergent tail wins over a frozen pre-compaction retained copy (zero-overlap guard case), so the served identities survive native compaction rebases. Folded tails never serve. The retention-cap refusal (#2433) stays first. Fork materialization uses the same selection, so a zero-branchpoint child keeps its legitimate empty snapshot. Server (src/session.ts): resetSessionCompression clears the stale publicSnapshot* metadata so a rebased parent cannot fail the list-level stored-refs check against the old view.
…s gate) Review fix for #3011/#3024: forkSourceMessages is only referenced inside src/plugin.ts (forkSnapshot + handlePluginFork); no test or module imports it, so the leading export tripped scripts/check-unused-exports.mjs (INTERNAL_ONLY). Drop the keyword per the gate's own remediation — no behavior change.
…-view fallback The master-side test pinned 409 before the parent's next model request after restart. With #3011's forkSourceMessages fallback, a restore that carries a verifiable unfolded raw tail (unified-store restore) is served immediately (200); folded-only restores (legacy/shadow envelopes) still 409. Both pre-states are valid; the self-heal assertion below remains the pinned contract.
e4e912d to
77876e6
Compare
(ai: vllm-qwen/qwen3.8-27b) CI 修复完成: 这条红是从 master 继承来的,不是本 PR 引入的:
修复内容(commit 八问
剩余红的现状(均为继承项,不在本 PR 范围):
判定:本车道点名的 |
(ai: vllm-qwen/qwen3.8-27b) 判定(attempt 2/2):这 11 个 test 分片红是 master 继承性失败,不在本分支修复——停在此处,等 #4606 合入后 rebase。 证据
为何不在本分支顺手修(不是修不了,是不该修)
转绿路径:#4606 合并 → 本 PR rebase 到 master 重触发(rebase 时与本 PR commit 八问
结论:本 PR 自身改动面无 CI 问题;剩余红全部为 #4599 跟踪的 master 继承项,等 #4606 合并后 rebase 即可,无需再触发本车道。 |
…elp-merge sweep)
…elp-merge sweep)
…elp-merge sweep)
…elp-merge sweep)
|
ci失败 冲突 |
…flict fix) Conflict resolved by union of intent on the 409 SNAPSHOT_UNAVAILABLE path: - #3011 raw-view picker (retained copy -> recent raw tail, head-divergence rule) gains master's verified storage reconstruction as the last rung; - master's recon diagnostics (gate-named [reconstruction: ...] suffix, waitForReconstructionReplicas) kept verbatim; - #2433 retention cap stays a hard refusal even when a fresh view exists; - #4266 signed-thinking acceptance adopted (thinkingTokens rejection dropped); - fork handler serves child baseline from the SAME picked source view (identity-derived fromTail keeps CCR bookkeeping fail-closed); - test hunk takes master's strict 200 assertion (reconstruction makes the cold-restore scenario deterministically servable under unified-full).
(ai: vllm-qwen/qwen3.8-27b) 冲突已解决并验证通过——本轮没有 push(哨兵 #4599 已先行合入语义等价的解决,a3cf4a4be),我做了完整独立验证。 1. 是什么问题:PR 分支与 master 冲突(dirty),fork/snapshot 端点的同一组函数被两边重写。 2. 什么原因导致的:PR(#3011)给 3. 影响范围与严重程度:仅插件车道 fork/snapshot 端点(409 SNAPSHOT_UNAVAILABLE 路径)。冲突未解时 PR 无法合并,阻塞本修复上线;未发布,不影响现网用户。 4. 怎么修的:merge origin/master(d640d0d)进 PR 分支,union-of-intent 解决三处冲突:
5. 验证证据(本地定向,全量交给 CI):
6. 回归审查:逐项过既有行为——D-B cap 字节级错误消息 ✓、#5188 7. 是否改了设计方向:不涉及。 8. 是否引入新配置:不涉及。 判定:冲突侧收工,等 CI 矩阵跑完;若转红由本车道 attempt 2/3 继续接。 |
[ework] CI fix verified green ✓ |
[ework] CI fix verified green ✓ |
1 similar comment
[ework] CI fix verified green ✓ |
What & why
Closes #3011
A DSH desktop clone whose parent had just been natively compacted died on its first turn (
context_length_exceeded, 1063420 > 1048576) because bili permanently abandoned fork inheritance after a single 409. The client classified degraded outcomes by a reason-string regex (/^(snapshot|fork) (error|http 5\d\d)/insrc/agent/fork-adopt.ts); the incident's reasonsnapshot http 409 SNAPSHOT_UNAVAILABLE (raw snapshot unavailable; ...)matched neither arm, sotransient === false→ the child went into the permanent-done set after ONE attempt (themaxAttempts = 3retry budget was unreachable). The server-side 409 itself was a residency gap: the parent's raw snapshot was not persisted (persistence requires a prior fork receipt /publicSnapshotRetained) and had been lost to eviction/restart.One mechanism family (fork inheritance permanently abandoned), fixed at both ends:
Client — classify by field, not by string. Every degraded
ForkAdoptionResultnow carries an explicitretryableverdict: snapshot GET retries on network errors, HTTP ≥ 500, and 409 with parsed codeSNAPSHOT_UNAVAILABLE(a residency gap that self-heals on the parent's next request); 404PARENT_NOT_FOUNDand other 4xx stay terminal; fork POST classification is unchanged (network/≥500 retryable). Give-up now logs an explicitpermanently disabledline, distinct from the transientwill retry on a later request (attempt N/maxAttempts)line (issue suggestion 3).Server — give the 409 an available path. New
forkSourceMessages()insrc/plugin.tsselects the raw source view for bothGET /__bili/plugin/snapshotand fork materialization: the retained copy (pluginSnapshot) stays primary; when it is missing, the recent raw viewsession.lastMessagesis served instead — it is persisted unconditionally and re-stamped verbatim by every parent request, so it survives LRU eviction and host restarts. A head-divergent tail wins over a frozen pre-compaction retained copy (the zero-overlap guard case where the snapshot refuses to update after a full-cover native compaction), so served identities survive rebases. Folded tails never serve; the retention-cap refusal (#2433) stays first.resetSessionCompressionnow clears the stalepublicSnapshot*metadata so a rebased parent cannot fail the list-level stored-refs check against the old view.Together these rescue the incident end-to-end: at the child's first turn, adoption runs before the model request is forwarded; the parent now serves post-compaction identities from its recent raw view → prefix match succeeds → POST fork completes (child not yet registered) → the first forwarded request rides inherited folds instead of re-sending the full uncompacted history.
Behavior changes (old → new)
Deliberate, per issue suggestions 1–2:
SNAPSHOT_UNAVAILABLEwas terminal after 1 attempt (regex miss) → now retried up tomaxAttempts = 3within the process, then an explicit permanent-give-up log line. Side effect: 409s that are NOT residency gaps (multimodal history, genuinely inconsistent ref mapping) are also retried by the client — the wire code does not distinguish them. Cost is bounded (≤ 3 cheap GETs per child); worst case is one extra round of identical 409s before the same permanent stop.GET /__bili/plugin/snapshotmay now answer 200 from the recent raw view where it previously answered 409 (residency gaps, frozen-stale pre-compaction copies). Hard refusals unchanged: retention cap (feat(#2394): raise fork-snapshot retention default from 16 MiB to 100 MiB #2433), folded tail, missing refs/CCR originals. Fork materialization follows the same selection, so a zero-branchpoint fork child keeps serving its legitimate empty snapshot (childRevisioncomputation relies on this).fork adoption for <child> degraded (<reason>) — transient, will retry on a later request (attempt N/maxAttempts)...and...— permanently disabled, no further retries in this process...replace/augment the single old degraded line.No change to: wire protocol shape, message ids/refs, persistence format, kernel, nudge cadence, compression thresholds, or any default value.
Config surface
None added. No new config fields, env vars, or CLI flags — the fix reuses the existing
maxAttemptsinternal budget and existing metadata/persistence paths.Tests
tests/plugin-fork.test.ts: +4 — snapshot serves the recent raw view when no retained copy exists (and a full-prefix fork off it succeeds); stays 409 when neither source exists; folded tail never serves; fresh head preferred over a divergent frozen retained copy. All pre-existing pinned behaviors green (plain-panel 409, [Bug][plugin] protectedTools 的 BLOCKED 映射导致纯文本 snapshot 返回 409 #2620 mapping rejection, cap tests, concurrent-fork publish-once incl. zero-branchpoint child, cross-process fork).tests/pi-omp-fork-adopt.test.ts: +4 — structured verdict classification (pins the incident string verbatim, terminal vs retryable per code/status); adopter retries 409 until the parent recovers (exactly 1 fork POST, transient log lines, no permanent disable); closes permanently after the cap (exactly 3 attempts over 4 calls, silent afterwards).tests/dsh-fork-adopt.test.ts: existing expectations updated to the newretryablefield (multimodal/mapping 409s intentionallytrue— documented tradeoff above).Targeted local runs green: plugin-fork + pi-omp-fork-adopt + dsh-fork-adopt (98/98) and the wider fork/compaction/fork-adoption sweep (144/144); typecheck clean. Full suite left to CI.