# push-gateway 问题记录

> 负责人：ai02
> 关联：[coord.md](../coord.md)、[contracts/push-gateway_contract.md](../contracts/push-gateway_contract.md)、[push-gateway 02 架构设计](../../../services/push-gateway/docs/02-architecture-design.md)
> 规则：AI 遇到问题时在此追加条目，coord 仲裁后更新状态

---

## §0 已有仲裁核查（ai02 复审 02 文档对总裁裁决的回写情况）

> 本节为 ai02 在批次 0 等待期对 president-final-rulings 已裁决事项的回写核查。
> 裁决来源：[president-final-rulings.md](../../president-final-rulings.md) §1.5 / §3.3 / §4.2 / §4.3 / §4.4 / §7.2 / §3.4
> 核查日期：2026-07-10
> 核查结论：5 项裁决中 **0 项已完全回写**、**1 项部分回写**、**4 项未回写**

### 核查矩阵

| 裁决编号                        | 主题                       | 裁决要求（摘要）                                                                                                                                                                            | 02 文档现状                                                                                                                | 核查结论  | 状态         |
| ------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------- | ------------ |
| ISSUE-053                       | Kafka topic 命名           | 02 §5.1 topic 改为 `edu.notification.requested`，禁止抽象名 `edu.*.events`                                                                                                                  | §5.1 仍写 `edu.notification.events` / `NotificationRequested`                                                              | ❌ 未回写 | 待 ai02 修复 |
| ISSUE-055                       | /readyz 软失败             | push-gateway /readyz 对 Kafka 软失败（失败仅告警 + `degraded: true` + 返 200，不返 503）                                                                                                    | §6.7 仅 Redis PING 硬失败返 503，无 Kafka 软失败逻辑                                                                       | ❌ 未回写 | 待 ai02 修复 |
| ISSUE-056                       | 设计决策记录章节           | 02 §5.4 改名为"设计决策记录：gRPC vs HTTP 协议选型（coord 已采纳 P1）"，正文标注"coord 已采纳"                                                                                              | 02 无"设计决策记录"章节                                                                                                    | ❌ 未回写 | 待 ai02 修复 |
| ISSUE-058                       | Redis SET 启动重建         | 02 §3.1 补充"Hub 启动时遍历内存连接 SADD + EXPIRE 60s + 清空旧 instanceID 成员"；/readyz Redis 失败仅告警不阻塞；metrics 暴露 `push_gateway_redis_set_rebuild_total`；文档化 60s 不一致窗口 | §3.1/§8.4 仅描述运行期 SADD/SREM，无启动重建；§6.7 Redis 硬失败返 503（与"仅告警不阻塞"冲突）；无重建指标；无 60s 窗口说明 | ❌ 未回写 | 待 ai02 修复 |
| ARB /internal/push 契约（§4.2） | 第一版 /internal/push 契约 | coord "as-is" 采纳 ai02 02 §4.2 作为第一版契约，仅在 ai10 异议时调整                                                                                                                        | 02 §4.2 已定义 `{user_id, event, data, ttl?}` → `{success, delivered, online}`                                             | ✅ 已落地 | 无需动作     |

### 核查结论

- **ISSUE-053/055/056/058 共 4 项须 ai02 在批次 4 启动前回写到 02 文档**（president-final-rulings §3.4 明确"批次 4 启动前"完成回写）
- **ARB /internal/push 契约已落地**，无需动作；但 ai10 若提出异议（如 batch 接口/异步回调），coord 会公布差异点
- ISSUE-058 中"/readyz Redis 失败仅告警不阻塞"与 ISSUE-055"软失败规则"形成耦合：Redis 作为 push-gateway 必需依赖本应硬失败，但 ISSUE-058 裁决要求"仅告警不阻塞"以避免雪崩 —— **此耦合需 coord 明确优先级**（见下方 ISSUE-006-ai02）

---

## §1 问题列表（提请 coord 仲裁）

### ISSUE-001-ai02：SSE 端点是否提供（跨文档三方冲突）

