Skip to content

[Bug][compress] A rejected or no-op compress also triggers a one-time ref/ledger alignment: nothing was folded, yet the | [Bug][compress] 被拒绝或 no-op 的 compress 也会触发一次性的 ref/账本对齐:什么都没折叠,下一个请求却改写了所有标签 #6330

Open

Description

@BBleae

版本

master c26c31ace(2026-11-11)。已发布的 0.1.213 不受影响:它在冷加载时对齐,没有延后的标记。这个标记是 #5608-D 带进来的(提交 2071e9fe7,在 PR #5609 里合入)。

现在的行为

#5608-D 把一次性的 ref/账本对齐(#4316)从冷加载挪到了会话下一次成功的折叠。折叠本来就会打断 provider 前缀,所以对齐带来的字节变化搭这个请求一起走;用 src/persist.ts 冷加载路径上(afterLoad)那段注释的话说,"sessions that never fold pay nothing ever"。

applyRanges(src/stream.ts)为此置上标记 session.pendingLedgerAlign = true,位置紧跟在提交 kernel 返回的状态(ctx.session.state = res.state)之后。这在后面的 r.blocksCreated === 0 分支之前,所以两类什么都没折叠的调用也会置上它:

下一次存盘会消费这个标记(consumePendingLedgerAlign → maybeRestampToLedger → restampRefsToLedger)。被拒绝时,这次存盘就发生在同一次 applyRanges 调用里,经由 recordCompressFailure → markDirty → scheduleSave。no-op 时是会话的下一次存盘。

于是对齐跑在一次什么都没折叠的调用上,下一个请求发出去的字节就变了:

  • 引用已经都在各自账本位置上(ref = 行 seq + 1)的会话:这一遍保留所有引用(kept=N restamped=0 rehomed=0),但仍然把 timestampsHighWaterRefSeq 抬到最高的引用号,此前渲染过的每个 <acp> 标签都丢掉 time= 属性。
  • 引用偏离了账本位置的会话(比如有一条受保护的消息占了一个存储行、却不占引用号):这一遍还会给偏离的引用重新编号。这正是 # Cache hit collapses to 1–12% for a whole generation, then the very next request returns to 100% — repeated 15× in one day #5608-D 推迟掉的那次重编号,现在发生在一次拒绝上,而不是一次重启上。

两种情况下,出站字节都从第一条带标签的消息起就变了,provider 前缀缓存从那条消息往后都未命中,而这次调用的回执报的是失败或 no-op。对齐是一次性的,所以每个会话只发生一次:在第一次走到提交那一步的 compress 调用上,不管它结果如何。

在提交之前就返回的拒绝(参数解析失败、gate:no-visible-context、gate:coverage-regression、gate:no-net-shrink 等)不受影响。

这和 src/persist.ts 里 consumePendingLedgerAlign 处写下的约定("No fold ⇒ marker never set ⇒ pure legacy no-op")以及 #5797 的 D 项(不 fold 的会话永久保留旧号,零代价)相矛盾。

复现

宿主层面,不联网。一个 9 轮、开着时间戳的会话,经 SessionStore 存盘、行在统一存储里,然后调一次 applyRanges、存一次盘,再对同一段历史跑第二次 processTurn:

会话 compress 调用 对齐日志行 引用 字节变了的带标签消息 带 time= 的标签
引用都在账本位置上 被拒绝(gate:too-small) kept=9 restamped=0 rehomed=0 blocked=0 superseded=0 不变 9 / 9 9 → 0
引用都在账本位置上 no-op(范围在保护区里) 同上 不变 9 / 9 9 → 0
第三条消息受保护,它后面的六个引用都比账本位置低一位 被拒绝(gate:too-small) kept=2 restamped=6 rehomed=0 blocked=1 superseded=6 m00003–m00008 → m00004–m00009 8 / 8 8 → 0
同上 no-op 同上 同上 8 / 8 8 → 0

第一行里第一个标签前后的样子:

<acp tokens="752" type="text" time="…">m00001</acp>
<acp tokens="752" type="text">m00001</acp>

只在创建了块之后才置标记时,四行都是:没有对齐日志行、引用不变、0 条消息变化、每个标签都保留 time=。

前两行就是 PR 里的测试文件(tests/compress-refused-ledger-align.test.ts)固定下来的。后两行来自同一夹具的一个本地变体(一条消息受保护)。

端到端:在 master c26c31ace 上起一个代理(插件车道,带 x-bili-plugin: dsh,Anthropic wire,compress 经插件工具端点发出,一段合成对话,上游是本机回环上的假服务):

