Files
supply-intelligence/tech/TECHLEAD_GATEWAY_CLOSURE_DESIGN_2026-05-08.md

632 lines
28 KiB
Markdown
Raw Normal View History

2026-05-12 18:49:52 +08:00
# TechLead 设计Gateway 收口 / 重试 / 灰度回滚 / 巡检门禁2026-05-08
状态:当前有效
阶段结论:可进入 QA 设计审查
仓库:`/home/long/project/supply-intelligence`
上游真源:
- `/home/long/project/supply-intelligence/tech/CURRENT_SOURCE_OF_TRUTH_2026-05.md`
- `/home/long/project/supply-intelligence/tech/BASELINE_TECHLEAD_V2.md`
- `/home/long/project/supply-intelligence/tech/GATEWAY_CONSUMER_DECISION_2026-05.md`
- `/home/long/project/supply-intelligence/tech/PRODUCTION_LAUNCH_CLOSURE_BOARD_2026-05-08.md`
- `/home/long/project/supply-intelligence/prd/PM_GATEWAY_CLOSURE_PRD_2026-05-08.md`
## 0. 当前结论
当前仓库已经具备以下真实落点,可作为本轮收口设计基础:
- package 发布 -> event 写入:`/home/long/project/supply-intelligence/internal/publish/service.go`
- gateway 拉取 / 自动消费 / ack
- `/home/long/project/supply-intelligence/internal/httpapi/server.go`
- `/home/long/project/supply-intelligence/internal/gatewayconsumer/service.go`
- `/home/long/project/supply-intelligence/internal/poller/gateway_package_poller.go`
- admission-state / routing-state / healthz / metrics 暴露:`/home/long/project/supply-intelligence/internal/httpapi/server.go`
- Postgres 持久化 package event 与 gateway snapshot
- `/home/long/project/supply-intelligence/internal/repository/postgres.go`
- `/home/long/project/supply-intelligence/migrations/0003_gateway_snapshots.sql`
- E2E 证明 publish -> consume -> ack -> admission-state`/home/long/project/supply-intelligence/internal/httpapi/postgres_e2e_test.go`
但按 PM 收口口径,当前仍缺三类工程化收口:
1. gateway 失败分类与自动重试边界尚未映射到现有 consumer/poller/repository 结构
2. rollout / rollback 仍缺脚本、命令入口、巡检文档的明确落点
3. 观测指标虽暴露 `/metrics`,但关键 gateway 语义尚未真正打点到调用链
因此本文件目标不是发散新架构,而是基于现有代码结构,把上线收口项转成文件级实现设计与任务拆解。
结论:
- 当前设计包已经足够进入 QA 设计审查
- 但 QA 审查应明确标记:进入的是“按本文件执行实现”的审查,不是“当前代码已可上线”
## 1. 设计边界
### 1.1 In Scope
- gateway package event 拉取与 ack 契约实现边界
- gateway 消费失败分类、自动重试、终态 failed、人工处置入口
- rollout / rollback runbook 的技术支撑:接口、脚本、命令、检查文档
- 观测指标、告警、巡检门禁落到具体文件
- QA 设计审查必须核查的真实调用链
- Engineer 文件级任务拆解
### 1.2 Out of Scope
- 不引入 MQ/Kafka/Redis/Temporal
- 不扩展到 NewAPI / Sub2API 的事件 ack 闭环
- 不重做独立控制台或外部告警平台
- 不改 package 发布主模型,不改 event + ack 基本模式
### 1.3 约束
- 必须贴合当前仓库已有代码与目录
- 优先复用已有:`internal/gatewayconsumer``internal/poller``internal/httpapi``internal/repository``internal/metrics`
- 不新增新基础设施,只允许新增当前仓库内脚本、文档、少量 repository 字段/方法、测试与打点
## 2. Gateway 契约实现边界
## 2.1 当前真实代码边界
当前已有契约实现如下:
1. 发布侧
- `POST /internal/supply-intelligence/publish/package-event`
- 实现:`/home/long/project/supply-intelligence/internal/httpapi/server.go :: handlePublishPackageEvent`
- 服务:`/home/long/project/supply-intelligence/internal/publish/service.go :: PublishDraft`
- 语义candidate `test_passed -> published`package `draft -> active`,生成 `PackageChangeEvent{gateway_sync_status=pending}`
2. 查询事件侧
- `GET /internal/supply-intelligence/gateway/package-changes?cursor=...`
- 实现:`/home/long/project/supply-intelligence/internal/httpapi/server.go :: handleListPackageChanges`
- repo`/home/long/project/supply-intelligence/internal/repository/interfaces.go :: ListPackageEventsAfter`
- Postgres`/home/long/project/supply-intelligence/internal/repository/postgres.go :: ListPackageEventsAfter`
3. ack 回写侧
- `POST /internal/supply-intelligence/gateway/package-changes/{event_id}/ack`
- 实现:`/home/long/project/supply-intelligence/internal/httpapi/server.go :: handleAckPackageChange`
- repo`/home/long/project/supply-intelligence/internal/repository/interfaces.go :: AckPackageEvent`
- Postgres`/home/long/project/supply-intelligence/internal/repository/postgres.go :: AckPackageEvent`
4. 本地默认消费方
- 消费服务:`/home/long/project/supply-intelligence/internal/gatewayconsumer/service.go :: ConsumeOnce`
- poller`/home/long/project/supply-intelligence/internal/poller/gateway_package_poller.go :: PollOnce`
- runtime`/home/long/project/supply-intelligence/internal/poller/runtime.go :: Start`
- 装配:`/home/long/project/supply-intelligence/internal/app/app.go`
### 2.2 契约边界结论
必须按以下边界实现,不得越界:
supply-intelligence 负责:
1. 产出 pending event
2. 提供 cursor 拉取接口
3. 接收 applied/failed ack
4. 对 event 的同步状态做持久化与查询暴露
5. 提供 admission-state 读口径,明确 `published != applied`
当前仓库内 gateway consumer 负责:
1. 拉取 pending event
2. 执行本地 apply
3. 对每次尝试产出显式结果
4. 在安全可重试范围内受控重试
5. 达到终态后回写 applied 或 failed
不负责:
- supply-intelligence 不同步调用下游管理 RPC 决定发布是否成功
- gateway consumer 不修改上游 candidate/package 状态
- ack 不承担重跑发布逻辑,只回写消费结果
### 2.3 状态语义约束
必须统一以下状态语义,并落到 API 返回、测试断言、runbook 文案:
- `candidate.status=published`:上游已发布,可被消费
- `package.status=active`:上游已允许下游消费
- `event.gateway_sync_status=pending`:尚未拿到最终消费确认
- `event.gateway_sync_status=applied`:消费方已成功应用
- `event.gateway_sync_status=failed`:消费方已确认失败,停止自动重试
当前 admission-state 已通过 `last_event.gateway_sync_status` 暴露该语义,代码位于:
- `/home/long/project/supply-intelligence/internal/httpapi/server.go :: handleModelAdmissionState`
### 2.4 当前设计缺口
当前代码与 PM 口径相比的缺口:
1. `AckPackageEvent` 只有 applied/failed 最终写回,没有“重试中”结构
2. `gatewayconsumer.Service` 当前一轮消费内直接 ack applied/failed没有失败分类
3. `poller.Runtime` 只做固定间隔拉取,没有按 event 维度退避与重试上限
4. event 表当前只有 ack 结果,没有重试次数、最后失败时间、失败分类
5. `ListPackageEventsAfter` 当前会返回已 failed 事件,但 consumer 因仅消费 pending 会跳过,导致缺少“失败后如何再次进入自动重试”的结构
## 3. 失败重试策略映射到现有代码结构
## 3.1 PM 口径到代码模型映射
PM 定义:
- 可自动重试:瞬时网络错误、临时 5xx、超时、gateway 短暂不可用且幂等安全
- 不可自动重试:参数/契约错误、幂等冲突、鉴权错误、明确业务拒绝
- 上限:每个 event 最多 3 次自动重试,退避 1m / 5m / 15m
- 第 3 次失败后转最终 `failed`
映射到当前代码结构后的实现原则:
1. `gateway_sync_status` 仍只保留 `pending|applied|failed`,不新增更复杂外部语义
2. 自动重试中的 event 仍保持 `pending`
3. 重试元数据落到 repository 持久化字段,而不是把 `failed` 当成“还要自动重试”
4. 只有最终不可自动重试,或达到 3 次上限,才 ack 为 `failed`
5. 任一成功尝试直接 ack 为 `applied`
### 3.2 建议新增/补齐的数据字段
基于当前表结构,建议在 package events 所在 schema 增补以下字段,保持不引入新表:
- `retry_count int not null default 0`
- `last_retry_at timestamptz null`
- `next_retry_at timestamptz null`
- `last_failure_category varchar(32) null`
- `last_failure_detail text null`
文件落点:
- 新 migration`/home/long/project/supply-intelligence/migrations/0004_gateway_event_retry_state.sql`
- Postgres 读写:`/home/long/project/supply-intelligence/internal/repository/postgres.go`
- 内存实现同步:`/home/long/project/supply-intelligence/internal/repository/memory.go`
- 领域模型:`/home/long/project/supply-intelligence/internal/domain/types.go`
### 3.3 失败分类模型
建议在 domain 内新增消费失败分类枚举,只用于内部消费与观测,不暴露为新的上线状态:
- `temporary_network`
- `temporary_timeout`
- `temporary_5xx`
- `temporary_unavailable`
- `contract_invalid`
- `auth_forbidden`
- `idempotency_conflict`
- `business_rejected`
- `unknown`
文件落点:
- `/home/long/project/supply-intelligence/internal/domain/types.go`
### 3.4 consumer 层实现边界
现有文件:`/home/long/project/supply-intelligence/internal/gatewayconsumer/service.go`
当前 `applier` 返回:
- `GatewayAckResult`
- `detail string`
为支持失败分类与重试,建议改为内部结果结构,不改 HTTP 契约:
- `ackResult`:仅在最终写回时使用
- `retryable bool`
- `failureCategory string`
- `detail string`
consumer 的处理规则:
1. 拉取 event
2. 若 event `pending``next_retry_at` 为空或已到期,则尝试 apply
3. apply 成功:
- 更新 snapshot
- ack `applied`
4. apply 失败且可自动重试:
- `retry_count + 1`
-`last_failure_category/detail`
- 计算 `next_retry_at`
- 若次数 < 3保持 `pending`,不写最终 ack
- 若次数 == 3ack `failed`
5. apply 失败且不可自动重试:
- 直接 ack `failed`
- 持久化失败分类与 detail
### 3.5 repository 层需要补齐的方法
`internal/repository/interfaces.go` 增加以下接口,避免把 retry 逻辑塞进 HTTP handler
- `ListRetryablePendingPackageEvents(ctx context.Context, consumer string, now time.Time, limit int) []domain.PackageChangeEvent`
- `MarkPackageEventRetry(ctx context.Context, eventID string, retryCount int, nextRetryAt time.Time, category, detail string) (domain.PackageChangeEvent, error)`
- `GetPackageEventByID(ctx context.Context, eventID string) (domain.PackageChangeEvent, bool)`
对应 Postgres 实现文件:
- `/home/long/project/supply-intelligence/internal/repository/postgres.go`
对应内存实现文件:
- `/home/long/project/supply-intelligence/internal/repository/memory.go`
原因:
- 当前 `ListPackageEventsAfter` 是“事件流读取”语义,不适合直接承担“到期重试任务队列”语义
- 自动重试应按 pending + next_retry_at 过滤,而不是依赖 cursor 重新扫全量历史
### 3.6 poller/runtime 层映射
现有文件:
- `/home/long/project/supply-intelligence/internal/poller/gateway_package_poller.go`
- `/home/long/project/supply-intelligence/internal/poller/runtime.go`
建议映射:
1. `GatewayPackagePoller.PollOnce` 保留“单轮执行”语义
2. `gatewayconsumer.Service.ConsumeOnce` 内部改为:
- 先处理 cursor 拉取的新 pending event
- 再处理到期的 retryable pending event
3. `Runtime` 保持简单定时器,不在 runtime 层做复杂调度
4. 退避时间计算放在 `gatewayconsumer/service.go` 或新建 `gatewayconsumer/retry_policy.go`
这样可贴合当前结构,不引入新 scheduler/queue。
### 3.7 retry 状态机
事件消费内部状态机如下:
1. `pending` + 首次消费成功 -> `applied`
2. `pending` + retryable 失败 + 次数 1/2 -> 保持 `pending`,写 `next_retry_at`
3. `pending` + retryable 失败 + 次数 3 -> `failed`
4. `pending` + non-retryable 失败 -> `failed`
5. `failed` 不再被自动消费
6. `applied` 不再重复消费
这与 PM 口径一致,并且不破坏外部 API 现有三态语义。
## 4. Rollout / Rollback runbook 需要的脚本、接口、文档支撑
## 4.1 现有可复用接口
当前 runbook 已可复用的真实接口:
- `/healthz``/home/long/project/supply-intelligence/internal/httpapi/server.go :: handleHealth`
- `/metrics``/home/long/project/supply-intelligence/internal/httpapi/server.go :: Routes`
- `POST /internal/supply-intelligence/publish/package-event`
- `GET /internal/supply-intelligence/gateway/package-changes`
- `POST /internal/supply-intelligence/gateway/package-changes/{event_id}/ack`
- `POST /internal/supply-intelligence/gateway/consume-once`
- `GET /internal/supply-intelligence/models/{platform}/{model}/admission-state`
- `GET /internal/supply-intelligence/accounts/{account_id}/routing-state`
### 4.2 缺少的 runbook 支撑物
按 PM 要求runbook 不能只写文字,必须配套脚本与检查入口。建议新增:
1. 桌面演练脚本
- 路径:`/home/long/project/supply-intelligence/scripts/gateway_closure_smoke.sh`
- 作用:执行 publish -> package-changes -> consume-once/ack -> admission-state 检查
- 用于上线前前提第 3 条“至少完成一轮桌面演练”
2. 巡检脚本
- 路径:`/home/long/project/supply-intelligence/scripts/gateway_closure_inspect.sh`
- 作用:读取 metrics、healthz、admission-state 样本、失败 event 数量,输出是否满足继续/暂停/回滚条件
3. 回滚脚本或操作模板
- 路径:`/home/long/project/supply-intelligence/scripts/gateway_closure_rollback.sh`
- 作用:不是直接删数据,而是调用受控入口做“停止 poller / 定位失败 event / 人工 ack 或重新发布替换 package”的半自动操作模板
4. runbook 文档
- 路径:`/home/long/project/supply-intelligence/tech/RUNBOOK_GATEWAY_ROLLOUT_ROLLBACK_2026-05-08.md`
- 四段必须存在:
- 上线前检查
- 灰度观察
- 失败回滚
- 回滚后确认
### 4.3 需要补充的运维/控制接口
当前仓库缺少显式的 gateway runtime 开关与状态查看接口runbook 无法落地“暂停放量/停止自动消费”。建议新增最小控制入口:
1. runtime 状态查询
- 建议路径:`GET /internal/supply-intelligence/gateway/runtime-status`
- 落点:`/home/long/project/supply-intelligence/internal/httpapi/server.go`
- 返回poller 是否启动、cursor、最近轮询时间、最近错误、待重试数量、最终 failed 数量
2. runtime 暂停/恢复
- 建议路径:
- `POST /internal/supply-intelligence/gateway/runtime/pause`
- `POST /internal/supply-intelligence/gateway/runtime/resume`
- 落点:
- `internal/httpapi/server.go`
- `internal/app/app.go`
- `internal/poller/runtime.go`
- 作用:支持 runbook 中“暂停继续放量但不立即回滚”
注意:
- 这里不是引入新平台,只是给现有 poller/runtime 补一个可控开关
- 若不补该开关runbook 只能通过进程级停服务实现,粒度过粗
### 4.4 rollback 技术定义
本仓库现状下,回滚不应定义为“删除 event”或“改回 published 之前状态”,而应定义为以下受控动作之一:
1. 暂停 gateway consumer runtime阻止继续消费新 event
2. 对错误 package 生成替代发布 event让新正确版本覆盖旧错误版本
3. 对最终 failed event 人工判定后重新投递或关闭
4. 通过 admission-state 与 gateway snapshot 确认错误影响范围已止血
因此 runbook 需要的技术支撑文件为:
- 脚本:`scripts/gateway_closure_rollback.sh`
- 文档:`tech/RUNBOOK_GATEWAY_ROLLOUT_ROLLBACK_2026-05-08.md`
- 查询接口runtime-status、admission-state、package-changes
## 5. 观测指标、告警、巡检门禁落点
## 5.1 当前现状
当前 `internal/metrics/metrics.go` 已声明:
- `GatewayEventsProcessedTotal`
- `GatewayEventLatencySeconds`
- `AccountsByStatus`
- `RoutingEnabledAccounts`
但搜索当前仓库可见:这些指标尚未真正接到 gateway/probe/admission 关键调用链上,至少当前代码中没有使用引用。因此现状是:
- `/metrics` 端点存在
- 指标声明存在
- 关键 gateway 收口指标未真实打点
这正是 PM 文档中“已有 metrics 暴露,不等于生产口径清晰”的对应缺口。
### 5.2 指标落点设计
1. gateway 事件处理量
- 指标:`supply_intelligence_gateway_events_processed_total`
- 文件:`/home/long/project/supply-intelligence/internal/gatewayconsumer/service.go`
- 打点点位:
- 每次最终 `applied`
- 每次最终 `failed`
- 标签建议从现有 `{platform,event_type}` 扩展为 `{platform,event_type,result}`
2. gateway 事件处理时延
- 指标:`supply_intelligence_gateway_event_latency_seconds`
- 文件:`internal/gatewayconsumer/service.go`
- 打点点位:从开始 apply 到本次尝试结束
- 说明:用于看 PM 要求的“新 event 到 applied 时延是否稳定”虽然严格的“event 产生到 applied”还需要额外观察值
3. gateway 重试次数/积压
建议新增:
- `supply_intelligence_gateway_event_retries_total{platform,category}`
- `supply_intelligence_gateway_pending_retry_events{consumer}`
- `supply_intelligence_gateway_failed_events{consumer}`
文件:
- 声明:`/home/long/project/supply-intelligence/internal/metrics/metrics.go`
- 更新:`/home/long/project/supply-intelligence/internal/gatewayconsumer/service.go`
- 查询支撑:`/home/long/project/supply-intelligence/internal/repository/postgres.go``memory.go`
4. routing 状态盘点
- `AccountsByStatus`
- `RoutingEnabledAccounts`
- 更新点位:`/home/long/project/supply-intelligence/internal/probe/service.go`
- 作用:支撑 24h 巡检中的“按 platform 查看 account status / routing enabled 数量”
5. admission-state 观测支撑
不一定需要新增指标,但必须保留 API 抽样检查入口:
- `/internal/supply-intelligence/models/{platform}/{model}/admission-state`
- 文件:`internal/httpapi/server.go`
### 5.3 告警门禁映射
在不引入新基础设施前提下,本轮先交付“告警规则定义文档 + 脚本化巡检 + metrics 落点”。
建议新增文档:
- `/home/long/project/supply-intelligence/tech/OBSERVABILITY_GATEWAY_CLOSURE_2026-05-08.md`
其中至少定义以下门禁:
1. 15 分钟 applied 比例 < 95% -> 暂停放量
2. pending retry event > 10 -> 暂停放量
3. 连续 3 个最终 failed -> 触发回滚
4. metrics/healthz 不可达 -> 停止继续上线
5. auth_forbidden / contract_invalid / idempotency_conflict 任一出现 -> 升级 TechLead + XL
### 5.4 巡检脚本最小输出项
`gateway_closure_inspect.sh` 建议输出:
- healthz 是否 200
- metrics 是否可抓取
- pending event 数量
- due retry event 数量
- failed event 数量
- 最近 15 分钟 applied 数量 / failed 数量
- 最近 15 分钟 applied 比例
- 是否命中 continue / pause / rollback 阈值
要实现这些输出repository 层需要补充 count 查询;文件落点:
- `/home/long/project/supply-intelligence/internal/repository/interfaces.go`
- `/home/long/project/supply-intelligence/internal/repository/postgres.go`
- `/home/long/project/supply-intelligence/internal/repository/memory.go`
## 6. QA 设计审查时必须检查的调用链路
QA 不能只看定义,必须按“定义 -> 装配 -> 调用 -> 入口”四层核查。
### 链路 A发布后 event 进入待消费态
- 定义:`internal/publish/service.go :: PublishDraft`
- 装配:`internal/app/app.go :: buildApp`
- 调用:`internal/httpapi/server.go :: handlePublishPackageEvent`
- 入口:`POST /internal/supply-intelligence/publish/package-event`
- 必查点:返回体或后续 admission-state 中必须能看到 `gateway_sync_status=pending`
### 链路 Bgateway 自动消费成功
- 定义:`internal/gatewayconsumer/service.go :: ConsumeOnce`
- 装配:`internal/app/app.go``GatewayConsumerService``GatewayPoller``GatewayRuntime`
- 调用:`internal/poller/gateway_package_poller.go :: PollOnce`
- 入口:
- `POST /internal/supply-intelligence/gateway/consume-once`
- 或 runtime 定时启动 `internal/poller/runtime.go :: Start`
- 必查点:成功后 event `pending -> applied`snapshot 已写入
### 链路 Cgateway 自动重试
- 定义:新增 `gatewayconsumer/retry_policy.go``service.go` 内 retry 逻辑
- 装配:`app.go` 注入 consumer 与 runtime
- 调用:`ConsumeOnce` 内对 retryable event 的二次处理
- 入口:定时 runtime 或显式 `consume-once`
- 必查点:
- retryable 失败不会立刻写最终 `failed`
- `retry_count``next_retry_at` 持续变化
- 第 3 次失败后才转 `failed`
### 链路 D不可自动重试失败终态
- 定义:`gatewayconsumer/service.go` 失败分类
- 装配:同上
- 调用apply 返回 contract/auth/conflict/business reject
- 入口:`consume-once` 或 poller runtime
- 必查点:首轮即 `failed`,且 failure category/detail 可查询
### 链路 Eadmission-state 对 published/applied 差异暴露
- 定义:`internal/httpapi/server.go :: handleModelAdmissionState`
- 装配server routes mounted
- 调用repo `GetLatestPackageEvent`
- 入口:`GET /internal/supply-intelligence/models/{platform}/{model}/admission-state`
- 必查点:不能把 `package active` 误报成“已生效”
### 链路 Frunbook 执行前置检查
- 定义:`/healthz``/metrics``gateway_closure_smoke.sh`
- 装配:`server.go :: Routes`
- 调用:脚本对 HTTP 入口发起真实调用
- 入口:`scripts/gateway_closure_smoke.sh`
- 必查点:脚本不是伪脚本,命令与接口路径必须真实存在
### 链路 G暂停 / 恢复自动消费
- 定义:新增 runtime pause/resume 接口
- 装配:`server.go` + `app.go` + `poller/runtime.go`
- 调用runbook 中暂停放量时调用
- 入口pause/resume HTTP endpoint 或等价 CLI
- 必查点:暂停后不再消费新 event但已有状态查询仍可用
## 7. Engineer 任务拆解(必须包含具体文件路径)
以下任务按“贴合当前代码、最小必要改动”拆解。
### 7.1 Domain / Schema
1. `/home/long/project/supply-intelligence/internal/domain/types.go`
- 新增 gateway failure category 枚举
-`PackageChangeEvent` 增加 retry 元数据字段
2. `/home/long/project/supply-intelligence/migrations/0004_gateway_event_retry_state.sql`
- 为 package events 表新增 `retry_count` / `last_retry_at` / `next_retry_at` / `last_failure_category` / `last_failure_detail`
- 补索引:`ack_status + next_retry_at` 或等价查询索引
### 7.2 Repository
3. `/home/long/project/supply-intelligence/internal/repository/interfaces.go`
- 增加 retryable event 查询、event by id 查询、retry 标记、统计查询接口
4. `/home/long/project/supply-intelligence/internal/repository/postgres.go`
- 实现新增接口
- 更新 `ListPackageEventsAfter` / `GetLatestPackageEvent` / `AckPackageEvent` 的 scan 结构
- 增加 pending/retry/failed 统计查询
5. `/home/long/project/supply-intelligence/internal/repository/memory.go`
- 同步实现 retry 元数据与统计接口
6. `/home/long/project/supply-intelligence/internal/repository/memory_test.go`
- 补 memory 仓储行为测试
7. `/home/long/project/supply-intelligence/internal/repository/postgres_publish_tx_test.go`
- 补 Postgres 事务路径下 event retry 字段一致性测试
### 7.3 Gateway Consumer / Poller
8. `/home/long/project/supply-intelligence/internal/gatewayconsumer/service.go`
- 引入失败分类
- 增加自动重试判断
- 增加 1m/5m/15m 退避计算
- 成功时写 applied
- retryable 失败时保持 pending 并更新 next_retry_at
- non-retryable 或超过 3 次时写 failed
- 补 metrics 打点
9. `/home/long/project/supply-intelligence/internal/gatewayconsumer/retry_policy.go`
- 抽出 retry 判定与退避函数,避免 `service.go` 过重
10. `/home/long/project/supply-intelligence/internal/gatewayconsumer/service_test.go`
- 增加以下测试:
- retryable failure stays pending on attempt 1/2
- retryable failure becomes failed on attempt 3
- non-retryable failure becomes failed immediately
- applied path updates snapshot and metrics
11. `/home/long/project/supply-intelligence/internal/poller/gateway_package_poller.go`
- 保持最小变更;若需要暴露最近轮询结果,可加 last run state
12. `/home/long/project/supply-intelligence/internal/poller/runtime.go`
- 增加 pause/resume/status 能力
- 保留 Start/Stop 现有行为兼容
13. `/home/long/project/supply-intelligence/internal/poller/runtime_test.go`
- 增加 pause/resume/status 测试
### 7.4 HTTP API / App Wiring
14. `/home/long/project/supply-intelligence/internal/httpapi/server.go`
- 新增 runtime-status / pause / resume 路由
- 若需要,新增 inspect 用统计接口
- 保持现有 `package-changes``ack``consume-once` 不破坏兼容
15. `/home/long/project/supply-intelligence/internal/httpapi/server_test.go`
- 补充 runtime 控制接口测试
16. `/home/long/project/supply-intelligence/internal/httpapi/server_integration_test.go`
- 增加 pause 后不再自动消费、resume 后恢复消费测试
17. `/home/long/project/supply-intelligence/internal/app/app.go`
- 把 runtime 状态控制能力暴露给 HTTP 层
- 如需要,给 Application 增加获取 gateway runtime status 的方法
### 7.5 Metrics / Observability
18. `/home/long/project/supply-intelligence/internal/metrics/metrics.go`
- 为 gateway 增加 retry total / pending retry gauge / failed gauge
- 如必要,扩充 processed_total label 维度
19. `/home/long/project/supply-intelligence/internal/gatewayconsumer/service.go`
- 实际写 metrics不允许只声明不调用
20. `/home/long/project/supply-intelligence/internal/probe/service.go`
-`AccountsByStatus``RoutingEnabledAccounts` 真正接到状态写回路径
21. `/home/long/project/supply-intelligence/internal/probe/service_test.go`
- 补 probe 指标更新测试
### 7.6 Runbook / Scripts / Docs
22. `/home/long/project/supply-intelligence/scripts/gateway_closure_smoke.sh`
- 上线前演练脚本
- 验证 publish -> package-changes -> consume-once/ack -> admission-state
23. `/home/long/project/supply-intelligence/scripts/gateway_closure_inspect.sh`
- 24h / 72h 巡检脚本
- 输出 continue / pause / rollback 判定
24. `/home/long/project/supply-intelligence/scripts/gateway_closure_rollback.sh`
- 回滚操作模板脚本
- 支持 pause runtime、查询 failed、给出人工恢复提示
25. `/home/long/project/supply-intelligence/tech/RUNBOOK_GATEWAY_ROLLOUT_ROLLBACK_2026-05-08.md`
- 记录 rollout / rollback 执行步骤与负责人
26. `/home/long/project/supply-intelligence/tech/OBSERVABILITY_GATEWAY_CLOSURE_2026-05-08.md`
- 指标、告警、巡检、升级路径文档
### 7.7 E2E / QA 证据
27. `/home/long/project/supply-intelligence/internal/httpapi/postgres_e2e_test.go`
- 扩展为覆盖:
- pending -> applied
- retryable failure -> pending -> applied
- retryable failure x3 -> failed
- non-retryable failure -> failed
- runtime pause/resume
28. `/home/long/project/supply-intelligence/internal/poller/gateway_package_poller_test.go`
- 补 cursor + retry 混合路径测试
29. `/home/long/project/supply-intelligence/internal/httpapi/admission_state_api_test.go`
- 补 published/pending/applied/failed 语义测试
## 8. QA 审查结论口径
### 8.1 当前可给出的设计阶段结论
- 结论:可进入 QA 设计审查
原因:
1. 真源与 PM 收口要求已经被映射到当前仓库真实文件
2. gateway 主链现有代码落点真实存在,不是空设计
3. 本文件已把失败重试、runbook、观测、调用链路、Engineer 任务细化到文件级
4. 没有发散到新基础设施,符合当前仓库约束
### 8.2 QA 需要重点卡住的补设计红线
若后续实现/补文档出现以下任一情况QA 应打回:
1. 仍把 `published``active``applied` 混为一谈
2. 仍用 `failed` 表示“以后自动再试”,没有 pending + retry 元数据
3. 仅新增 metrics 定义,不在真实调用链打点
4. runbook 只有文档,没有脚本/接口支撑
5. pause/resume 缺失,导致“暂停放量”只能靠停整个服务
6. E2E 仍只测 happy path不测 retryable / final failed 路径
## 9. 最终结论
当前结论:可进入 QA 设计审查。
说明:
- 这是“设计可审查”的结论,不是“当前代码已可上线”的结论
- 进入实现前,不再需要补 PM 口径
- 进入实现后,必须严格按本文件的文件路径和调用链补齐 retry、runbook、observability、QA 证据
## 10. 本文档对应的绝对路径
`/home/long/project/supply-intelligence/tech/TECHLEAD_GATEWAY_CLOSURE_DESIGN_2026-05-08.md`