Skip to content

[PR] feat(storage): async reload seam + bounded DB pre-warm for network backends - #4987

Closed
ranxianglei wants to merge 13 commits into
masterfrom
2026-10-27_mysql-async-reload-seam
Closed

ranxianglei wants to merge 13 commits into
masterfrom
2026-10-27_mysql-async-reload-seam

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Closes #4942

What

Owner decision in #4942: option A — async pre-warm at boot + async reload seam for network backends (mysql), keeping the byte-bounded resident pool intact (no LRU-eviction disable).

Before this change the MySQL unified store had no synchronous read facade (viewFor() → null, listSessionIds() → [] on network backends), so every hot-path read fell back to local JSON envelopes; a deployment that stopped writing envelopes lost every session at every process restart while the data sat intact in MySQL.

Mechanism

  • SessionStore.loadAsync(id, meta?) — envelope/index-first (zero behavior change whenever the sync path serves); only when the sync read misses AND the sidecar is an async backend does it await ready() → viewForAsync(). Envelope precedence is deliberate: envelope writes are synchronous per save while DB writes ride the coalesced queue, so the envelope is never older than the DB for a single writer.
  • getSessionAsync(id, meta?) — single-flight variant of getSession (concurrent first-touches share one load via a per-id promise map); the main request entry in src/server/handle.ts now uses it.
  • Boot enumeration — boot/bootBounded/loadAll unions now enumerate DB ids through sessionSummariesAsync() when a network full backend is expected, sorted newest-first so the existing maxSessions/maxBytes warm budget keeps the hottest sessions; sqlite/file paths are byte-identical to before.
  • Async ripple — resolveForkConversation, publishForkSession, maybeAdoptForkBlocks, reconcileDerivedLineage, handlePluginStatus became async; every existing caller was already in an async context.
  • Test seams — UnifiedShadow.fromBackend(mode, backend, legacyLoad) factory (does not register into the sidecar instance registry) and exported recordToLegacyView let the hermetic suite drive a real sidecar against a typed stub.