before compress      : tags with time=19 / all tags=19
compress result      : refused | [Compression FAILED: Total compressible content too small (408 chars across 1 range(s), min 5000). …
right after          : tags with time=0 / all tags=19

拒绝之后那个请求有 33 条出站消息,之前是 31 条(多了这次调用和它的回执),和上一个请求的第一处不同在出站消息 0。代理日志里有 [persist] ledger-aligned refs for <session>: kept=55 restamped=0 rehomed=0 blocked=0 superseded=0 (#4316 PR3/3)。拿已发布的 0.1.213 包做的对照:被拒绝的调用之后 17 个标签里 17 个仍带 time=,每个请求都是下一个请求的前缀。

影响

  • 谁:master 上第一次走到提交那一步的 compress 调用没有创建块的任何会话。applyRanges 是共用的提交路径:代理的 compress 循环和流改写、插件工具端点、外部摘要 compress、预检折叠都走它。关了消息时间戳(compress.messageTimestamps: false 或 BILI_MESSAGE_TIMESTAMPS=0)的会话,标签上本来就没有 time=:引用都在账本位置上时对齐照样会跑,但出站字节不变;引用偏离了的仍然会被重新编号。
  • 代价:每个会话一次,从第一条带标签的消息起前缀缓存未命中,而那时什么都没折叠。引用偏离了的会话,模型引用的编号也在那时被重排(旧号经 superseded 表拒绝,和任何一次对齐之后一样)。此前渲染过的消息上 time= 属性没了,之后也不会回来。
  • 没有丢东西,也没有折错。这是一次缓存代价和一处显示变化。

提议的修法

在 r.blocksCreated === 0 的那几处返回之后再置标记,和其他只在成功时做的记账(clearCompressFailures、clearCompressLoopStreak)放在一起。创建了块的 compress 照旧置上。只动宿主侧(src/stream.ts);PR 随后就发。

这个改动管不到的:搭着真折叠跑的对齐,在一个引用都没挪的时候仍然会抬高水位线。抬高发生在 restampRefsToLedger(kernel/src/refs.ts)里,所以我会另外提一个 kernel 授权申请。

相关:#5608(C、D 两项)、#5797、PR #5609、#6160(在真折叠时观察到的同一个对齐)。

Activity

  1. changed the title [-][Bug][compress] 被拒绝或 no-op 的 compress 也会触发一次性的 ref/账本对齐:什么都没折叠,下一个请求却改写了所有标签[/-] [+][Bug][compress] A rejected or no-op compress also triggers a one-time ref/ledger alignment: nothing was folded, yet the | [Bug][compress] 被拒绝或 no-op 的 compress 也会触发一次性的 ref/账本对齐:什么都没折叠,下一个请求却改写了所有标签[/+] on Nov 12, 2026
  2. ranxianglei commented on Nov 12, 2026

    @ranxianglei
    Owner

    该issue已按根因归类纳入 [kernel-compression] 根因汇总 #6265,由汇总单统一跟踪处理;本单不再单独跟进,故关闭。如认为归类有误或问题仍未解决,请直接回复,会重新打开。

  3. ranxianglei commented on Nov 12, 2026

    @ranxianglei
    Owner

    状态更正(自动化脚本误操作): 本单此前被「纳入根因聚合」流程误关闭, 现重新打开。机器人工作已停, 本单由根因聚合线程统一跟踪, 根因修复落地后请手动关闭或在本单回复「close」。
    [automated: re-open after bot-stop-only policy]

  4. ranxianglei commented on Nov 12, 2026

    @ranxianglei
    Owner

    重新打开:此前被归类聚合脚本误关。按 owner 口径,纳入根因聚合的 issue 只停机器人、不关闭,由根因聚合线程统一跟踪,根因修复落地后统一关闭。
    (reopened: absorbed issues must not be closed, only bot work stopped — owner policy 2026-11-12)

  5. ranxianglei commented on Nov 12, 2026

    @ranxianglei
    Owner

    (更正)此前按"清理"口径误关闭;按 owner 口径纠正:被根因聚合纳入的 issue 只停机器人、不关闭。本 issue 重新打开,机器人工作已停。

  6. ranxianglei commented on Nov 12, 2026

    @ranxianglei
    Owner

    🤖 Powered by ework · qwen3.8-27b

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

    查重结论:本单已按根因归类纳入 [kernel-compression] 根因汇总 #6265(owner 2026-11-12 口径),由汇总线程统一跟踪,本单不单独跟进。

    状态确认:

    • 本单当前为 OPEN。早先「纳入聚合并关闭」是自动化脚本误操作,owner 已多次更正并重新打开(见 3/6/8/9/10/11/15/17/18/19/20 楼)。
    • 按 owner 2026-11-12 口径:纳入根因聚合的 issue 只停机器人、不关闭;根因修复落地后由聚合线程统一关闭。
    • 机器人在本单上的工作已停。我不会重新关闭、不会单独跟进、也不会对本单重复开发。

    根因修复与关闭统一由汇总线程 #6265 处理,本单不再采取行动。

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions