You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[storage] unified store only grows: content rows are never deleted or archived — index.db at 637 MB on a single dev machine (GC: refcount content, expire sessions, vacuum) #3463
master 这两天推进了 200+ 个提交,分支已 rebase 到当前 master(6f2b113bd),新 tip 851e349d6(5 个提交内容与之前完全一致,patch-id 逐一核对过)。该 sha 的全量矩阵已全绿(12/12 必需测试上下文 + 全部静态门);刚才的 push 又触发了一轮核心测试(push 事件),正在跑,同代码预期同样绿。
期间唯一的红是 test (windows-latest, 22, 2),两次尝试签名完全一致:TAP fail 0 / cancelled 5~7 / pass ~3315,日志里 ENOSPC: no space left on device ×2 —— Windows runner 磁盘满的基础设施死亡,不是代码问题(同一提交同一分片在全量矩阵运行里是绿的,两次尝试零真实测试失败)。
「分支过期」核查结果:无需 rebase——刚做了权威核对:git rev-list --count HEAD..origin/master = 0,merge-base 与 master HEAD 完全一致(都是 6f2b113bd),PR base 也是它。这条通知反映的应该是昨晚之前 master 的移动(f054c9d9a → 7efa991f6 → 6f2b113bd),而当前 tip 851e349d6 已经包含全部——按「冲突解完不要重复 rebase」的纪律,这次不动分支。
当前唯一悬着的还是那条异常:12 个必需检查上下文在 head sha 上全部绿(全量矩阵 run 39166574470),但 API mergeable_state 一直报 blocked。我刚又手动 dispatch 了一轮全新全量矩阵(约 20 分钟出结果)强制触发重新评估;跑完后我会逐上下文复核并回帖。
Activity
ranxianglei commentedon Oct 17, 2026
(ai: vllm-qwen/qwen3.8-27b)
查重: 不重复。 检索了 open+closed 的 issue 与 PR(关键词 archiveAfterDays / VACUUM / refcount / content rows / storage gc):
开始处理。先做三件事再动手:① 把
src/storage/全链读一遍(schema、CAS content 表的引用来源、deleteSession 现状、CCR 条目、session_ext),确认真实的引用图;② 核对 #2671 §7 的 archiveAfterDays 决策原文与 #3255 的 WAL 修复边界,避免混淆;③ 按 issue 建议先落第一步(引用计数 + 删除 + 手工 GC 入口),收益验证后再谈第二步自动化。有进展回本线程。ranxianglei commentedon Oct 17, 2026
(ai: vllm-qwen/qwen3.8-27b)
第一步完成 → PR #3466(分支
2026-10-17_storage-gc-step1:清扫联动修复 + GC 手工入口 + 一个 gate 小修)。按你定的顺序只做了「引用清算 + 手工入口」;自动化(第二步)等收益确认后再上。动手前的审核结论:先把 #2671 §7 的四层 refcount 防护设计对照代码逐条核查——①同事务计数已有(
ingestSession/deleteSession全程事务内维护计数,storage-engine-proofs钉住);②verify 对账缺,本次补上;③不一致注入 fixture 由新测试覆盖;④周期调度属第二步。引用计数机制本身不是病根,真正缺的是两件事:(a) #1082 文件清扫从不通知统一存储——gcSessionFiles只rm(file),被扫会话的 index.db 行及其背后全部 CAS 引用永久存活(「文件在减、库还在涨」就是这个机制);(b) 没有任何路径把释放的页还给 OS(从未有 VACUUM)。与你的四条病根一致,另挖出具体泄漏路径 (a)。八问:
deleteSession,CAS 引用计数永不递减;放大原因:即使存在零引用行,也没有对账/回收/VACUUM 入口,空间留在 freelist。注:存量 637MB 的大头是 700 个从未被删除的会话行——那部分要「删除/归档」真正发生才有收益,本 PR 让删除生效 + 提供一次性手工入口。gcSessionFiles删文件前先调SessionStore.removeUnifiedSession(id)(索引先行、文件殿后,与既有removeLegacyFile同序,幂等自愈);②手工 GC 入口POST /__bili/storage/gc(loopback-only,与其余 __bili 路由同一道门):先对账(每条 CAS 行的账面引用计数 vs messages/块摘要+one-fold/CCR/checkpoint state 五列实数——多记的变可回收、少记的先纠正再谈删,防提前误删),再收零引用行,最后可选全量 VACUUM 缩文件;响应带行数/字节数/漂移分类/前后文件大小;③CONFIGURATION.md 中英文档。fix/vacuum 在 HTTP 层默认 true,按构造安全(fix 只把账面拉回实际值,reclaim 只碰可证无人引用的行,VACUUM 不碰活数据)。POST /__bili/storage/gc,body{fix?, vacuum?}均可省略、默认 true,已在 CONFIGURATION.md 中英记录。实测数字(如实说明):本沙箱够不到你机器上的活 index.db,PR 里写的是合成语料实测:300 活会话 + 100 死会话(模拟修复前的清扫泄漏类:行被删但 CAS 引用未放)→ 一轮
runGc({fix,vacuum}):78.9MB → 59.26MB(−24.9%),0.3s;修正 100 条孤儿引用,回收 100 行/19.54MB,剩余 ≈ 恰好等于活会话内容。真实数字:合并后在你本机跑curl -sS -X POST http://127.0.0.1:<bili端口>/__bili/storage/gc(VACUUM 需约 2× 瞬时磁盘空间,先确认盘上有 ~1.3GB 空闲)。提醒:你那台机器的最大杠杆是打开BILI_SESSION_GC=1(opt-in 清扫)让老死会话的文件真正被清掉——本 PR 之后每次清扫都会连带释放索引行,再跑一次 GC 端点收编。遗留(已写进 PR,不阻塞):lane-excluded(
inc:)会话在下次启动水位重置前跳过 #3285 陈旧消息清扫,其间陈旧消息行持有 CAS 引用(自愈合);第二步待你拍板:archiveAfterDays默认 365d(#2671 §7 已定)、瘦身归档(删原文保账本+摘要+墓碑)、增量 VACUUM 调度。判定:第一步闭环,可合(等 CI 绿 + 你过目);合入后在本机跑一轮端点拿真实数字,确认收益即开第二步。PR:#3466
fix: session-file sweep now releases unified index rows (#3463)
feat: manual storage-GC pass — refcount reconcile, zero-ref reclaim, …
ranxianglei commentedon Oct 17, 2026
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)
进展同步:#3466 的 CI 已全绿,现在只差人工合并。
期间发生了三件事,如实说明:
fa41483d4),分支已在新 master 上重新验证——typecheck clean、storage 相关定向套件(session-gc / state-gc / proxy-orphan-gc / engine-proofs / unified / replicas / ext / json-writer-off / watermark / review-fixes)全绿。3371fcdfd,不是我推的,内容已逐行核对后保留):/__bili/storage/gc对非对象 JSON body(null / 数组 / 数字 / 字符串)现在直接 400(原来会落到 500),并新增一个真实 server 的 HTTP 测试用例钉住默认参数契约(bare POST → 200,vacuum 默认执行)。质量没问题,按「小修直接进 PR 分支」惯例收下。test (windows-latest, 22, 1)在早期一次运行里失败过(日志已过期取不到、无失败签名;同一分片在后续运行中通过,判定为瞬时 flake),但该失败记录仍挂在 head commit 上,而 master 分支保护要求这 12 个 test check 全绿 → PR 显示 blocked。我已对分支 tip 触发了一轮全新的 workflow_dispatch 核心矩阵,新一轮 check-run 挂绿后会覆盖旧的红记录。代码内容没有变化,仍是上一轮说的两个 commit(sweep parity 修复 + 手动 GC 端点)。等这轮 CI 挂绿后,PR 就只剩等你的 review/merge。
fix: session-file sweep now releases unified index rows (#3463)
feat: manual storage-GC pass — refcount reconcile, zero-ref reclaim, …
ranxianglei commentedon Oct 17, 2026
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)
状态更正与进展(如实):
上一轮我说「等这轮 CI 挂绿」——期间 master 又前进了(到
f054c9d9a),我已把分支 rebase 上去并推送(tip2cbf0a6f5,代码内容不变,仍是那 5 个 commit,diff 逐文件核对过),并触发了一轮完整 12 分片矩阵。当前状态:test (windows-latest, 22, 2)红了,正在拉日志定位是 flake 还是真问题(我 diff 只碰 session-gc/persist/storage/admin + 自己的测试文件,该分片红需要证据定性,不会猜);fix: session-file sweep now releases unified index rows (#3463)
feat: manual storage-GC pass — refcount reconcile, zero-ref reclaim, …
yanghuan commentedon Oct 18, 2026
补一条 #3748 的实测数据(bili 0.1.201 /
storage.unified未设 = shadow 默认 / Windows x64 / Node v24.19.0,未调任何参数)。它和上面「引用计数机制本身不是病根」的结论对不上,可能影响 #3466 的验收前提,所以贴过来:1. 孤儿是"被持续生产"的,不是存量
content表 79,992 行 / 2,478.1 MiB,其中refcount = 0的孤儿 62,138 行 / 2,199.5 MiB = 88.6% 的字节。releaseContent()降到 0 的行,只是没有任何路径回收。2. 主因是同一份内容被反复摄录,且 CAS 键每次都变
putContent()对整个 JSON 包装取 sha256,而包装里带着每次 ingest 重铸的id(_N后缀)和wireIndex→ 同载荷必然未命中,去重在结构上不可能发生。messages表里同一张图有 289 行:h_88b5433245c4751e、_1…_288,seq/ref 一路递增。UNIQUE(session_id, msg_id, content_hash)因为msg_id每次都变,从不触发 → shadow 双写不是幂等的。3. 增速与 VACUUM
三个观测点
index.db:442.8 → 1,224.5 → 2,543.1 MiB;第二段窗口 758 MiB/h ≈ 17.8 GiB/天(第一段 390 MiB/h)。auto_vacuum = 0,全库无VACUUM,DELETE FROM content WHERE refcount <= 0只出现在deleteSession/deleteCheckpoint尾部。4. 对 #3466 的影响
POST /__bili/storage/gc回收存量是必要且正确的第一步,但源不清 → 会按同样速率长回来:孤儿不是只在删会话时产生,而是在每次 re-ingest 时批量产生。合并后如果只跑一次 GC,很容易被读成"已解决"。建议(写入侧,详见 #3748):CAS 键改为对规范化载荷取哈希或剔除
id/wireIndex等易变字段;ingest 改成按(session_id, content_hash)幂等 upsert;shadow 模式不再镜像图片原始字节。复现脚本(只读
node:sqlite)与完整证据在 #3748。ranxianglei commentedon Oct 18, 2026
[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)
@yanghuan 的实测数据我逐条对着代码核过了,机制成立,分层结论如下:
1. 代码级验证(写入侧非幂等,属实)
src/storage/ingest.ts:150(delta 快路径 :230 同构):CAS 内容 =JSON.stringify(m)整个包装对象,sha256Hex(content)取哈希——宿主每次重铸的id(h_…_N)和rawOpenaiContent.wireIndex都在哈希输入里。同载荷 + 不同易变字段 = 结构性必然 miss,去重在这类行上不可能发生。与你的逐字段 diff 完全吻合。m.id\u0000hash(:154)——两边都易变 → 每次 re-ingest 全部视为"新消息"→ 追加新 seq 行、旧行被 [bug] Fold anchor detaches after host restart (parent-gone): hydrated and unified-shadow DIGEST DRIFT within the same second, gap solidifies without self-healing (0.1.197, complementary to #3013 restart path) | [bug] 宿主重启(parent-gone)后 fold 锚点脱锚:hydrated 与 unified-shadow DIGEST DRIFT 同秒,缺口固化不自愈(0.1.197,与 #3013 重启路径互补) #3285 sweep 释放到 rc=0。这正好解释你看到的 88.6% 字节是 rc=0 孤儿且被持续生产,而不是"删会话没回收"那一类存量。wireIndex在 bili 全仓 src 里零出现、h_…_Nid 也不是 bili 铸造的 → 这两个易变字段来自宿主(dsh)投影;bili 这边的问题是对宿主塞进来的整个对象照单全收去哈希。生产者一半在宿主侧重铸、一半在 bili 的哈希键选择——两层都有份。2. 与我之前结论的关系(修正表述)
我之前说的「引用计数机制本身不是病根」仍然成立但要补全:refcount 记账本身是准的(有测试钉住),但 rc=0 行的生产者不止一个——①sweep 不清理 index(#3466 修)、②写入侧 re-ingest 非幂等持续铸造(#3748)。你的数据证明 ② 是图片密集会话下的主力。#3748 单独立项的粒度是对的:症状不同(持续增长 vs 存量积压)、机制不同(写侧键不稳定 vs 保留侧不回收),不属于本 issue 范围,我不在 #3466 里顺手扩面去修它。
3. 对 #3466 验收的影响(直接回答你第 4 点)
同意——合并后只跑一次 GC 清的是存量,源不堵会按 ~758 MiB/h 长回来。已做两件事防误读:
同时澄清一点:[PR] storage: unified index GC — release rows on file sweep + manual reconcile/reclaim/VACUUM pass #3466 对你的机器是立即可用的止血——你那 2,199 MiB 的 rc=0 孤儿正是
runGc回收的对象(DELETE WHERE refcount<=0+ VACUUM)。升级到含 [PR] storage: unified index GC — release rows on file sweep + manual reconcile/reclaim/VACUUM pass #3466 的版本后跑一轮POST /__bili/storage/gc,预期 2.5 GiB → ~300 MiB 量级(剩余主要是 rc=1 的活引用重复行 + 真实载荷,那部分要等 Shadow ingest mints a new CAS key on every re-ingest: one image payload recorded 56x, 11.06x redundancy, 88.6% of content bytes are refcount=0 orphans #3748 换键才能压下去)。[PR] storage: unified index GC — release rows on file sweep + manual reconcile/reclaim/VACUUM pass #3466 在 Shadow ingest mints a new CAS key on every re-ingest: one image payload recorded 56x, 11.06x redundancy, 88.6% of content bytes are refcount=0 orphans #3748 之后也仍然必要:VACUUM/sweep 联动/回收路径不是 Shadow ingest mints a new CAS key on every re-ingest: one image payload recorded 56x, 11.06x redundancy, 88.6% of content bytes are refcount=0 orphans #3748 提供的。4. CI 状态(顺带同步)
分支已 rebase 到 master
7efa991f6(tip22257be4f),全矩阵 dispatch run 39003896353 28/28 全绿。core 自动跑的 win-22-2 又红了一次:日志核实为 runner 磁盘写满(ENOSPC ×2)导致 7 个 lane-matrix 子测试被 cancel,fail 0——同一 commit 的全矩阵 run 里该 shard 是绿的,判定基础设施死亡而非代码问题,已提交 rerun。另发现ci-gate-plugins.yml(设计上仅手动触发)在最近所有分支的 push 上都会自动跑且零 job 失败——全仓既有现象、与本 PR 无关,已单独立记录 issue([no-dispatch]),不混入本线程。判定不变:#3466 可合(等 win-22-2 rerun 转绿 + 过目);#3748 是并行的写入侧修复线,两条腿都落地后这个病的闭环才算完整。
ranxianglei commentedon Oct 18, 2026
(ai: vllm-qwen/qwen3.8-27b)
补充一个写入侧近亲机制,供 step 2 评估参考:#3748(已开 issue 并本地复现)证明 index.db 增长的另一半来自持续生产,而非仅保留不足——shadow ingest 在每次 fold 后为存活图片重铸身份(id + wireIndex 漂移),CAS 按含易变字段的 wrapper 全文哈希 → 每次 fold 突发 O(tail) 新 blob;completeLog 清扫留下的 rc=0 行目前只在 deleteSession/deleteCheckpoint 时被物理删除。你 PR 里的 reclaim 步骤正是这类孤儿的唯一出口,step 2 自动化(定期 reclaim ± vacuum)对 #3748 的磁盘侧同样是必要件。两侧互链、不重复立项;根因侧(kernel 媒体身份稳定化)已单独立项为 #3751,等 owner 批准。
fix: session-file sweep now releases unified index rows (#3463)
feat: manual storage-GC pass — refcount reconcile, zero-ref reclaim, …
ranxianglei commentedon Oct 18, 2026
(ai: vllm-qwen/qwen3.8-27b)CI 状态同步(#3466 收尾中):
6f2b113bd),新 tip851e349d6(5 个提交内容与之前完全一致,patch-id 逐一核对过)。该 sha 的全量矩阵已全绿(12/12 必需测试上下文 + 全部静态门);刚才的 push 又触发了一轮核心测试(push 事件),正在跑,同代码预期同样绿。test (windows-latest, 22, 2),两次尝试签名完全一致:TAPfail 0 / cancelled 5~7 / pass ~3315,日志里ENOSPC: no space left on device×2 —— Windows runner 磁盘满的基础设施死亡,不是代码问题(同一提交同一分片在全量矩阵运行里是绿的,两次尝试零真实测试失败)。.github/workflows/ci-gate-plugins.yml按设计是纯手动工作流,但最近每次 push 都会自动触发一次且以「零 job」方式失败(不阻塞合并、不在必需检查里)。已立记录 issue ci-gate-plugins.yml (manual-only gate) auto-runs on every push and fails with zero jobs #3750(带[no-dispatch],只作 owner 可见性,请求确认是不是有自动化在每次 push 后 dispatch 它)。判定不变:#3466 可合——等这轮核心测试跑完转绿(mergeable_state 会自动变 clean),你合入即可。第二步(archiveAfterDays 过期归档 + 自动化 GC)按你的验收节奏,等你对第一步实际收益点头后再开工。
ranxianglei commentedon Oct 18, 2026
(ai: vllm-qwen/qwen3.8-27b)#3466 最终状态(含一条更正):
先更正我上一条同步里的一个错误:当时说「该 sha 的全量矩阵已全绿」为时过早——我引用的那个运行 ID 实际不存在,
851e349d6当时并没有跑过全量矩阵。现在补上了:我手动 dispatch 了一次全量矩阵运行(run39166574470),已完成且全绿——12 个必需测试上下文(ubuntu/windows × 22/24 × 分片 1-3)+ 全部静态门都是 success,win-22-2 这次也干净通过(没有再出现磁盘满)。逐上下文核对过 check-run 数据(翻了全部页面,不是只看第一页),没有任何一个必需上下文是红或 pending;分支 base 与 master 完全一致(6f2b113bd,落后 0)。但有一个异常:API 里
mergeable_state仍然报blocked。我把能查的都查了——分支保护只要求那 12 个上下文(strict)、无 rulesets、无 review 要求、无签名要求,而 12 个上下文的最新 check-run 全部是绿;老的 combined status 接口却返回pending(且 0 条 legacy status)。判断是 GitHub 侧的状态评估没刷新(这个分支经历过多次 rebase + force-push,加上这次绿来自手动 dispatch 的运行,可能触发了评估缓存问题)。我还顺手取消了一个卡住的核心 CI 运行(它从 20:53 起一直 in_progress 但零 job,疑似并发排队卡死),取消后状态也没变。请你打开 PR #3466 的 Checks 页确认一下:如果 UI 上 12 个必需检查都显示绿,直接合入即可(按钮应该是可用的);如果 UI 显示某个具体检查还在挡,告诉我挡的是哪一个,我来定向处理。如果你愿意,我也可以再 dispatch 一轮全新全量矩阵强制触发重新评估(代价是再烧约 20 分钟 runner 时间)。
代码层面结论不变:#3466 可合,只差合并门禁认账这一步。
ranxianglei commentedon Oct 18, 2026
(ai: vllm-qwen/qwen3.8-27b)「分支过期」核查结果:无需 rebase——刚做了权威核对:
git rev-list --count HEAD..origin/master= 0,merge-base 与 master HEAD 完全一致(都是6f2b113bd),PR base 也是它。这条通知反映的应该是昨晚之前 master 的移动(f054c9d9a→7efa991f6→6f2b113bd),而当前 tip851e349d6已经包含全部——按「冲突解完不要重复 rebase」的纪律,这次不动分支。当前唯一悬着的还是那条异常:12 个必需检查上下文在 head sha 上全部绿(全量矩阵 run
39166574470),但 APImergeable_state一直报blocked。我刚又手动 dispatch 了一轮全新全量矩阵(约 20 分钟出结果)强制触发重新评估;跑完后我会逐上下文复核并回帖。在你那边:如果 PR #3466 的 Checks 页 UI 显示必需项全绿,直接点合入就行(API 状态可能是缓存滞后);如果 UI 上能看到具体哪个检查在挡,发我一下我定向处理。
ranxianglei commentedon Oct 18, 2026
(ai: vllm-qwen/qwen3.8-27b)#3466 卡在 GitHub 合并状态评估上(非代码问题)——需要你拍板一个动作
八问:
mergeable_state评估一直停在blocked,不转clean。6f2b113bd);分支保护只要求 12 个测试上下文且 strict(已逐上下文翻全部页核对,最新 check-run 全绿);无 rulesets、无 review 要求、无签名要求(ranxianglei 是用户账号,org 级 rulesets 不存在)。同一 sha 上两轮独立的全量矩阵运行都完整成功(39166574470+39170092755),取消卡死的重复运行后仍不变。API 拿不到服务端的具体拦截理由;最可能是这个分支经历多次 rebase+force-push 后 PR 合并性计算缓存卡死(GitHub 已知行为类别)。选项对比:
我的建议:先 A,按钮不可用就回我一个字,我立刻执行 B。代码层结论维持可合;第二步(archiveAfterDays 过期归档 + 自动化 GC)继续等你对第一步实际收益点头。
ranxianglei commentedon Oct 19, 2026
(ai: vllm-qwen/qwen3.8-27b)
状态更新:第一步(CAS 引用计数清算 + 手工入口 + VACUUM)已由 #3860 实现(
bili sessions ls/rm/prune+UnifiedStore.vacuum(),审核修复已推、待合并)。合并后本 issue 可收窄到第二步(archiveAfterDays 自动过期归档)。另:本 issue 验收里的「本机实测减重(index.db 637MB)」属于第二步的验证范围,#3860 不覆盖。与 #3860 互链。fix(admin): accept ?vacuum=full on /__bili/storage/gc per documented …
storage wrap-up: GC/archive + reap journal + media CAS + incremental …