shaw
ae9a01d852
fix(compact): 二轮审计加固——心跳字节不得污染 failover 判定与并发写安全
对首轮修复的对抗式审计发现并修复以下问题:
1. failover 判定污染(真实回归风险):handler 以 "Forward 前后
c.Writer.Size() 是否变化" 判定响应是否已写出并据此放弃换号。心跳注释
字节会使该判定恒真,compact 请求一旦在上游等待期间发过心跳,上游
429/5xx 将不再 failover。新增
OpenAICompactKeepaliveAdjustedWrittenSize(扣除心跳字节、互斥锁下
一致读取、仅心跳字节归一化为未写哨兵 -1),快照、failover 比较与
openAIForwardErrorAlreadyCommunicated 三处判定统一改用该口径;无心跳
请求完全等价于原 c.Writer.Size()。
2. 并发写竞争:心跳 goroutine 与未被显式拦截的写回路径(Forward 内部
本地拒绝等)存在 ResponseWriter 数据竞争。StartOpenAICompactSSE-
Keepalive 现将 c.Writer 替换为 openAICompactKeepaliveWriter:写侧
方法(Header/Write/WriteString/WriteHeader/WriteHeaderNow/Flush)
先在互斥锁下停拍,读侧(Status/Size/Written)仅加锁不停拍——任何
请求侧响应构造与心跳从构造上互斥,热路径状态读取不误杀心跳。
3. 语义拦截补齐:rejectIfCyberSessionBlocked(在用户槽位长等待之后
执行的直接 c.JSON)与 writeOpenAIFastPolicyBlockedResponse 在心跳
提交后降级为 response.failed 终止事件;未提交时先停拍再写 JSON,
状态码语义不变。失败事件 errType 参数化(permission_error 等)。
4. reconstruct 混合形态:done 事件存在但 compaction 只在
output_item.added 中时也要补入;done 已含 compaction 时跳过 added,
避免无 id 可去重时收集两份(Codex 要求恰好一个)。
5. 观测性:logOpenAIRemoteCompactOutcome 对心跳提交后的失败(wire 200)
以 GetOpsStreamError 纠正 outcome,不再误记 succeeded。
新增 5 个测试:failover 口径不变式、包装器停拍语义(-race)、fast
policy 提交前后两态、混合 done/added 形态(含去重)。
Refs #3887 #3777
2026-07-10 10:18:16 +08:00
..
2026-07-09 22:30:04 +08:00
2026-07-09 15:03:38 +08:00
2026-05-26 10:49:20 +08:00
2026-06-02 00:46:50 +08:00
2026-04-22 13:19:28 +08:00
2026-05-19 15:27:47 +08:00
2026-05-19 15:27:47 +08:00
2026-05-19 15:27:47 +08:00
2026-06-11 18:55:27 +08:00
2026-06-11 18:55:27 +08:00
2026-06-11 18:55:27 +08:00
2026-05-20 13:25:45 +08:00
2026-06-30 01:11:51 +08:00
2026-06-30 01:11:51 +08:00
2026-04-22 12:30:00 +08:00
2026-06-19 21:17:45 +08:00
2026-06-11 18:55:27 +08:00
2026-05-21 15:19:29 +08:00
2026-05-21 15:19:29 +08:00
2026-06-11 18:55:27 +08:00
2026-05-26 10:49:20 +08:00
2026-05-26 10:49:20 +08:00
2026-06-11 18:55:27 +08:00
2026-07-01 17:46:12 +08:00
2026-07-01 17:46:12 +08:00
2026-07-07 18:53:56 +08:00
2026-04-21 00:21:29 +08:00
2026-05-28 10:03:41 +00:00
2026-06-16 11:41:54 +08:00
2026-05-07 09:14:47 +08:00
2026-07-07 10:20:23 +08:00
2026-07-07 10:20:23 +08:00
2026-03-08 14:12:17 +08:00
2026-03-08 14:12:17 +08:00
2026-05-26 10:49:20 +08:00
2026-07-09 11:15:52 +08:00
2026-06-09 22:46:06 +08:00
2026-06-11 20:46:47 +08:00
2026-07-09 11:15:52 +08:00
2026-03-15 16:21:49 +08:00
2026-07-03 21:02:31 +08:00
2026-07-09 16:35:29 +08:00
2026-02-07 22:14:07 +08:00
2026-07-03 21:02:31 +08:00
2026-07-06 16:45:15 +08:00
2026-07-02 15:58:29 +08:00
2026-07-05 08:35:34 +08:00
2026-02-07 17:12:15 +08:00
2026-04-01 02:20:13 +08:00
2026-06-26 15:38:06 +08:00
2026-07-01 11:43:35 +08:00
2026-07-06 11:43:16 +08:00
2026-07-04 05:30:50 +08:00
2026-02-23 12:45:37 +08:00
2026-02-23 12:45:37 +08:00
2026-05-26 10:49:20 +08:00
2026-05-05 03:26:54 +08:00
2026-02-12 19:01:09 +08:00
2026-06-26 15:38:06 +08:00
2026-06-26 16:06:01 +08:00
2026-07-09 11:15:52 +08:00
2026-07-07 19:49:40 +08:00
2026-07-07 13:53:39 +08:00
2026-07-09 21:40:51 +08:00
2026-04-25 05:26:33 +00:00
2026-07-09 11:15:52 +08:00
2026-06-12 01:47:01 +08:00
2026-03-15 22:13:42 +08:00
2026-07-09 14:26:10 +08:00
2026-07-10 10:18:16 +08:00
2026-05-28 22:44:25 +08:00
2026-05-05 03:26:54 +08:00
2026-06-26 10:36:09 +08:00
2026-07-01 12:21:45 +08:00
2026-06-28 11:13:09 +08:00
2026-06-03 14:18:12 +08:00
2026-06-03 14:18:12 +08:00
2026-06-05 14:00:57 +08:00
2026-06-12 01:47:01 +08:00
2026-07-08 02:51:51 +00:00
2026-07-08 02:51:51 +00:00
2026-05-12 15:21:31 +08:00
2026-06-10 14:16:51 +08:00
2026-06-30 10:00:48 +08:00
2026-07-06 14:34:17 +08:00
2026-05-11 11:17:26 +08:00
2026-05-11 11:17:26 +08:00
2026-07-09 11:15:52 +08:00
2026-07-07 13:53:39 +08:00
2026-07-07 13:53:39 +08:00
2026-04-21 20:36:10 +08:00
2026-07-02 16:11:07 +08:00
2026-05-25 18:16:46 +08:00
2026-07-07 10:20:23 +08:00
2026-05-20 13:25:45 +08:00
2026-06-05 14:00:57 +08:00
2026-07-08 13:50:49 +08:00
2026-04-09 18:14:28 +08:00
2026-07-08 13:50:49 +08:00
2026-05-28 22:44:25 +08:00
2026-05-30 17:52:18 +08:00
2026-05-26 10:49:20 +08:00
2026-03-03 01:05:11 +08:00
2026-05-26 10:49:20 +08:00
2026-07-04 05:30:50 +08:00