- **提请方**：ai02
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：SSE 端点（`/sse`）在三个文档中存在冲突：
  - [push-gateway_contract.md](../contracts/push-gateway_contract.md) §1.2 列出 `GET /sse` 端点，认证 JWT
  - [matrix.md](../matrix.md) §5 HTTP 接口矩阵列出 `push-gateway (ai02) | SSE | /sse`
  - [02-architecture-design.md](../../../services/push-gateway/docs/02-architecture-design.md) §10 明确建议"不支持 SSE，WebSocket 已够用，避免协议膨胀"
  - 01-understanding.md 完全未提及 SSE
  - 实际代码无 `/sse` 实现，`gin-contrib/sse` 仅为 gin 间接依赖
- **建议方案**：采纳 02 文档建议 —— **push-gateway 不提供 SSE，仅 WebSocket**。理由：
  1. 单一协议降低维护成本与测试矩阵
  2. SSE 单向下行 + 文本协议，不适合未来 reconnect/ack 双向协议
  3. 各 portal 已规划 WebSocket 接入（[coord-cross-review.md](../../coord-cross-review.md)）
- **影响方**：ai13/ai14/ai15（前端需统一走 WebSocket，移除 SSE 兜底）、ai10（msg 不需调 /sse）、coord（更新 matrix.md §5 与 contract.md）
- **状态**：待 coord 仲裁

### ISSUE-002-ai02：内部 API 鉴权命名三方不一致

