## Why 深度审计确认。**本条在 `481e31e2b7`(summary.diffs 256 KiB 闸,见 #522 撤回说明)之后的当前代码上依然成立**,是修复后剩余浪费的主因。 1. `packages/core/src/event.ts:273` 的 `.insert(EventTable)` 是全仓唯一的事件写入点,**没有任何重复检测**(`rg "dedup|idempot|lastData|hash\(data\)" packages/core/src/event.ts` 返回空)。同 `aggregate_id`、同 `type`、payload 逐字节相同的事件被无条件重复追加 2. **修复后数据实测**(只统计 `time_created >= 2026-08-28 16:05:55` 的会话,即全部跑在带闸代码上): ``` message.updated.1 事件总数 28,059 条 / 208.9 MiB distinct (会话, payload sha256) 21,960 会话内重复 ≥2 次的 payload 721 组,涉及 6,820 条事件 逐字节重复浪费 134.3 MiB / 208.9 MiB = 64.3% ``` 即:**闸住 diff 摘要之后,仍有 64.3% 的消息更新事件是逐字节重复写入**,信息增量为零 3. 修复前的同类证据(sha256 比对,长度恰为 18,455,856 字节的 15 条事件哈希后只有 3 个 distinct payload,重复发生在同一会话内部): | 会话 | 同一 sha256 出现次数 | 逐字节重复体积 | |---|---|---| | `ses_034083992ffe` | **7 次**(`e7cdedfa…`,seq 276/293/310/325/345/357/367) | 123 MiB | | `ses_03408397affe` | 4 次(`f3efbc8b…`,seq 208/227/241/250) | 70 MiB | | `ses_034083993ffe` | 4 次(`e876d42e…`,seq 388/404/418/427) | 70 MiB | 注:跨会话之间 payload 并不相同,仅长度巧合相等 —— 缺陷是**会话内重复写入**,不是跨会话复制 4. 放大系数:`ses_fe5fecc1e45c` 有 8,117 条 `message.updated.1` 对应 2,082 条真实 message 行、8,330 个 part —— 平均每条消息被重发 **3.9 次全量快照**(流式更新每次都持久化完整状态) 5. 全量快照语义下中间态无重建价值:若每个 aggregate 只保留 `max(seq)` 一条,`message.updated.1` 从 6.371 GiB 降至 **0.141 GiB**,可回收 6.229 GiB / 283,266 行(97.8%) ## Scope - `packages/core/src/event.ts`(append / insert 路径 `:273`) - `packages/core/src/event/sql.ts`(`EventTable` 新增 payload hash 列 + migration) ## Approach - **insert 前增加幂等闸**:比对同 `aggregate_id` + `type` 的上一条事件 payload hash,相同则跳过写入 - hash 建议**落列**(如 `data_hash`)而非每次重算 —— 对 MiB 级 payload 反复 sha256 的代价不可接受;写入时算一次,比对走索引(现有 `event_aggregate_type_seq_idx` on `(aggregate_id, type, seq)` 可直接定位上一条) - 跳过的重复事件**是否仍推进 `event_sequence.seq`** 需按 sync 契约明确决定,两种语义都要在测试里固定下来 - 该防线对**所有全量快照型事件**生效,不与具体业务字段(如 `summary.diffs`)耦合,与 #522 的源头闸独立可叠加 ## Acceptance - 单测:对同一 aggregate 连续 publish 两次内容完全相同的事件,`event` 表只新增一行 - 单测:内容不同时正常追加,`seq` 严格递增且无重复 - 明确并测试 seq 语义:被跳过的重复事件是否占用 seq。以下三处读取路径必须在新语义下行为正确 —— `core/src/event.ts` 的 `gt(seq, after)` + `orderBy(asc(seq))` 增量读、`opencode/src/server/routes/instance/httpapi/handlers/sync.ts` 的客户端增量同步、`opencode/src/control-plane/workspace.ts` 的 `where(eq(aggregate_id, sessionID))` + `asc(seq)` 全量重放 - 回归:sync / workspace / projector 相关测试全绿 - 实测口径:新建会话跑一轮多文件编辑后,`message.updated.1` 的「distinct payload 数 / 事件总数」应接近 1(当前 post-fix 为 21,960 / 28,059 = 0.78,即 64.3% 字节浪费)
Why
深度审计确认。本条在
481e31e2b7(summary.diffs 256 KiB 闸,见 #522 撤回说明)之后的当前代码上依然成立,是修复后剩余浪费的主因。packages/core/src/event.ts:273的.insert(EventTable)是全仓唯一的事件写入点,没有任何重复检测(rg "dedup|idempot|lastData|hash\(data\)" packages/core/src/event.ts返回空)。同aggregate_id、同type、payload 逐字节相同的事件被无条件重复追加修复后数据实测(只统计
time_created >= 2026-08-28 16:05:55的会话,即全部跑在带闸代码上):即:闸住 diff 摘要之后,仍有 64.3% 的消息更新事件是逐字节重复写入,信息增量为零
修复前的同类证据(sha256 比对,长度恰为 18,455,856 字节的 15 条事件哈希后只有 3 个 distinct payload,重复发生在同一会话内部):
ses_034083992ffee7cdedfa…,seq 276/293/310/325/345/357/367)ses_03408397affef3efbc8b…,seq 208/227/241/250)ses_034083993ffee876d42e…,seq 388/404/418/427)注:跨会话之间 payload 并不相同,仅长度巧合相等 —— 缺陷是会话内重复写入,不是跨会话复制
放大系数:
ses_fe5fecc1e45c有 8,117 条message.updated.1对应 2,082 条真实 message 行、8,330 个 part —— 平均每条消息被重发 3.9 次全量快照(流式更新每次都持久化完整状态)全量快照语义下中间态无重建价值:若每个 aggregate 只保留
max(seq)一条,message.updated.1从 6.371 GiB 降至 0.141 GiB,可回收 6.229 GiB / 283,266 行(97.8%)Scope
packages/core/src/event.ts(append / insert 路径:273)packages/core/src/event/sql.ts(EventTable新增 payload hash 列 + migration)Approach
aggregate_id+type的上一条事件 payload hash,相同则跳过写入data_hash)而非每次重算 —— 对 MiB 级 payload 反复 sha256 的代价不可接受;写入时算一次,比对走索引(现有event_aggregate_type_seq_idxon(aggregate_id, type, seq)可直接定位上一条)event_sequence.seq需按 sync 契约明确决定,两种语义都要在测试里固定下来summary.diffs)耦合,与 fix: message snapshot persists untruncated session diff summary into the event log #522 的源头闸独立可叠加Acceptance
event表只新增一行seq严格递增且无重复core/src/event.ts的gt(seq, after)+orderBy(asc(seq))增量读、opencode/src/server/routes/instance/httpapi/handlers/sync.ts的客户端增量同步、opencode/src/control-plane/workspace.ts的where(eq(aggregate_id, sessionID))+asc(seq)全量重放message.updated.1的「distinct payload 数 / 事件总数」应接近 1(当前 post-fix 为 21,960 / 28,059 = 0.78,即 64.3% 字节浪费)