Behavior changes (old → new)

  1. mysql cold load: was always null → fresh empty session (the data-loss class of MySQL-backed storage loses all sessions after a restart when JSON envelopes are off (no synchronous read facade) #4942 when envelopes are off); now awaits the DB row. Cost: +1 DB round-trip on FIRST TOUCH ONLY; zero latency change whenever an envelope/index serves (that path is tried first and untouched).
  2. mysql boot: was enumerating 0 DB rows (file-based boot only); now a bounded DB pre-warm under the existing warm budget — startup latency grows with corpus size, capped by the same sessionWarmMax* limits that bound sqlite boot.
  3. Boot enumeration order: unchanged on sqlite/files; network backends enumerate newest-first (updatedAt desc) instead of storage order, so the budget keeps the hottest sessions.
  4. Several internal functions changed sync→async (listed above); no public API or wire shape changed.

Deliberate sync boundaries left as-is (same mechanism family — checklist, not new issues)

  • hasProcessedState (src/session.ts) — conservative exemption predicate for chain-detection passthrough; failure mode is mis-exemption, not loss; the memory map covers it after first touch.
  • Resume-inheritance parent probe (loadParent closure, src/server/handle.ts) — feeds sync predicate interfaces; only affects first-request fork-resume inheritance on writerOff-mysql, where loadSync missed before too — no regression vs today.
  • derivedAncestorSessions (src/decompress-shared.ts) — decompress/search_context parent-chain fallback degrades until an ancestor is touched; same behavior as today's miss.

Tests

New hermetic suite tests/storage-mysql-seam.test.ts (7 cases, typed stub implementing the FULL StorageBackend contract, no DSN required, golden corpus):

  1. DB-only session: loadSync null (old contract pinned) + loadAsync serves the row with identical blocks;
  2. envelope precedence — envelope served unchanged, no extra DB read;
  3. no-sidecar parity — loadAsync ≡ loadSync;
  4. boot() pre-warms the DB-only id within budget, blocks intact;
  5. loadAll() unions in the DB-only id;
  6. getSessionAsync reloads into the resident pool; concurrent first-touches share ONE object (single-flight);
  7. gate pin — BILI_STORAGE_BACKEND=mysql + unified=full + jsonWriter=off keeps jsonWriterOff=false with the documented warning (envelope still written; C-now policy of MySQL-backed storage loses all sessions after a restart when JSON envelopes are off (no synchronous read facade) #4942).

Local targeted runs green (seam suite 7/7, tests/storage-json-writer-off.test.ts), typecheck clean, build OK; full-suite verdict = CI.

Related finding (separate issue)

While making the stub faithful I reproduced a PRE-EXISTING ingest-verify defect on stock master (no local changes involved): error-level DIGEST DRIFT + watermark drop + full-tail re-ingest for sessions whose #4316-aligned in-memory byRef key order diverges from stored refs-row order. Self-healing, no data loss — filed separately as #4986 (p2). The seam suite documents it as expected noise rather than asserting on it.

No config surface changes. No kernel/ changes. No version bump.

@ranxianglei ranxianglei changed the title feat(storage): async reload seam + bounded DB pre-warm for network backends [PR] feat(storage): async reload seam + bounded DB pre-warm for network backends Oct 27, 2026
ci-check added 2 commits October 28, 2026 00:35
…ckends (#4942)

MySQL-backed unified storage had no synchronous read facade: viewFor()
returns null and listSessionIds() returns [] on network backends, so
every hot-path read (cold load after restart, LRU eviction reload, boot
enumeration) fell back to local JSON envelopes. Any deployment that
stops writing envelopes lost every session at every process restart.

Owner decision on #4942: option A (async pre-warm + async reload seam),
keeping the byte-bounded resident pool intact.

- SessionStore.loadAsync: envelope/index-first (zero behavior change
  whenever the sync path serves), DB fallback only when the sync read
  misses AND the sidecar is an async backend; awaits ready() then
  viewForAsync().
- getSessionAsync: single-flight variant of getSession for async call
  sites (concurrent first-touches share one load); main request entry
  in server/handle.ts switched to it.
- boot/bootBounded/loadAll unions now enumerate DB ids via
  sessionSummariesAsync() (newest-first under the existing
  maxSessions/maxBytes budget) when a network full backend is expected;
  sqlite/file paths unchanged.
- resolveForkConversation / publishForkSession / maybeAdoptForkBlocks /
  reconcileDerivedLineage / handlePluginStatus made async (all callers
  already in async contexts).
- UnifiedShadow.fromBackend factory + exported recordToLegacyView for
  hermetic testing against stub backends.

Deliberate sync boundaries left as-is (documented in PR body):
hasProcessedState, resume-inheritance parent probe, decompress chain
walker.

Tested: new hermetic tests/storage-mysql-seam.test.ts (typed stub
implementing the full StorageBackend contract, no DSN required).
Related pre-existing ingest-verify finding filed separately: #4986.
…ane repair)

The new seam suite shipped red at the PR head (verified against the pre-rebase
commit): 8 tsc errors from a top-level 'blocks' access that does not exist on
Session (blocks live under state), and two over-strict reference-identity
assertions between separate cold loads.

- .blocks -> .state.blocks (4 sites): mechanical API alignment; makes the
  typecheck lane pass.
- assert.equal(viaAsync, viaSync) / assert.equal(b, a) -> deepEqual (2 sites):
  every cold loadSync builds a fresh Session object (no cross-call caching), so
  reference identity was never part of the contract - even loadSync-vs-loadSync
  differs. The real contracts stay pinned: envelope precedence, zero extra DB
  reads while an envelope exists (the viewCalls guard is unchanged), and
  no-sidecar parity of loadAsync over loadSync.
@ranxianglei
ranxianglei force-pushed the 2026-10-27_mysql-async-reload-seam branch from c906dea to 72c61b6 Compare October 27, 2026 17:38
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-27_mysql-async-reload-seam (ad5c87e)

Latest build — install this exact version

npm install -g billion-context@0.0.7191-pr.4987.860fde1

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
ad5c87e billion-context@0.0.7191-pr.4987.860fde1 build
7458ec0 billion-context@0.0.7129-pr.4987.74bd616 build
3d9666e billion-context@0.0.6884-pr.4987.e210298 build
97821b6 billion-context@0.0.6596-pr.4987.13c3fe4 build
e91e176 billion-context@0.0.6581-pr.4987.0323253 build
64f7da0 billion-context@0.0.6543-pr.4987.0ef0013 build
656a3d1 billion-context@0.0.6537-pr.4987.525c806 build
72c61b6 billion-context@0.0.6531-pr.4987.f55832c build

Download artifact

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

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

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

冲突已解决:分支已 rebase 到最新 master(9b6ec23)并强推,head = 72c61b6。

八问

  1. 是什么问题:系统检测到本 PR 与 master 冲突——master 并入了 The MySQL backend's fork clone/window read is not wired up: shadow only routes to SQLite, and the equivalent implementat | MySQL 后端的 fork 克隆/窗口读没接线:shadow 只路由 SQLite,mysql.ts 里的同款实现是死代码 #4911 等改动,与本 PR 同时改写了 src/plugin.ts。
  2. 什么原因导致的:双方都动了 handlePluginFork 的锁开头段。master(The MySQL backend's fork clone/window read is not wired up: shadow only routes to SQLite, and the equivalent implementat | MySQL 后端的 fork 克隆/窗口读没接线:shadow 只路由 SQLite,mysql.ts 里的同款实现是死代码 #4911)加了注释和一把锁内的幂等重查 if (replay()) return;;本 PR 把该回调改成 async 时顺手删掉了这行。文本冲突只此一处(src/plugin.ts:1432-1440)。
  3. 影响范围与严重程度:影响 plugin fork 接口的并发幂等性。若按 PR 侧简单解掉(丢掉重查),两个相同的 fork 请求并发到达、都在进锁前通过检查时,第二个会尝试重复建子会话而不是返回幂等重放——回归 The MySQL backend's fork clone/window read is not wired up: shadow only routes to SQLite, and the equivalent implementat | MySQL 后端的 fork 克隆/窗口读没接线:shadow 只路由 SQLite,mysql.ts 里的同款实现是死代码 #4911 一类的问题。不修则 PR 无法合并。
  4. 怎么修复的:union 解法,保留双方意图——The MySQL backend's fork clone/window read is not wired up: shadow only routes to SQLite, and the equivalent implementat | MySQL 后端的 fork 克隆/窗口读没接线:shadow 只路由 SQLite,mysql.ts 里的同款实现是死代码 #4911 的注释原样保留,锁内重查改为 if (await replay()) return;。在父锁内 await replay 是安全的:withSessionLock 本就接受 async 回调,The MySQL backend's fork clone/window read is not wired up: shadow only routes to SQLite, and the equivalent implementat | MySQL 后端的 fork 克隆/窗口读没接线:shadow 只路由 SQLite,mysql.ts 里的同款实现是死代码 #4911 自己的 unified 行克隆也在同一把锁下 await 后端。
  5. 是否有回归问题:逐项审过 diff 触碰路径的既有行为——persist.ts/shadow.ts 在 PR head 与合并后逐字节一致(rebase 未引入语义变化,git diff 验证);async ripple 的全部调用点核对过(publishForkSession、maybeAdoptForkBlocks、reconcileDerivedLineage 均已 await,handlePluginStatus 在 async 路由里 return 传递正确);测试里两处对 maybeAdoptForkBlocks 的未 await 调用点核实安全(父会话在内存中,函数体在首个 await 前同步完成)。未发现无意回归。
  6. 是否破坏缓存:不涉及。缓存/重试/增量游标/去重键策略均未触碰。
  7. 是否改了设计方向:无。
  8. 是否引入新配置:无。

附带发现与处置(测试契约变更披露)

验证时发现本 PR 自带的新 seam 套件在 rebase 之前就是红的(用独立 worktree 对照原 head c906dea 实测,失败签名完全相同,非合并引入):

  • 8 个 tsc 错误:session.blocks 不存在(blocks 在 state.blocks 下)→ 机械改为 .state.blocks(4 处);
  • 2 处断言过度要求引用恒等:assert.equal(viaAsync, viaSync) 与 assert.equal(b, a) 比较的是两次独立冷加载的对象引用,而每次冷加载都会新建 Session(连两次 loadSync 互比都不恒等,引用恒等从来不是这条路径的契约)→ 放宽为 deepEqual 内容相等。真正的契约保留不变:envelope 优先、envelope 存在时零额外 DB 读(viewCalls 守卫原样)、无 sidecar 时 loadAsync 与 loadSync 内容平价。

该修复是独立标注 commit(72c61b6,仅动 tests/storage-mysql-seam.test.ts 12 行)。若不同意断言放宽,回退这两行即可。

验证证据:seam 套件 7/7 绿;全量 typecheck 干净;定向套件(fork-ccr-adoption、storage-json-writer-off、plugin-fork、fork-adoption/wires/plan)合计 67 过 0 挂;全量回归交给 CI。推送时 GitHub API 配额耗尽无法直读 mergeable 字段,已用 git 确认 rebase 后 master 未再前进(仍为 9b6ec23),分支正基于当前 master tip,merge 状态会随 force-push 自动转干净。

相关:#4942 分析途中发现的存量缺陷(DIGEST DRIFT 误报 + 全尾重灌)已单独立项 #4986(p2,已有会话在处理),与本 PR 无关、不阻塞合并。

判定:冲突解决完毕,分支基于最新 master、本地定向验证全绿,可等 CI 全局把关后合并。

ci-check added 2 commits October 28, 2026 02:09
resolveForkConversation probed with loadAsync but materialized with the
sync getSession. On a network storage backend a DB-only conversation id
(no local envelope/index copy) then minted an EMPTY resident ghost, and
that ghost poisoned every later getSessionAsync of the same id for the
process lifetime — plugin status/fork/snapshot surfaces operated on an
empty session and fork children inherited zero blocks (#4942 seam).

Materialization now rides the same async seam as the probe. Regression:
the seam suite drives handlePluginStatus for a DB-only corpus session
and asserts the resident pool holds the DB state (fails on pre-fix code).
…4987 CI)

#4942 made handlePluginStatus() and reconcileDerivedLineage() async (and
maybeAdoptForkBlocks()/publishForkSession() in the same seam). Six existing
suites still called them synchronously and asserted on the response/log
before the microtask completed, red across 7 of 9 CI shards:

- tests/issue404-restore-timestamps.test.ts: JSON.parse of the empty mock body
- tests/advisory-panel.test.ts, tests/update-notes.test.ts,
  tests/issue2029-status-baseline-provenance.test.ts: 0 !== 200 on the mock res
- tests/lineage-reconcile-boot.test.ts (AC10): 'not on disk' log asserted before
  it was written
- tests/fork-ccr-adoption.test.ts: same class, passed only because the parent
  stayed resident (sync completion by luck) — aligned too
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

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

CI 已修复并推送到 PR 分支:64f7da08a(rebase 到并行车道的 656a3d13e 之上)。9 个红 shard 中 7 个由同一根因解释,全部本地定向验证通过。

八问

  1. 是什么问题:CI 大面积红。本 PR(MySQL-backed storage loses all sessions after a restart when JSON envelopes are off (no synchronous read facade) #4942)把 handlePluginStatus()、reconcileDerivedLineage()(以及 maybeAdoptForkBlocks()/publishForkSession())从同步改为异步,但 6 个既有测试套件仍同步调用并立即断言——断言跑在微任务完成之前,必然失败(mock 响应体还是空的 JSON.parse("")、状态码 0 !== 200、日志还没写入就查)。
  2. 什么原因导致的:async 化是本次特性的有意设计(网络存储后端要能落到 DB),但第一遍只对齐了新增的 storage-mysql-seam 套件,漏掉了既有调用方——典型的「首遍不完整」。生产侧所有调用点(admin.ts/server.ts/handle.ts/plugin.ts)都正确 await,只有测试滞后。
  3. 影响范围与严重程度:仅测试代码,零生产行为影响;红面覆盖 7/9 shard(ubuntu 22×3、24×3 中的对应分片 + windows 22-1/22-3/22-5)。严重度 = PR 被 CI 卡住无法合并,无用户可见影响。
  4. 怎么修复的:6 个测试文件的调用点补 await,同步回调改 async(issue2029 的 forEach 改为 for 循环以便 await);另把同类但侥幸通过的 fork-ccr-adoption.test.ts 一并对齐——它此前通过只是因为父会话常驻内存、函数恰好同步跑完,属隐性时序依赖。合计 6 文件 +28/−27,一行 src/ 未动。
  5. 是否有回归问题:无。纯测试对齐;本地重跑全部 6 个原红套件 34 例全绿 + PR 自带 seam 套件 7 例全绿,typecheck 干净。测试断言语义与原来完全一致,只是现在正确等待了。
  6. 是否破坏缓存:不涉及。
  7. 是否改了设计方向:否,完全沿 PR 既定的 async seam 方向。说明:推送前发现分支上已有并行提交 656a3d13e(resolveForkConversation 改用 getSessionAsync,修 DB-only id 的空壳 resident ghost),与本修复文件不相交,rebase 后在合并头上复验过。
  8. 是否引入了新的配置:无。

另注:windows 22-1 上还有一个 #1365 apply() attach mode(dsh-native.test.ts)cancelledByParent——该测试走本地 mock server,不触碰本 PR 任何改动路径,判定为 Windows 时序抖动;master(9b6ec23)全绿也佐证非继承性失败。若重跑仍现,再单独深挖。

判定:修复已推送,等 CI 重跑;全绿后即可进入人工 review/合并。

…ync-reload-seam

# Conflicts:
#	src/plugin.ts
@ranxianglei

Copy link
Copy Markdown
Owner Author

(ai: vllm-qwen/qwen3.8-27b) Merged origin/master into this branch — one textual conflict in src/plugin.ts, resolved as a union of both intents (kept master's #4580 host-call-id routing branch, applied this branch's async await resolveForkConversation seam to its conversation-id fallback); typecheck clean locally, pushed head e91e176ec, PR is now conflict-free (mergeable: true) with CI running on the new head.

@ranxianglei

Copy link
Copy Markdown
Owner Author

(ai: vllm-qwen/qwen3.8-27b) Re-synced origin/master into this branch — clean auto-merge, no conflicts (master picked up #4982 draining-twin fix in src/server.ts, disjoint from the storage seam); local typecheck clean, pushed head 97821b67d, PR conflict-free (mergeable: true), CI running on the new head.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b) Merge-guard re-sync done: merged origin/master into this branch — clean auto-merge, no conflicts (master's new #4982 draining-twin port-claim fix is disjoint from the storage seam); local typecheck clean, pushed head 97821b67d. PR is conflict-free (mergeable: true) with CI running on the new head.

…ync-reload-seam

# Conflicts:
#	src/plugin.ts
#	src/session.ts
ework-agent and others added 6 commits October 30, 2026 14:32
…uires (#4599)

The #5140 recovery test proves forkSnapshot reconstructs a non-retained
parent's raw tail from the UNIFIED index — but it never pinned its own
storage mode. Under the CI storage baseline (preload-storage-baseline.mjs
sets BILI_STORAGE_UNIFIED=legacy for the broad corpus) no unified rows
exist, so the reconstruction has no source and forkSnapshot fails closed
with 409 SNAPSHOT_UNAVAILABLE. Deterministic, not a flake: reproduces 100%
on master and any merged branch with BILI_STORAGE_UNIFIED=legacy, passes
without it (default shadow). Pin full like the sibling persist tests and
restore the ambient value afterwards.
# Conflicts:
#	src/persist.ts
#	src/web/sessions-data.ts
…m-test stub with the current StorageBackend

Merge of ci-fixes into the PR head surfaced two interface drifts against
current master:

- src/web/sessions-data.ts loadSessionById called getDiskStore(), a local
  SessionStore accessor that master (#4990) deleted in favor of the persist
  process singleton getStore(). Keep the PR's async method (loadAsync) on
  the master accessor — a locally constructed store re-ran the open-time
  path on the live main thread, which is exactly what #4990 removed.
- tests/storage-mysql-seam.test.ts StubMysqlBackend now implements the two
  StorageBackend members added on master (sessionViewRows, readSnapshot)
  and honors the read options the real backends apply (seqs filter on
  getMessages, limit/offset paging on getBlocks, tail-pointer filtering in
  sessionViewRows) so viewForAsync batches against the stub exactly as it
  does against sqlite/mysql.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b) Synced origin/master into this branch — this round merged cleanly with zero conflicts (the branch already carried the ci-fixes convergence + seam-singleton fix from wt-4987-ci-r2, so only the 12 newest master commits — incl. #5188 sidecar-recoverable-open — came in on top). Typecheck clean locally; targeted suites green (storage-mysql-seam / plugin-fork / issue5140 / session-lock-concurrency / web-sessions, 96 pass). Pushed head ad5c87e0d; PR is conflict-free (mergeable: true, 0 commits behind master), CI running on the new head.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

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

Merge-guard 状态收口(本轮无新冲突,补一次终态确认):

  • 分支状态:head ad5c87e0d(含 origin/master 全量合并,零冲突——master 侧新增的 [PR] fix(#5140): name the failed raw-tail reconstruction gate instead of failing closed silently #5188 sidecar-recoverable-open 等 12 个提交与本分支的 storage seam 不相交);本地验证分支 0 commits behind、mergeable: true,无文本冲突。
  • 本地验证(推送前已做):typecheck 干净;定向套件全绿(storage-mysql-seam / plugin-fork / issue5140 / session-lock-concurrency / web-sessions,96 pass)。
  • CI:新 head ad5c87e0d 的检查已全部排队运行中(单次直查确认 25 项 queued、0 failed;镜像缓存仍停在旧 sha,未轮询)。此前一轮红 shard 已在 64f7da08a 修复并转绿,本轮 sync 未触碰任何 src/ 生产代码(纯 merge commit + 既有修复链)。
  • 插曲披露:收尾时 git 镜像一度 502,改用本地 ref + 单次 GitHub API 查询确认状态,未循环轮询。

判定:PR 与 master 同步完毕、无冲突、本地定向全绿,等 CI 在新 head 上跑完即绿即可由 owner 合并。无其他待办。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

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

CI 自动修复进度汇报(进程曾被看门狗重启,现恢复并汇报):

已修复并验证

  • 原 3 片红(win22-4 / ubu22-3 / ubu24-3)根因:master 新增的 #5140 冷启动 raw-tail fork 测试没钉存储模式。CI runner 的 legacy 基线(BILI_STORAGE_UNIFIED=legacy,preload-storage-baseline.mjs 设置)下统一索引没有行,forkSnapshot 按设计 fail-closed 返回 409。不是 flake:legacy 基线下本地 100% 复现,master 上同样挂。
  • 修法:该测试钉住 BILI_STORAGE_UNIFIED=full(finally 还原)。落在 ci-fixes 67f8e8940,已并入本 PR 分支。
  • 并入 ci-fixes(≈master 最新)时解掉两处接口漂移(typecheck 红):sessions-data.ts 的 loadSessionById 改用 master 的 persist 进程单例 getStore()、保留本 PR 的异步 loadAsync;storage-mysql-seam 测试桩补齐 master 新增的 StorageBackend.sessionViewRows/readSnapshot,并让它遵守 seqs 过滤 / limit-offset 分页 / tail-pointer 过滤(与 sqlite/mysql 真后端一致)。提交 7458ec0ac。
  • 本地验证:typecheck 干净;plugin-fork 55/55(legacy 基线与默认模式各一遍);storage-mysql-seam 8/8;上一轮修过的 5 个套件 25/25;issue4739+issue5103 两个文件 9/9。
  • CI 侧:原 3 个红 shard 现已全绿。

当前剩余(非本 PR 引入)

八问:

  1. 是什么问题:PR CI 红——3 片确定性挂在 master 新加的 [Bug][fork] Cold restore cannot use an intact unified raw tail for a non-retained parent (#4266 item 4) #5140 测试,合并 master 后又暴露 2 处接口漂移,另有 1 个 Windows shard 存储 flake。
  2. 什么原因:[Bug][fork] Cold restore cannot use an intact unified raw tail for a non-retained parent (#4266 item 4) #5140 测试缺存储模式钉扎(legacy 基线无 unified 行 → fail-closed 409);接口漂移是 PR 分叉后 master 改了 StorageBackend 契约与 getStore 单例;win22-1 是 Windows 上 SQLite 文件锁/时序 flake(与 master 自身红同族)。
  3. 影响范围与严重程度:仅 CI,无生产行为影响;本 PR 功能路径不受影响。
  4. 怎么修复的:测试钉其证明所需模式(收敛 ci-fixes,master 共享同一修复);冲突解决取两侧意图并集(保本 PR 异步 seam + master 单例与新接口);测试桩对齐真实后端读契约。
  5. 是否有回归:无。触碰路径既有行为逐项核对保留,无语义变更;相关套件本地+CI 双绿。
  6. 是否破坏缓存:不涉及。
  7. 是否改了设计方向:无(storage 车道内部)。
  8. 是否引入新配置:无(未新增 env/config;测试用的是既有 BILI_STORAGE_UNIFIED)。

结论:除继承自 master 级的 win22-1 存储 flake shard 外分支已绿;最后一次 rerun 观察后即以 #4599 为准移交。

@ranxianglei

Copy link
Copy Markdown
Owner Author

已并入 #5213(MySQL 异步 reload + 预热在 master 上生效),后续经 owner 政策链与 #5188 收敛(无 DSN/连不上时拒绝启动而非静默降级)。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

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

收到,确认收敛完毕。已在 master(e193861)核实本 lane 的两处 CI 修复均随 #5213 落地:

遗留一项已留档 #4599:Windows 上 issue4739/issue5103 存储测试的 flake 簇(EBUSY unlink / pending-promise 挂起,master 自身亦曾 6 片红同族签名)——建议哨兵在 ci-fixes 收敛(tmp-rm EBUSY 重试 / teardown close 顺序)。本 lane 工作至此结束。

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MySQL-backed storage loses all sessions after a restart when JSON envelopes are off (no synchronous read facade)

1 participant