- **提请方**：ai02
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：内部 API 鉴权头与环境变量在四处不一致：
  - **代码**（[handler.go#L30](../../../services/push-gateway/internal/ws/handler.go#L30) + [config.go#L41](../../../services/push-gateway/internal/config/config.go#L41)）：`X-Internal-Key` 头 + `INTERNAL_API_KEY` 环境变量
  - **02 文档** §4.2/§6.1：`X-Internal-Token` 头 + `INTERNAL_API_TOKEN` 环境变量
  - **ai-allocation.md** §5：`X-Internal-Key`
  - **president-final-rulings.md** §7.2："X-Internal-Token 重命名"（暗示应改为 Token）
  - **contract.md** §1.2：`内网 mTLS`（第四种方案！）
- **建议方案**：统一为 `X-Internal-Token` + `INTERNAL_API_TOKEN`（对齐总裁裁决 §7.2）。理由：
  1. 总裁裁决已明确倾向 Token 命名
  2. "Token"语义比"Key"更准确（共享密钥而非公私钥对）
  3. mTLS 在 P5 阶段引入成本过高，且 K8s 内网已有 NetworkPolicy 隔离，共享密钥足够
- **影响方**：ai10（msg 调用方需用相同头名）、coord（更新 contract.md 与 matrix.md §5 移除 mTLS）
- **状态**：待 coord 仲裁

### ISSUE-003-ai02：单节点容量目标 50k vs 10w+ 冲突

- **提请方**：ai02
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：单节点最大连接数目标在两份文档冲突：
  - [modules/push-gateway/README.md](../../../docs/modules/push-gateway/README.md) §7："单节点最大连接数 50k，超出时拒绝新连接"
  - [02-architecture-design.md](../../../services/push-gateway/docs/02-architecture-design.md) §11："单实例最大连接数 10w+"
  - [01-understanding.md](../../../services/push-gateway/docs/01-understanding.md) §5 引用 pending-features："单节点支撑 10w+ 连接"
- **建议方案**：统一为 **10w+**（对齐 02 文档与 pending-features）。理由：
  1. 10w+ 是 P5 设计目标（pending-features 权威）
  2. Go goroutine-per-connection + 64KB send chan 单连接约 20-30KB，10w 连接约 2-3GB，单节点可承载
  3. 50k 目标过于保守，与横向扩展方案不匹配
- **影响方**：coord（更新 modules/README.md §7）、ai02（02 §11 已正确）
- **状态**：待 coord 仲裁

### ISSUE-004-ai02：contract.md 内部端点路径 /internal/send vs /internal/push 冲突

- **提请方**：ai02
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：内部单推端点路径在 contract.md 与代码/02 文档冲突：
  - [contract.md](../contracts/push-gateway_contract.md) §1.2 + §2.4：`POST /internal/send`
  - **代码**（[main.go#L58](../../../services/push-gateway/main.go#L58)）+ **02 文档** §4.2：`POST /internal/push`
  - 总裁裁决 §4.2 已"as-is 采纳 ai02 02 §4.2"，即应使用 `/internal/push`
- **建议方案**：contract.md 统一改为 `POST /internal/push`（对齐总裁裁决与代码）。此为 ai02 自主回写范畴，不需 coord 仲裁动作，仅在此登记以便 coord 复核。
- **状态**：ai02 自行修复（见 contracts 回写）

### ISSUE-005-ai02：审计表（6 字段）设计缺失

- **提请方**：ai02
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：[ai-allocation.md](../../ai-allocation.md) §5 将"审计表（6 字段）"列为 ai02 设计重点，但：
  - 01-understanding.md §2 明确"不持有业务状态""无 DB"
  - 02-architecture-design.md §3 明确"无数据库。所有状态在内存 + Redis"
  - 两份文档均无审计表设计
- **疑问**：审计表是否要求 push-gateway 引入 MySQL/PostgreSQL？这与"无 DB"定位冲突。可能的解读：
  1. push-gateway 引入轻量审计表（如 SQLite/Redis Stream 持久化推送记录）
  2. 审计表由 msg 服务维护（msg 已落库），push-gateway 仅通过 Kafka 事件回流
  3. ai-allocation 表述过度，审计需求由 msg 满足
- **建议方案**：方案 2（审计由 msg 维护，push-gateway 仅同步返结果）。理由：保持 push-gateway 无 DB 定位，避免引入持久化层增加运维复杂度。
- **影响方**：ai10（msg 需确认审计字段是否覆盖 push-gateway 推送结果）、coord（澄清 ai-allocation §5 表述）
- **状态**：待 coord 仲裁

### ISSUE-006-ai02：ISSUE-058 与 ISSUE-055 对 Redis /readyz 失败策略耦合冲突

- **提请方**：ai02
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：两份裁决对 push-gateway /readyz Redis 检查失败的策略存在表述冲突：
  - **ISSUE-055**（[president §3.3](../../president-final-rulings.md)）：将 Redis 列为"必需依赖"，失败返 503 触发 Pod 重启
  - **ISSUE-058**（[president §4.3](../../president-final-rulings.md)）："/readyz Redis 检查失败时仅告警不阻塞，与 ISSUE-055 协调，避免雪崩"
- **冲突点**：Redis 是 push-gateway 跨实例广播的必需依赖（必需 → 503），但实例重启不能恢复 Redis 故障，且重启会丢失本地连接表加剧雪崩（应仅告警）
- **建议方案**：明确为 **Redis 软失败**（仅告警 + `degraded: true` + 返 200），从 ISSUE-055 必需依赖列表中移除 push-gateway → Redis。理由：
  1. push-gateway 重启不解决 Redis 故障
  2. Redis 故障时单实例仍能服务本地连接（仅跨实例广播失效）
  3. 雪崩风险高于短暂不一致
- **影响方**：coord（澄清两裁决优先级）
- **状态**：待 coord 仲裁

### ISSUE-007-ai02：02 文档缺 ADR / 非功能性需求 / 失败模式章节（不符业界架构文档规范）

- **提请方**：ai02
- **日期**：2026-07-10
- **类型**：工作量超批
- **描述**：02-architecture-design.md 不符合业界架构文档规范（arc42 / C4 模型）：
  1. **无 ADR 章节**：[modules/push-gateway/README.md](../../../docs/modules/push-gateway/README.md) §8 提到"待 P5 交付时补充 ADR 记录"，但 02 文档未落地。关键决策（gorilla/websocket 选型、Redis Pub/Sub vs Stream、心跳间隔 30s/60s 选型、10w 容量依据）无 ADR
  2. **无非功能性需求章节**：无可用性 SLO（如 99.9%）、安全合规、容量 SLA
  3. **无失败模式/混沌工程章节**：实例崩溃、Redis 故障、网络分区、Kafka 消费积压场景下的降级策略缺失
  4. **§11 容量表无依据**：10w 连接的内存/CPU/网络带宽估算缺失
- **建议方案**：在批次 4（P5）补全 02 文档 §14-§17 四个章节（ADR / 非功能性需求 / 失败模式 / 容量估算）。预估工作量：1-1.5 天。
- **影响方**：ai02（自主补全）
- **状态**：待 coord 确认是否纳入 P5 Must Have

---

## §2 已自主修复的文档偏差（ai02 直接修复，不需 coord 仲裁）

> 以下为 01-understanding.md 与现码不符的偏差，ai02 在批次 0 自主修复

| #   | 位置       | 偏差                                                                           | 修复方向                                           |
| --- | ---------- | ------------------------------------------------------------------------------ | -------------------------------------------------- |
| 1   | 01 §3.2    | `/readyz` 标"无（待实现）"，实际已实现（仅未检查 Redis）                       | 改为"已实现，仅返连接数，待补 Redis PING"          |
| 2   | 01 §3.2    | `/metrics` 标"无（待实现）"，实际已挂载 promhttp                               | 改为"已实现，待补自定义指标"                       |
| 3   | 01 §3.2    | `/internal/push` `/internal/broadcast` 标"待补鉴权"，实际已实现 X-Internal-Key | 改为"已实现 X-Internal-Key 校验（DevMode 跳过）"   |
| 4   | 01 §6 + §7 | Dockerfile 标"❌ 单阶段"，实际为多阶段（缺非 root/healthcheck/ldflags）        | 改为"⚠️ 多阶段但缺非 root + healthcheck + ldflags" |
| 5   | 01 §7.1 #6 | 引用 `main.go L44-46` 行号过期，鉴权状态错误                                   | 更新行号并改为"已实现"                             |
| 6   | 01 全文    | 未提及 SSE 端点（contract.md/matrix.md 列出但 02 建议不支持）                  | 待 ISSUE-001 仲裁后补充结论                        |
| 7   | 01 全文    | 未提及审计表（ai-allocation §5 设计重点）                                      | 待 ISSUE-005 仲裁后补充                            |

---

## §3 历史问题

（暂无）

---

## §4 仲裁结论（coord，2026-07-10）

> 详见 [coord.md](../coord.md) §17 ARB-015

| ISSUE                | 仲裁结论                                                        | 状态                  |
| -------------------- | --------------------------------------------------------------- | --------------------- |
| 001（SSE 端点）      | ✅ 不支持 SSE，仅 WebSocket；coord 移除 contract/matrix 的 /sse | 已裁决（§17 ARB-015） |
| 002（鉴权命名）      | ✅ 统一 `X-Internal-Token` + `INTERNAL_API_TOKEN`（总裁 §7.2）  | 已裁决（§17 ARB-015） |
| 003（容量目标）      | ✅ 统一 10w+（对齐 02 文档 + pending-features）                 | 已裁决（§17 ARB-015） |
| 004（端点路径）      | ✅ 统一 /internal/push（ai02 已自行修复）                       | 已裁决（§17 ARB-015） |
| 005（审计表）        | ✅ 方案 2：审计由 msg 维护，push-gateway 无 DB                  | 已裁决（§17 ARB-015） |
| 006（Redis /readyz） | ✅ Redis 软失败（仅告警不阻塞，ISSUE-058 覆盖 ISSUE-055）       | 已裁决（§17 ARB-015） |
| 007（02 缺 ADR/NFR） | ✅ P5 批次 4 补全 02 §14-§17 四章节                             | 已裁决（§17 ARB-015） |
