chore: sync local project state
This commit is contained in:
154
tech/B2_B3_B4_IMPLEMENTATION_SPEC_2026-05-07.md
Normal file
154
tech/B2_B3_B4_IMPLEMENTATION_SPEC_2026-05-07.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# B2/B3/B4 实施规格(2026-05-07)
|
||||
|
||||
状态:当前有效
|
||||
范围:candidate 状态收敛、publish 事务闭环、admission-state API 真正接线
|
||||
真源:
|
||||
- tech/CURRENT_SOURCE_OF_TRUTH_2026-05.md
|
||||
- tech/BASELINE_TECHLEAD_V2.md
|
||||
- tech/GATEWAY_CONSUMER_DECISION_2026-05.md
|
||||
|
||||
## 1. 目标
|
||||
|
||||
把 supply-intelligence 从“各子模块最小骨架存在”推进到“candidate -> admission -> draft package -> publish -> gateway sync state -> admission-state 查询”这一条真实生产闭环更接近可验状态。
|
||||
|
||||
本轮不扩范围到独立平台化、重基础设施、自动注册,只做当前收口板 B2/B3/B4。
|
||||
|
||||
## 2. 当前已验证现状
|
||||
|
||||
1. `go test ./...` 当前通过。
|
||||
2. `internal/domain/types.go` 中 candidate 状态枚举已不包含 `pending_admission` / `admitted`。
|
||||
3. `internal/httpapi/server.go` 的 `parseDiscoveryCandidateStatus()` 已只接受:
|
||||
- discovered
|
||||
- testing
|
||||
- test_passed
|
||||
- test_failed
|
||||
- retry_pending
|
||||
- ignored
|
||||
- published
|
||||
- deprecated
|
||||
- closed
|
||||
4. `internal/httpapi/server.go` 已存在 `/internal/supply-intelligence/models/{platform}/{model}/admission-state` 路由与 handler。
|
||||
5. `internal/publish/service.go` 目前只支持“追加 package published event”,还不是“运营确认上架事务”。
|
||||
6. `internal/admission/service.go` 在测试通过后会创建/更新 draft package,并把 candidate 置为 `test_passed`。
|
||||
7. `internal/httpapi/admission_state_api_test.go` 目前只验证 candidate/package/event 聚合读取,不验证真实 publish 事务。
|
||||
|
||||
## 3. 本轮必须收敛的缺口
|
||||
|
||||
### B2. candidate 状态与 admission 流转
|
||||
|
||||
必须满足:
|
||||
- admission 只允许 `discovered` / `retry_pending` 进入执行。
|
||||
- admission 执行开始后置为 `testing`。
|
||||
- admission 失败后置为 `test_failed` 或 `retry_pending`(本轮沿用现状失败归 `test_failed`)。
|
||||
- admission 成功后置为 `test_passed`。
|
||||
- publish 成功后 candidate 必须从 `test_passed` -> `published`。
|
||||
- 不允许重新引入旧状态口径。
|
||||
|
||||
### B3. publish 事务闭环
|
||||
|
||||
必须新增真实语义:
|
||||
- 输入不再只是 event append 所需字段。
|
||||
- 以 `platform + model`(必要时 package/candidate)为主键读取当前真实状态。
|
||||
- 仅当 candidate 最新状态为 `test_passed` 且 package 当前为 `draft` 时允许发布。
|
||||
- 发布动作要同时完成:
|
||||
1. package `draft -> active`
|
||||
2. candidate `test_passed -> published`
|
||||
3. 追加 `supply_package_published` event,默认 `gateway_sync_status=pending`
|
||||
- 明确 `published != applied`:gateway applied 仍由 ack 驱动。
|
||||
|
||||
### B4. admission-state API
|
||||
|
||||
必须返回当前组合真相:
|
||||
- latest candidate truth
|
||||
- current package truth
|
||||
- latest matching package event truth
|
||||
- gateway sync status
|
||||
|
||||
并在 publish 事务跑完后能够体现:
|
||||
- candidate_status = published
|
||||
- package_status = active
|
||||
- gateway_sync_status = pending(直到 ack)
|
||||
|
||||
## 4. 最小改动设计
|
||||
|
||||
### 4.1 repository / app 适配层
|
||||
|
||||
尽量不改 repository 主接口的大结构,只补 publish service 所需最小能力,优先复用已有:
|
||||
- `GetLatestDiscoveryCandidateContext()`
|
||||
- `GetSupplyPackage()`
|
||||
- `UpsertSupplyPackage()`
|
||||
- `UpdateCandidateStatus()`
|
||||
- `AppendPackageEventContext()`
|
||||
|
||||
如 publish 包直接依赖 domain/repository 成本更低,可在 publish 内定义更完整 repo interface,再由现有 repository.Repository 满足。
|
||||
|
||||
### 4.2 publish service 新增主入口
|
||||
|
||||
建议新增:
|
||||
- `PublishDraft(ctx, PublishDraftInput) (PublishDraftOutput, error)`
|
||||
|
||||
输入最小字段:
|
||||
- event_id
|
||||
- platform
|
||||
- model
|
||||
- actor/source(可选;本轮如无真实审计先留空)
|
||||
- occurred_at(可选)
|
||||
|
||||
输出最小字段:
|
||||
- candidate
|
||||
- package
|
||||
- event
|
||||
- gateway_sync_status
|
||||
|
||||
保留 `RecordPackagePublished()` 兼容测试/已有接口,但 HTTP 主入口要逐步切换为真正发布语义,而不是“外部直接塞 event”。
|
||||
|
||||
### 4.3 HTTP API
|
||||
|
||||
当前 `/internal/supply-intelligence/publish/package-event` 若继续存在,本轮将其语义提升为“确认发布 draft package”,不再允许脱离 candidate/package 真相直接伪造 event。
|
||||
|
||||
请求体建议最小化为:
|
||||
- event_id
|
||||
- platform
|
||||
- model
|
||||
- occurred_at
|
||||
|
||||
如果保留 package_id/version 也应以服务端真相为准,不信任调用方覆盖 package 当前状态。
|
||||
|
||||
## 5. 验证标准
|
||||
|
||||
必须新增/更新测试覆盖:
|
||||
|
||||
1. publish 成功:
|
||||
- candidate `test_passed -> published`
|
||||
- package `draft -> active`
|
||||
- event appended with pending sync
|
||||
|
||||
2. publish 拒绝:
|
||||
- candidate 不是 `test_passed` 时拒绝
|
||||
- package 不是 `draft` 时拒绝
|
||||
- candidate/package 不存在时拒绝
|
||||
|
||||
3. admission-state:
|
||||
- publish 后查询可看到 `published + active + pending`
|
||||
- ack 后查询可看到 `applied/failed`
|
||||
|
||||
4. 全量验证:
|
||||
- `go test ./...`
|
||||
|
||||
## 6. 不做项
|
||||
|
||||
本轮明确不做:
|
||||
- 审计表完整补齐
|
||||
- actor/审批链完整产品化
|
||||
- DB 事务级锁语义重构
|
||||
- gateway 实际远端集成
|
||||
- auto-supply / deep registration
|
||||
|
||||
## 7. 完成定义
|
||||
|
||||
仅当以下同时成立,B2/B3/B4 才能算完成:
|
||||
- 代码不再只有“event append 记录器”语义
|
||||
- publish 真正驱动 candidate/package 状态变化
|
||||
- admission-state 能反映 publish 后组合真相
|
||||
- 新增测试通过
|
||||
- `go test ./...` 通过
|
||||
487
tech/G4_GATEWAY_REMOTE_INTEGRATION_DESIGN_2026-05-10.md
Normal file
487
tech/G4_GATEWAY_REMOTE_INTEGRATION_DESIGN_2026-05-10.md
Normal file
@@ -0,0 +1,487 @@
|
||||
# G4 真实远端 Gateway 集成验证:技术设计与验证方案
|
||||
|
||||
状态:当前有效
|
||||
仓库:`/home/long/project/supply-intelligence`
|
||||
阶段:G1-G3 已完成(本地 + tksea 43.155.133.187:8081),G4 待验证
|
||||
|
||||
---
|
||||
|
||||
## 1. 设计范围
|
||||
|
||||
### 1.1 In Scope
|
||||
- supply-intelligence 与 sub2api/tokens-reef 的端到端事件触达验证
|
||||
- 利用现有 HTTP API(package-changes / ack / runtime pause-resume)构造真实远端消费窗口
|
||||
- 改造 sub2api-bridge 为"真实远端 gateway 代理",走外部消费+手动 ack 闭环
|
||||
- 在 tksea 可触及环境内完成最小可行的对账证据链
|
||||
|
||||
### 1.2 Out of Scope
|
||||
- 不修改 supply-intelligence 核心 publish / consume-once / retry 状态机
|
||||
- 不恢复或重建已下线的 103.56.49.28 旧 sub2api 节点
|
||||
- 不引入新的消息队列或外部基础设施
|
||||
- 不修改 admission 测试逻辑(当前 tksea 使用 ADMISSION_TEST_MOCK=1,与 G4 无关)
|
||||
|
||||
### 1.3 约束
|
||||
- 必须复用现有 HTTP 契约与 runtime 控制接口
|
||||
- 验证脚本必须可在一个 QA 窗口内(< 15 分钟)执行完毕
|
||||
- 对账证据必须双向可校验:supply-intelligence 侧 + sub2api-bridge 侧
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构与模块分析(现有事件流)
|
||||
|
||||
### 2.1 当前事件流拓扑
|
||||
|
||||
```
|
||||
[Publisher]
|
||||
|
|
||||
v
|
||||
POST /internal/supply-intelligence/publish/package-event
|
||||
|
|
||||
v
|
||||
internal/publish/service.go :: PublishDraft
|
||||
|
|
||||
v
|
||||
Repository :: PackageChangeEvent (gateway_sync_status = pending)
|
||||
|
|
||||
+---> 路径 A:内部自动消费(默认)
|
||||
| GatewayPackagePoller (1s) -> ConsumeOnce -> applier -> auto ack
|
||||
|
|
||||
+---> 路径 B:外部远端消费(G4 验证目标)
|
||||
GET /gateway/package-changes -> 远端应用 -> POST .../ack
|
||||
```
|
||||
|
||||
### 2.2 关键模块状态(截至代码审查)
|
||||
|
||||
| 模块 | 文件 | 状态 | G4 相关性 |
|
||||
|------|------|------|-----------|
|
||||
| Publish Service | `internal/publish/service.go` | 已闭合 | 产生 pending event |
|
||||
| Gateway Consumer | `internal/gatewayconsumer/service.go` | 已闭合(含 retry/metrics) | 路径 A 自动消费 |
|
||||
| HTTP Server | `internal/httpapi/server.go` | 已闭合(含 pause/resume/status) | 提供路径 B API + runtime 控制 |
|
||||
| Repository (PG) | `internal/repository/postgres.go` | 已闭合(含 retry 字段) | 持久化 event / ack |
|
||||
| Repository (Mem) | `internal/repository/memory.go` | 已闭合(含 retry 字段) | 本地验证用 |
|
||||
| Poller/Runtime | `internal/poller/runtime.go` | 已闭合(含 pause/resume) | 控制本地消费窗口 |
|
||||
| Metrics | `internal/metrics/metrics.go` | 已声明 | 观测支撑 |
|
||||
| sub2api-bridge | `cmd/sub2api-bridge/main.go` | **旧实现,需改造** | G4 核心验证工具 |
|
||||
|
||||
### 2.3 事件流结论
|
||||
- supply-intelligence 已有完整的"内部自动消费"闭环(路径 A)
|
||||
- supply-intelligence 已有完整的"外部消费+手动 ack" HTTP 契约(路径 B)
|
||||
- 当前缺口:没有外部消费者真实走过路径 B 并留下对账证据
|
||||
- G4 目标就是补全路径 B 的端到端验证
|
||||
|
||||
---
|
||||
|
||||
## 3. 接口与数据模型
|
||||
|
||||
### 3.1 supply-intelligence 对外暴露的 Gateway 接口
|
||||
|
||||
| 方法 | 路径 | 作用 | 代码落点 |
|
||||
|------|------|------|----------|
|
||||
| GET | `/internal/supply-intelligence/gateway/package-changes?cursor=` | 拉取事件流(含 pending/applied/failed) | `server.go:311` |
|
||||
| POST | `/internal/supply-intelligence/gateway/package-changes/{event_id}/ack` | 外部 consumer 回写 ack | `server.go:320` |
|
||||
| POST | `/internal/supply-intelligence/gateway/consume-once` | 内部自动消费(服务端执行 applier+ack) | `server.go:362` |
|
||||
| GET | `/internal/supply-intelligence/gateway/runtime-status` | 查看 poller 状态 | `server.go:389` |
|
||||
| POST | `/internal/supply-intelligence/gateway/runtime/pause` | 暂停本地自动消费 | `server.go:415` |
|
||||
| POST | `/internal/supply-intelligence/gateway/runtime/resume` | 恢复本地自动消费 | `server.go:431` |
|
||||
| GET | `/internal/supply-intelligence/models/{platform}/{model}/admission-state` | 查询 model 最新 event 状态 | `server.go:507` |
|
||||
|
||||
### 3.2 ack 请求/响应模型
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
POST /internal/supply-intelligence/gateway/package-changes/{event_id}/ack
|
||||
{
|
||||
"consumer": "sub2api-bridge",
|
||||
"result": "applied",
|
||||
"detail": "synced to tokens-reef"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
- 204 No Content:成功
|
||||
- 400:invalid_json / invalid_result
|
||||
- 404:event not found
|
||||
- 500:internal_error
|
||||
|
||||
### 3.3 package-changes 响应模型
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"event_id": "evt-xxx",
|
||||
"account_id": 1,
|
||||
"event_type": "supply_package_published",
|
||||
"package_id": 1001,
|
||||
"platform": "openai",
|
||||
"model": "gpt-4.1-mini",
|
||||
"occurred_at": "2026-05-10T12:00:00Z",
|
||||
"version": 2,
|
||||
"gateway_sync_status": "pending",
|
||||
"retry_count": 0,
|
||||
"next_retry_at": null,
|
||||
"last_failure_category": ""
|
||||
}
|
||||
],
|
||||
"next_cursor": "evt-xxx"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 runtime-status 响应模型
|
||||
|
||||
```json
|
||||
{
|
||||
"started": true,
|
||||
"paused": false,
|
||||
"cursor": "evt-xxx",
|
||||
"last_poll_at": "2026-05-10T12:01:00Z",
|
||||
"last_error": "",
|
||||
"pending_retry_events": 0,
|
||||
"failed_events": 0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 对接点分析(supply-intelligence -> sub2api/tokens-reef)
|
||||
|
||||
### 4.1 当前 sub2api-bridge 的问题
|
||||
|
||||
**代码路径:** `cmd/sub2api-bridge/main.go`
|
||||
|
||||
当前 sub2api-bridge 调用的是 `/gateway/consume-once`:
|
||||
```
|
||||
consumeOnce -> POST /gateway/consume-once -> supply-intelligence 服务端执行本地 applier -> 自动 ack
|
||||
```
|
||||
|
||||
这导致 sub2api-bridge 只是**读取了服务端已经处理完毕的结果**,而不是**真实代表远端 gateway 消费事件**。对账证据只能证明"服务端本地模拟了消费",不能证明"事件触达了远端 gateway"。
|
||||
|
||||
### 4.2 改造后的 sub2api-bridge 对接模型
|
||||
|
||||
改造目标:让 sub2api-bridge 成为路径 B 的真实远端 consumer。
|
||||
|
||||
```
|
||||
sub2api-bridge (远端 gateway 代理)
|
||||
|
|
||||
|--1---> GET /gateway/package-changes?cursor=
|
||||
| (拉取 pending 事件)
|
||||
|
|
||||
|--2---> 应用到本地 DB (supply_bridge_log)
|
||||
| (真实持久化 = "远端已接收"证据)
|
||||
|
|
||||
|--3---> POST /gateway/package-changes/{event_id}/ack
|
||||
| {"consumer":"sub2api-bridge","result":"applied"}
|
||||
|
|
||||
v
|
||||
supply-intelligence 侧 event 状态变为 applied
|
||||
```
|
||||
|
||||
### 4.3 认证方式
|
||||
- 当前 supply-intelligence HTTP API 无认证(内部网络)
|
||||
- sub2api-bridge 与 supply-intelligence 通过内网/localhost 通信
|
||||
- G4 验证阶段保持此约束,不新增认证复杂度
|
||||
|
||||
### 4.4 对账机制
|
||||
|
||||
**supply-intelligence 侧对账点:**
|
||||
1. `GET /models/{platform}/{model}/admission-state` -> `last_event.gateway_sync_status`
|
||||
2. `GET /gateway/runtime-status` -> pending/failed 计数
|
||||
3. Repository 直接查询:`ack_consumer='sub2api-bridge'` 且 `ack_status='applied'`
|
||||
|
||||
**sub2api-bridge 侧对账点:**
|
||||
1. `supply_bridge_log` 表:`SELECT * FROM supply_bridge_log WHERE event_id='evt-xxx'`
|
||||
2. bridge 程序日志 stdout:记录每次 fetch/bridge/ack 动作
|
||||
|
||||
**双向对账断言:**
|
||||
```
|
||||
supply-intelligence.event.acked_at IS NOT NULL
|
||||
AND supply-intelligence.event.consumer = 'sub2api-bridge'
|
||||
AND supply-intelligence.event.gateway_sync_status = 'applied'
|
||||
AND sub2api-bridge.supply_bridge_log.event_id = '{event_id}'
|
||||
AND sub2api-bridge.supply_bridge_log.result = 'applied'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. G4 验证方案设计(最小可行方案)
|
||||
|
||||
### 5.1 验证环境
|
||||
|
||||
| 组件 | 地址/位置 | 角色 |
|
||||
|------|-----------|------|
|
||||
| supply-intelligence (tksea) | 43.155.133.187:8081 | 事件源 + 状态持久化 |
|
||||
| sub2api-bridge (本地或 tksea同机) | 本地编译运行 | 远端 gateway 代理 |
|
||||
| sub2api DB (本地 Postgres) | localhost:5432/sub2api | 远端 gateway 持久化证据 |
|
||||
|
||||
**环境变量(tksea):**
|
||||
- `SEED_LOCAL_DEMO=1`:已预置 demo candidate/package
|
||||
- `ADMISSION_TEST_MOCK=1`:与 G4 无关
|
||||
|
||||
### 5.2 验证前置条件
|
||||
1. tksea 上 supply-intelligence 可访问(`curl 43.155.133.187:8081/healthz` == 200)
|
||||
2. 本地有可编译 Go 环境 + 本地 Postgres(或 SQLite 替代)
|
||||
3. supply-intelligence 的本地 gateway runtime 可被暂停(已有 API 支持)
|
||||
|
||||
### 5.3 验证执行步骤(SOP)
|
||||
|
||||
**Step 0:暂停本地自动消费(打开外部验证窗口)**
|
||||
```bash
|
||||
curl -X POST http://43.155.133.187:8081/internal/supply-intelligence/gateway/runtime/pause
|
||||
# 期望:{"paused":true}
|
||||
```
|
||||
|
||||
**Step 1:确认 demo 数据就绪**
|
||||
```bash
|
||||
curl http://43.155.133.187:8081/internal/supply-intelligence/models/openai/gpt-4.1-mini/admission-state
|
||||
# 期望:candidate.status=test_passed, package.status=draft, gateway_sync_status=""
|
||||
```
|
||||
|
||||
**Step 2:发布 package,产生 pending event**
|
||||
```bash
|
||||
curl -X POST http://43.155.133.187:8081/internal/supply-intelligence/publish/package-event \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"event_id":"g4-test-001","platform":"openai","model":"gpt-4.1-mini"}'
|
||||
# 期望:返回 Event 对象,gateway_sync_status=pending
|
||||
```
|
||||
|
||||
**Step 3:启动改造后的 sub2api-bridge**
|
||||
```bash
|
||||
export SUPPLY_URL=http://43.155.133.187:8081
|
||||
export CONSUMER=sub2api-bridge
|
||||
export SUB2API_DB="postgres://sub2api:***@localhost:5432/sub2api?sslmode=disable"
|
||||
./sub2api-bridge
|
||||
```
|
||||
|
||||
**Step 4:bridge 执行外部消费闭环**
|
||||
- bridge 调用 `GET /gateway/package-changes`
|
||||
- 过滤出 `gateway_sync_status=pending` 的事件
|
||||
- 将事件写入本地 `supply_bridge_log`
|
||||
- 调用 `POST /gateway/package-changes/{event_id}/ack` 回写 applied
|
||||
|
||||
**Step 5:supply-intelligence 侧验证**
|
||||
```bash
|
||||
curl http://43.155.133.187:8081/internal/supply-intelligence/models/openai/gpt-4.1-mini/admission-state
|
||||
# 断言:last_event.gateway_sync_status == "applied"
|
||||
```
|
||||
|
||||
**Step 6:sub2api-bridge 侧验证**
|
||||
```sql
|
||||
SELECT event_id, result, detail FROM supply_bridge_log WHERE event_id = 'g4-test-001';
|
||||
-- 断言:存在记录,result='applied'
|
||||
```
|
||||
|
||||
**Step 7:恢复本地 runtime**
|
||||
```bash
|
||||
curl -X POST http://43.155.133.187:8081/internal/supply-intelligence/gateway/runtime/resume
|
||||
# 期望:{"paused":false}
|
||||
```
|
||||
|
||||
### 5.4 验证通过标准
|
||||
|
||||
| 检查项 | 通过标准 | 对账侧 |
|
||||
|--------|----------|--------|
|
||||
| event 发布成功 | HTTP 200,返回 event_id | supply-intelligence |
|
||||
| runtime 暂停成功 | HTTP 200,`paused:true` | supply-intelligence |
|
||||
| event 未被本地消费 | pause 期间 `gateway_sync_status` 保持 `pending` | supply-intelligence |
|
||||
| bridge 成功拉取 | bridge stdout 出现 fetch 日志 | sub2api-bridge |
|
||||
| bridge 成功持久化 | `supply_bridge_log` 存在对应记录 | sub2api-bridge |
|
||||
| bridge 成功 ack | HTTP 204,无错误 | supply-intelligence |
|
||||
| event 终态 applied | `admission-state` 显示 `applied` | supply-intelligence |
|
||||
| consumer 标记正确 | event 的 `consumer='sub2api-bridge'` | supply-intelligence |
|
||||
| runtime 恢复成功 | HTTP 200,`paused:false` | supply-intelligence |
|
||||
|
||||
### 5.5 失败场景覆盖
|
||||
|
||||
| 场景 | 预期行为 | 验证方式 |
|
||||
|------|----------|----------|
|
||||
| bridge ack 前崩溃 | event 保持 pending,可重试 | 查询 event 状态仍为 pending |
|
||||
| bridge ack failed | supply-intelligence 记录 failed | 查询 event 状态为 failed,consumer=detail 可查 |
|
||||
| runtime 未 pause | 本地 poller 可能在 bridge 前消费掉 event | 需要重新发布新 event 并严格先 pause |
|
||||
| 网络中断 | bridge fetch/ack 报错,event 状态不变 | bridge 日志 + event 状态不变 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 任务拆解(具体到文件/函数,每项 < 5 分钟)
|
||||
|
||||
### 6.1 sub2api-bridge 改造
|
||||
|
||||
**任务 1:改造拉取逻辑**
|
||||
- 文件:`cmd/sub2api-bridge/main.go`
|
||||
- 动作:将 `consumeOnce` 从调用 `/gateway/consume-once` 改为调用 `/gateway/package-changes`
|
||||
- 函数:`fetchPackageChanges(ctx, baseURL, cursor)`
|
||||
- 输出:返回 `[]PackageChangeEvent` + `next_cursor`
|
||||
|
||||
**任务 2:改造 ack 逻辑**
|
||||
- 文件:`cmd/sub2api-bridge/main.go`
|
||||
- 动作:新增 `ackPackageChange(ctx, baseURL, eventID, consumer, result, detail)`
|
||||
- 函数:调用 `POST /gateway/package-changes/{event_id}/ack`
|
||||
- 输出:HTTP 204 或 error
|
||||
|
||||
**任务 3:主循环改造**
|
||||
- 文件:`cmd/sub2api-bridge/main.go`
|
||||
- 动作:将 `main()` 中的循环从 `consumeOnce -> bridge` 改为 `fetchPackageChanges -> filter pending -> bridgeToSub2API -> ackPackageChange`
|
||||
- 逻辑:
|
||||
```
|
||||
cursor := ""
|
||||
for {
|
||||
events, nextCursor := fetchPackageChanges(cursor)
|
||||
for _, evt := range events {
|
||||
if evt.GatewaySyncStatus != "pending" { continue }
|
||||
if err := bridgeToSub2API(db, evt); err != nil { log; continue }
|
||||
if err := ackPackageChange(evt.EventID, "applied", "synced"); err != nil { log }
|
||||
}
|
||||
cursor = nextCursor
|
||||
if cursor == "" { sleep 10s }
|
||||
}
|
||||
```
|
||||
|
||||
**任务 4:编译与本地测试**
|
||||
- 命令:`cd /home/long/project/supply-intelligence && go build ./cmd/sub2api-bridge`
|
||||
- 验证:二进制可生成,无编译错误
|
||||
|
||||
### 6.2 G4 验证脚本
|
||||
|
||||
**任务 5:编写 G4 验证脚本**
|
||||
- 文件:`scripts/g4_remote_gateway_verify.sh`
|
||||
- 动作:封装 5.3 节的 Step 0-7
|
||||
- 输入:SUPPLY_URL, SUB2API_DB
|
||||
- 输出:PASS / FAIL + 对账摘要
|
||||
|
||||
**任务 6:脚本本地调试**
|
||||
- 先对本地 supply-intelligence(`go run ./cmd/supply-intelligence`,PORT=8081)执行验证
|
||||
- 确认所有断言通过
|
||||
|
||||
### 6.3 tksea 远程验证
|
||||
|
||||
**任务 7:tksea 环境检查**
|
||||
- 确认 `43.155.133.187:8081/healthz` 可达
|
||||
- 确认 runtime pause/resume API 响应正常
|
||||
- 确认 demo 数据存在
|
||||
|
||||
**任务 8:tksea G4 执行**
|
||||
- 在可访问 tksea 的机器上运行改造后的 sub2api-bridge
|
||||
- 执行 `scripts/g4_remote_gateway_verify.sh`
|
||||
- 收集对账证据(supply-intelligence event 记录 + bridge log 记录)
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与保护
|
||||
|
||||
| 风险 | 影响 | 保护/降级 |
|
||||
|------|------|-----------|
|
||||
| tksea 不可达或 API 变更 | G4 无法执行 | 先在本地完整跑通,再迁移到 tksea;本地使用 postgres 或内存模式均可验证 |
|
||||
| runtime pause 后仍被本地消费 | 事件被提前消费,bridge 无事件可拉 | 验证方案加入"发布前 pause"时序;若仍失败,检查是否有其他 consumer 实例在运行 |
|
||||
| bridge ack 重复/幂等问题 | 同一 event 被 ack 两次 | supply-intelligence `AckPackageEvent` 是幂等更新(按 event_id),重复 ack 不会破坏状态 |
|
||||
| bridge DB 不可写 | 远端证据缺失 | bridge 在写入 DB 前应先检查连接;写入失败不打 ack,event 保持 pending 可重试 |
|
||||
| 网络抖动导致 fetch/ack 部分成功 | event 状态不一致 | fetch 成功但 ack 失败时,bridge 不记录为成功;下次轮询会重新发现该 pending event(因为未被 ack) |
|
||||
| 当前 tksea 使用 in-memory 后端 | 事件在进程重启后丢失 | G4 验证不要求持久化跨重启,只需验证同一进程生命周期内的触达闭环;若 tksea 使用 postgres,则更优 |
|
||||
|
||||
---
|
||||
|
||||
## 8. QA 交接与实施约束
|
||||
|
||||
### 8.1 QA 必须核查的调用链
|
||||
|
||||
**链路 G4-A:外部消费者拉取事件**
|
||||
- 定义:`internal/httpapi/server.go :: handleListPackageChanges`
|
||||
- 装配:`app.go` -> `NewServer` -> `Routes`
|
||||
- 调用:`repo.ListPackageEventsAfter`
|
||||
- 入口:`GET /gateway/package-changes?cursor=`
|
||||
- 必查点:返回体包含 `gateway_sync_status` 字段,且 pending 事件可被外部消费者识别
|
||||
|
||||
**链路 G4-B:外部消费者回写 ack**
|
||||
- 定义:`internal/httpapi/server.go :: handleAckPackageChange`
|
||||
- 装配:同上
|
||||
- 调用:`repo.AckPackageEvent`
|
||||
- 入口:`POST /gateway/package-changes/{event_id}/ack`
|
||||
- 必查点:ack 后 `admission-state` 中 `gateway_sync_status` 变为 applied/failed
|
||||
|
||||
**链路 G4-C:runtime 暂停/恢复**
|
||||
- 定义:`internal/poller/runtime.go :: Pause/Resume`
|
||||
- 装配:`app.go` -> `gatewayRuntime`
|
||||
- 调用:`server.go` HTTP handler
|
||||
- 入口:`POST /gateway/runtime/pause` / `resume`
|
||||
- 必查点:pause 后 `gateway/runtime-status` 返回 `paused:true`,且 poller 不再消费新 event
|
||||
|
||||
**链路 G4-D:sub2api-bridge 端到端**
|
||||
- 定义:`cmd/sub2api-bridge/main.go`
|
||||
- 装配:`go build ./cmd/sub2api-bridge`
|
||||
- 调用:package-changes -> bridge log -> ack
|
||||
- 入口:bridge 进程启动
|
||||
- 必查点:bridge stdout 显示完整闭环,DB 中有记录,supply-intelligence 侧状态同步
|
||||
|
||||
### 8.2 实施约束(Engineer)
|
||||
1. 不允许修改 supply-intelligence 的 publish / consumer / retry 核心逻辑
|
||||
2. sub2api-bridge 改造只允许在 `cmd/sub2api-bridge/` 内修改
|
||||
3. 验证脚本必须放在 `scripts/` 目录,且使用 bash/curl/psql 等通用工具
|
||||
4. 所有修改必须通过 `go build ./...` 编译检查
|
||||
|
||||
### 8.3 QA 验收标准
|
||||
- [ ] `scripts/g4_remote_gateway_verify.sh` 在本地环境执行通过
|
||||
- [ ] `scripts/g4_remote_gateway_verify.sh` 在 tksea 环境执行通过
|
||||
- [ ] 双向对账断言全部通过(supply-intelligence 侧 + bridge 侧)
|
||||
- [ ] runtime pause/resume 不影响其他 API 可用性
|
||||
- [ ] 失败场景(不 ack / ack failed)可复现并产生预期状态
|
||||
|
||||
---
|
||||
|
||||
## 9. 阶段门控结论
|
||||
|
||||
### 9.1 当前状态评估
|
||||
|
||||
| 维度 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 代码成熟度 | 已就绪 | supply-intelligence 侧 publish/consume/ack/retry/runtime-control 全部已实现 |
|
||||
| 接口可用性 | 已就绪 | package-changes + ack + pause/resume API 真实存在且可调用 |
|
||||
| 远端代理 | 需改造 | sub2api-bridge 当前走 consume-once(本地自动 ack),需改为 package-changes + 手动 ack |
|
||||
| 验证脚本 | 待编写 | 需新增 `scripts/g4_remote_gateway_verify.sh` |
|
||||
| 环境可达性 | 已知风险 | 103.56.49.28 不可达,但 tksea 43.155.133.187 可用,可作为替代验证目标 |
|
||||
|
||||
### 9.2 结论
|
||||
|
||||
**阶段门控结论:可进入 G4 实施**
|
||||
|
||||
原因:
|
||||
1. supply-intelligence 核心代码已具备 G4 所需的全部 API 与控制能力
|
||||
2. 缺口集中在 sub2api-bridge 的改造和验证脚本的编写,范围可控
|
||||
3. 改造不触及 supply-intelligence 核心,风险低
|
||||
4. 有明确的本地->tksea 两级验证路径,可逐步推进
|
||||
|
||||
### 9.3 进入下一阶段的条件
|
||||
- sub2api-bridge 改造完成并通过本地验证
|
||||
- `scripts/g4_remote_gateway_verify.sh` 编写完成并通过本地验证
|
||||
- tksea 环境验证通过,产出双向对账证据
|
||||
|
||||
---
|
||||
|
||||
## 10. 下游执行约束摘要
|
||||
|
||||
### Engineer
|
||||
- 任务范围:`cmd/sub2api-bridge/main.go` 改造 + `scripts/g4_remote_gateway_verify.sh` 编写
|
||||
- 不允许触碰 supply-intelligence 核心代码
|
||||
- 本地验证通过后,再提交到 tksea 验证
|
||||
- 产出物:改造后的 sub2api-bridge 二进制 + 验证脚本 + 执行日志
|
||||
|
||||
### QA
|
||||
- 核查四条调用链(G4-A ~ G4-D)是否真实可调用
|
||||
- 执行 `scripts/g4_remote_gateway_verify.sh` 并确认双向对账
|
||||
- 验证 runtime pause/resume 的隔离性
|
||||
- 产出物:QA 验收报告 + 对账证据截图/日志
|
||||
|
||||
### XL(TechLead / 运维)
|
||||
- 确认 tksea 环境可达性(43.155.133.187:8081)
|
||||
- 若 tksea 使用 in-memory 模式,确认验证期间不重启进程
|
||||
- 若需长期保留 G4 证据,建议将 tksea 切换为 postgres 后端后再执行验证
|
||||
- 产出物:环境确认签字 + 执行窗口协调
|
||||
|
||||
---
|
||||
|
||||
## 自检清单
|
||||
|
||||
- [x] 已读取关键代码并理解现有事件流
|
||||
- [x] 接口定义完整(请求/响应/错误)
|
||||
- [x] G4 验证方案可执行、可验证
|
||||
- [x] 每个任务 < 5分钟,有明确文件路径
|
||||
- [x] 风险评估完整
|
||||
- [x] 已明确标记是否可进入下一阶段
|
||||
- [x] 已给出 Engineer / QA / XL 的下游执行约束摘要
|
||||
262
tech/G4_REMOTE_GATEWAY_INTEGRATION_PRD_2026-05-10.md
Normal file
262
tech/G4_REMOTE_GATEWAY_INTEGRATION_PRD_2026-05-10.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# G4 真实远端 Gateway 集成验证 PRD
|
||||
|
||||
文档版本:v1.0
|
||||
日期:2026-05-10
|
||||
作者:PM(生产门禁收口)
|
||||
状态:待 TechLead 评审
|
||||
|
||||
---
|
||||
|
||||
## 1. 概述
|
||||
|
||||
Supply-Intelligence 的 G1(smoke 主链)、G2(inspect/metrics)、G3(rollback 演练)已在本地与 tksea 服务器完成。当前生产门禁为 `REQUEST_CHANGES`,唯一阻断项是 G4:真实远端 gateway 集成验证。
|
||||
|
||||
G4 不是新增功能,而是对已有 gateway publish / consume / ack 链路在共享预发环境中的端到端实证要求。supply-intelligence 作为事件生产者,sub2api / tokens-reef 作为下游消费者,必须在共享环境中留下可复核的双侧对账记录。
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标
|
||||
|
||||
在共享预发环境中完成一次闭环验证,证明:
|
||||
|
||||
1. supply-intelligence 产生的 `package_change_event` 能被远端系统(sub2api / tokens-reef)真实消费。
|
||||
2. 消费的 EVENT_ID 在 supply-intelligence 侧与远端侧均可被独立查询,且状态一致。
|
||||
3. 远端消费失败时,supply-intelligence 侧不会误标为 applied,而是进入 retry 或保持 pending。
|
||||
4. 验证过程可复现、可脚本化、可归档为 QA 复核证据。
|
||||
|
||||
---
|
||||
|
||||
## 3. 范围
|
||||
|
||||
### 3.1 In Scope
|
||||
|
||||
- supply-intelligence 共享预发环境(当前为 tksea 43.155.133.187:8081)的事件 publish 与 consume-once API。
|
||||
- sub2api / tokens-reef 作为远端 consumer 对 consume-once 的调用及后续处理。
|
||||
- 双侧 EVENT_ID 对账机制的定义与验证脚本。
|
||||
- 共享环境中 gateway runtime 的暂停/恢复操作(避免与远端 consumer 竞争单 ack 事件)。
|
||||
- G4 验证证据包的格式、归档位置与 QA 复核流程。
|
||||
|
||||
### 3.2 Out of Scope
|
||||
|
||||
- supply-intelligence 核心 publish / consume / ack 业务逻辑的代码级改造(主链路已在 G1-G3 验证通过)。
|
||||
- sub2api / tokens-reef 内部业务规则的深度改造(如 token 配额算法、模型路由策略)。
|
||||
- 多 consumer 独立 ack schema 的长期重构(已知当前为单 ack 设计,G4 通过操作规程规避竞争)。
|
||||
- 非 gateway 链路(probe、discovery、admission)的额外验证。
|
||||
|
||||
### 3.3 假设与依赖
|
||||
|
||||
- 假设 sub2api / tokens-reef 在共享环境中已可运行(tksea 8080 端口已确认运行)。
|
||||
- 假设 sub2api 侧至少能提供一张持久化表或一个查询接口,记录从 supply-intelligence 消费的事件及其处理结果。
|
||||
- 假设 supply-intelligence 与 sub2api 在共享环境中网络可达(同服务器已满足)。
|
||||
- 依赖 sub2api 侧负责人提供消费端的最小实现或已有 bridge 的扩展方案。
|
||||
- 依赖 TechLead 在 G4 验证前确认单 ack schema 的临时操作规程(暂停 gateway runtime)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 用户场景
|
||||
|
||||
### 4.1 主流程:共享环境端到端对账
|
||||
|
||||
1. **前置**:执行人调用 `POST /gateway/runtime/pause` 暂停 supply-intelligence 内置 gateway runtime。
|
||||
2. **Publish**:执行人调用 `POST /publish/package-event` 产生一个真实 EVENT_ID。
|
||||
3. **远端消费**:sub2api 以 consumer=`sub2api`(或已有 consumer 名称)调用 `POST /gateway/consume-once` 拉取事件。
|
||||
4. **远端处理**:sub2api 将事件应用到自身系统(更新模型列表、路由规则或至少写入持久化消费记录表),并在本地记录 processing_result。
|
||||
5. **Ack**:supply-intelligence 的 consume-once 内部自动将事件 ack 为 applied(若 sub2api 调用成功)或 failed(若处理返回失败)。
|
||||
6. **双侧对账**:执行人运行对账脚本,输入 EVENT_ID,查询 supply-intelligence 的 `package-changes` / `admission-state` 与 sub2api 侧的持久化记录,比对 event_id、package_id、status、consumer、timestamp。
|
||||
7. **恢复**:执行人调用 `POST /gateway/runtime/resume` 恢复 gateway runtime。
|
||||
8. **归档**:执行人保存命令、stdout、关键 JSON 片段到证据包目录。
|
||||
|
||||
### 4.2 异常流:远端消费失败
|
||||
|
||||
1. sub2api 调用 consume-once 成功获取事件,但在后续处理时抛出业务错误(如模型不存在、数据库冲突)。
|
||||
2. sub2api 不向 supply-intelligence 发送额外 ack(consume-once 已完成 ack)。
|
||||
3. 如果 sub2api 需要标记失败,当前单 ack schema 下 consume-once 已返回 applied/ failed。因此 TechLead 必须选择以下策略之一:
|
||||
- **策略 A**:sub2api 在本地记录失败,对账时以 sub2api 本地记录为准;supply-intelligence 侧状态视为传输层 ack。
|
||||
- **策略 B**:改造 consume-once 调用方式,使 sub2api 先读取事件但不自动 ack,处理成功后再显式调用 `POST /gateway/package-changes/{event_id}/ack`。
|
||||
4. 无论采用哪种策略,QA 必须能在对账脚本中明确区分"supply-intelligence 侧状态"与"sub2api 侧真实处理结果"。
|
||||
|
||||
### 4.3 边缘流:gateway runtime 未暂停导致事件被抢走
|
||||
|
||||
1. 若执行人未暂停 gateway runtime,内置 consumer 会在 1 秒内自动消费并 ack 新 publish 的事件。
|
||||
2. sub2api 再次调用 consume-once 时,该事件状态已为 applied,items 列表中不再包含此事件。
|
||||
3. 对账脚本检测到 sub2api 侧无此 EVENT_ID 记录,判定为 mismatch。
|
||||
4. **处置**:本场景作为 G4 验证的负向测试用例,用于证明单 ack schema 的竞争风险真实存在;正式 G4 验证必须通过 pause runtime 规避。
|
||||
|
||||
### 4.4 边缘流:重复 publish
|
||||
|
||||
1. 同一 EVENT_ID 被重复 publish 时,supply-intelligence 返回 HTTP 409(`duplicate_publish_request` 或 `publish_already_applied`)。
|
||||
2. 远端 consumer 不应收到重复事件。对账脚本验证 sub2api 侧同一 EVENT_ID 仅出现一次。
|
||||
|
||||
### 4.5 边缘流:unauthorized consumer
|
||||
|
||||
1. sub2api 使用的 consumer 名称若未关联目标事件的 account_id,则 `isAuthorizedForEvent` 返回 false。
|
||||
2. consume-once 的 items 列表中不包含该 unauthorized 事件。
|
||||
3. 事件在 supply-intelligence 侧保持 pending,不会被错误消费。
|
||||
|
||||
---
|
||||
|
||||
## 5. 验收标准(AC)
|
||||
|
||||
每条 AC 必须可被 QA 或自动化脚本在共享环境中执行,并给出二元判定(通过 / 不通过)。
|
||||
|
||||
**AC1:远端 consumer 可达性**
|
||||
- 判定方法:从 sub2api 所在主机执行 `curl -fsS -X POST "${SUPPLY_URL}/internal/supply-intelligence/gateway/consume-once?consumer=sub2api"`,HTTP 状态码必须为 200,响应 JSON 必须包含 `consumer` 和 `items` 字段。
|
||||
- 通过标准:HTTP 200 且 JSON schema 符合 `ConsumeOnceOutput` 定义。
|
||||
|
||||
**AC2:真实事件被远端消费**
|
||||
- 判定方法:在 supply-intelligence 侧执行 publish 产生 EVENT_ID `evt-g4-{timestamp}`;随后从 sub2api 侧调用 consume-once;检查 sub2api 侧持久化存储中是否存在该 EVENT_ID 的记录。
|
||||
- 通过标准:sub2api 侧数据库表或审计日志中至少存在一条记录,其 `event_id` 字段等于 `evt-g4-{timestamp}`。
|
||||
|
||||
**AC3:supply-intelligence 侧事件终态正确**
|
||||
- 判定方法:在 AC2 完成后,调用 `GET /internal/supply-intelligence/gateway/package-changes` 与 `GET /internal/supply-intelligence/models/{platform}/{model}/admission-state`,检查该 EVENT_ID 的 `gateway_sync_status`。
|
||||
- 通过标准:对于成功的远端消费,`gateway_sync_status` 为 `applied`;对于明确失败的远端消费,`gateway_sync_status` 为 `failed`。不允许为 `pending`。
|
||||
|
||||
**AC4:双侧状态可对账**
|
||||
- 判定方法:执行对账脚本(待 TechLead 提供,路径建议 `scripts/g4_reconcile.sh`),输入 EVENT_ID,脚本分别查询 supply-intelligence 与 sub2api 两侧。
|
||||
- 通过标准:脚本输出 JSON 必须包含 `match=true`,且两侧 `event_id`、`package_id`、`status`(或 processing_result)一致;脚本执行时间不得超过 60 秒。
|
||||
|
||||
**AC5:远端消费失败时的状态隔离**
|
||||
- 判定方法:制造一个远端处理失败的场景(例如 sub2api 消费后记录 processing_result=failed,或在 consume-once 前模拟 sub2api 内部错误);检查 supply-intelligence 侧事件状态与 sub2api 侧记录。
|
||||
- 通过标准:若采用策略 A(传输层 ack),supply-intelligence 侧可为 applied,但 sub2api 侧必须记录 processing_result=failed,对账脚本输出 `match=false` 并标注原因;若采用策略 B(显式 ack),supply-intelligence 侧必须为 failed。不允许出现"supply-intelligence 侧 applied 且 sub2api 侧无记录"的幽灵状态。
|
||||
|
||||
**AC6:gateway runtime 暂停不影响 API 可用性**
|
||||
- 判定方法:在 gateway runtime 暂停期间(`paused=true`),重复执行 AC1 的 consume-once 调用,同时检查 `healthz` 与 `runtime-status`。
|
||||
- 通过标准:consume-once API 返回 200;`healthz` 返回 ok;`runtime-status` 返回 `paused=true`;gateway runtime 恢复后 `paused=false`。
|
||||
|
||||
**AC7:完整闭环证据归档**
|
||||
- 判定方法:执行人在共享环境中完成 AC1-AC6 后,将产物写入 `reports/production/evidence-g4-{date}/` 目录。
|
||||
- 通过标准:目录中必须包含以下文件,且时间戳在 24 小时内:
|
||||
- `00_preflight.json`(healthz + runtime-status 演练前)
|
||||
- `01_publish.json`(publish 响应)
|
||||
- `02_consume_once.json`(sub2api 侧调用 consume-once 的响应)
|
||||
- `03_sub2api_record.sql` 或 `.json`(sub2api 侧持久化记录查询结果)
|
||||
- `04_reconcile.json`(对账脚本输出)
|
||||
- `05_runtime_after_resume.json`(恢复后的 runtime-status)
|
||||
|
||||
---
|
||||
|
||||
## 6. 边缘情况与失败路径
|
||||
|
||||
| 场景 | 预期行为 | 验证方式 |
|
||||
|------|---------|---------|
|
||||
| gateway runtime 未暂停,事件被内置 consumer 抢走 | sub2api consume-once 返回空 items;对账 mismatch | AC4 负向测试 |
|
||||
| sub2api 调用 consume-once 时 supply-intelligence 宕机 | sub2api 收到 HTTP 5xx 或连接超时;事件保持 pending | 检查 supply-intelligence 重启后事件状态仍为 pending |
|
||||
| sub2api 消费后宕机,未写入本地记录 | 对账时 sub2api 侧 not_found;supply-intelligence 侧可能已 applied | AC5 明确失败策略 |
|
||||
| 重复调用 consume-once 同一 cursor | 返回空 items 或 next_cursor 为空;无重复 ack | AC4 验证 sub2api 侧无重复记录 |
|
||||
| 使用未授权的 consumer 名称 | consume-once 不返回该账号事件;事件保持 pending | 负向测试:publish 后换 consumer 名称调用,验证 items 为空 |
|
||||
| 网络分区导致 consume-once 超时 | sub2api 侧重试;supply-intelligence 侧事件状态不变 | 模拟超时后重试,验证事件未被错误 ack |
|
||||
|
||||
---
|
||||
|
||||
## 7. 上线与运营准备
|
||||
|
||||
### 7.1 共享环境配置清单
|
||||
|
||||
- [ ] supply-intelligence 在 tksea 的 BASE_URL 已确认(当前 43.155.133.187:8081)。
|
||||
- [ ] sub2api / tokens-reef 在 tksea 的地址与数据库连接串已确认(当前 8080 端口,PostgreSQL 本地)。
|
||||
- [ ] sub2api 侧 consumer 名称已确定(建议 `sub2api` 或沿用 `sub2api-bridge`)。
|
||||
- [ ] sub2api 侧持久化表已创建(至少含 event_id, package_id, status, consumed_at, processing_result 字段)。
|
||||
- [ ] supply-intelligence 侧 gateway runtime 可在验证前被手动暂停。
|
||||
|
||||
### 7.2 对账脚本
|
||||
|
||||
- TechLead 需提供 `scripts/g4_reconcile.sh`,输入 EVENT_ID 与两侧 BASE_URL,输出 JSON 对账结果。
|
||||
- 脚本必须返回明确 exit code:0(match)、1(mismatch)、2(not_found / 查询失败)。
|
||||
|
||||
### 7.3 监控与告警
|
||||
|
||||
- G4 验证期间,共享环境必须保持 `/metrics` 可访问。
|
||||
- 对账脚本执行后,必须记录 `supply_intelligence_gateway_events_processed_total` 与 `supply_intelligence_gateway_failed_events` 的采样值。
|
||||
- 若 G4 验证重复执行超过 3 次仍 mismatch,值班人员必须通知 TechLead 排查,禁止强行修改数据通过门禁。
|
||||
|
||||
### 7.4 回滚预案
|
||||
|
||||
- 若 G4 验证导致 sub2api 侧数据异常,sub2api 侧负责人应使用自身系统的回滚机制恢复。
|
||||
- supply-intelligence 侧可通过 `gateway/runtime/pause` 停止事件下发,已 ack 的事件不可回滚(事件日志性质)。
|
||||
- 若需要撤销已 publish 的 package,使用 supply-intelligence 的 publish 替换机制(发布新 package-event),而非删除历史 event。
|
||||
|
||||
### 7.5 值班 runbook
|
||||
|
||||
1. 执行 G4 前,确认 `runtime-status` 中 `started=true`,然后执行 `runtime/pause`。
|
||||
2. 执行 publish,记录返回的 EVENT_ID。
|
||||
3. 等待 sub2api 侧执行 consume-once(或手动触发)。
|
||||
4. 运行 `g4_reconcile.sh`。
|
||||
5. 若 match=true,执行 `runtime/resume`,归档证据包。
|
||||
6. 若 match=false,保持 paused 状态,通知 TechLead 与 sub2api 侧负责人,排查后重新执行。
|
||||
|
||||
---
|
||||
|
||||
## 8. 依赖与风险
|
||||
|
||||
| 依赖项 | 状态 | 风险描述 | 缓解措施 |
|
||||
|--------|------|---------|---------|
|
||||
| sub2api 侧 consumer 实现 | 缺失 | sub2api 当前未配置 supply-intelligence 集成,无持久化消费记录 | sub2api 侧负责人需在 G4 前完成最小消费记录表与查询接口 |
|
||||
| 单 ack schema | 已知限制 | 同一时间只能有一个 consumer ack 事件;gateway runtime 会与 sub2api 抢事件 | G4 验证期间通过 `runtime/pause` 规避;长期需 TechLead 评估多 consumer schema 改造 |
|
||||
| 网络稳定性 | 中风险 | tksea 同服务器网络应稳定,但跨容器/进程调用仍可能失败 | 对账脚本增加重试与超时;失败时标记为 not_found 而非误报 match |
|
||||
| 证据包人工操作 | 中风险 | 执行人可能遗漏归档步骤或时间戳不一致 | 对账脚本自动将结果写入文件;QA 复核时检查文件存在性与时间戳 |
|
||||
| sub2api 业务逻辑不可用 | 低风险 | 若 sub2api 内部业务系统暂无法处理 package change,bridge 只能写日志 | PRD 接受"持久化消费记录表"作为最低证据,不要求立即触发完整业务闭环 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 阶段门控结论
|
||||
|
||||
### 9.1 当前信息是否足够进入 TechLead 设计阶段?
|
||||
|
||||
**结论:足够。**
|
||||
|
||||
依据:
|
||||
1. G4 缺口已被精确识别,不是模糊的"缺集成",而是"缺远端 consumer 消费 + 双侧对账证据"。
|
||||
2. supply-intelligence 侧的 API(publish、consume-once、package-changes、admission-state、runtime pause/resume)已经存在且经 G1-G3 验证稳定。
|
||||
3. sub2api-bridge 已提供技术方向参考(pull 模式、写日志表),TechLead 只需在此基础上扩展为持久化记录 + 查询接口。
|
||||
4. 单 ack schema 的限制已被识别,并有明确的临时操作规程(pause runtime)。
|
||||
5. 所有验收标准均已量化(HTTP 200、60 秒、match=true/false、特定 JSON 字段)。
|
||||
|
||||
### 9.2 TechLead 必须产出的设计决策
|
||||
|
||||
1. **策略选择**:采用策略 A(传输层 ack + sub2api 本地记录 processing_result)还是策略 B(显式 ack 接口)?
|
||||
2. **sub2api 侧最小实现**:确定 consumer 名称、持久化表 schema、查询接口路径。
|
||||
3. **对账脚本**:`scripts/g4_reconcile.sh` 的实现(语言、两侧查询方式、输出 schema)。
|
||||
4. **多 consumer 长期方案**:是否在 G4 之后启动多 consumer 独立 ack schema 的改造?(当前 G4 不要求改造)。
|
||||
|
||||
### 9.3 QA 可提前准备的内容
|
||||
|
||||
1. 基于本 PRD 的 AC 编写自动化测试用例框架(即使 sub2api 侧尚未 ready,也可 mock 远端查询接口)。
|
||||
2. 审核证据包目录结构与命名规范。
|
||||
3. 准备负向测试用例(unauthorized consumer、重复 publish、runtime 未暂停)。
|
||||
|
||||
---
|
||||
|
||||
## 10. 下游关注点摘要
|
||||
|
||||
### 10.1 给 TechLead
|
||||
|
||||
- **核心决策**:G4 只需要证明"远端真实消费",不需要一次性完成完美的双向 ack。请尽快确认策略 A 或 B,以便 QA 编写对账脚本。
|
||||
- **已知债务**:`CountRetryablePendingPackageEvents` 与 `ListRetryablePendingPackageEvents` 当前忽略 consumer 参数(QA 报告 4.1)。G4 使用单 consumer 验证,暂不触发该债务,但请记录到后续迭代 backlog。
|
||||
- **实现量评估**:sub2api 侧最小改造量约为:创建一张消费记录表 + 一个查询接口 + 扩展 bridge 逻辑。若已有 sub2api-bridge,改造量预计在 1-2 人日。
|
||||
|
||||
### 10.2 给 QA
|
||||
|
||||
- **测试重点**:不要只验证"consume-once 返回 200",必须验证 EVENT_ID 在 sub2api 侧有持久化记录。
|
||||
- **负向用例**:务必执行"runtime 未暂停"场景,证明单 ack 竞争真实存在,且 pause 是 G4 的必要前置步骤。
|
||||
- **证据完整性**:严格按照 AC7 的 6 个文件清单审核证据包,缺少任一文件即判定 G4 不通过。
|
||||
|
||||
### 10.3 给 XL(执行/运维)
|
||||
|
||||
- **执行顺序**:必须先 pause → publish → 等待 sub2api 消费 → 对账 → resume。任何跳过 pause 的执行均视为无效证据。
|
||||
- **环境保真**:G4 验证期间,tksea 上的 supply-intelligence 与 sub2api 配置不得被其他测试干扰。建议预约独占窗口。
|
||||
- **产物路径**:证据包统一存放于 `reports/production/evidence-g4-YYYY-MM-DD/`,由 QA 复核后合并到 `SHARED_ENV_EVIDENCE_RUN_YYYY-MM-DD.md`。
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:自检清单
|
||||
|
||||
返回本 PRD 时,以下条目已逐项确认:
|
||||
|
||||
- [x] 已明确真实目标,不是只复述功能
|
||||
- [x] 已写清 In Scope / Out of Scope
|
||||
- [x] 每个 AC 都可被 QA 或测试用例直接验证
|
||||
- [x] 已覆盖异常流、边缘流与失败路径
|
||||
- [x] 已补齐上线、运营、监控、回滚要求
|
||||
- [x] 已明确当前是否可进入 TechLead 阶段
|
||||
- [x] 已给出 TechLead / QA / XL 的下游关注点摘要
|
||||
- [x] 没有使用"优化、支持、友好、尽量、快速"等模糊词替代明确要求
|
||||
158
tech/GRAYSCALE_ROLLOUT_PLAN_2026-05-10.md
Normal file
158
tech/GRAYSCALE_ROLLOUT_PLAN_2026-05-10.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# Supply-Intelligence 灰度放量执行计划(2026-05-10)
|
||||
|
||||
状态:待执行
|
||||
仓库:`/home/long/project/supply-intelligence`
|
||||
前提:QA 报告 CONDITIONAL_APPROVED,上线前检查清单已通过
|
||||
|
||||
---
|
||||
|
||||
## 0. 灰度策略总览
|
||||
|
||||
supply-intelligence 采用 **account 级灰度**,通过控制 `AccountRoutingState.RoutingEnabled` 和 `SupplyAccount.ConsumerTag` 实现逐步放量。
|
||||
|
||||
灰度阶段:
|
||||
1. 影子运行(0% account,只验证服务存活)
|
||||
2. 单 account 验证(1 个测试 account)
|
||||
3. 小批量放量(10% active accounts)
|
||||
4. 半量放量(50% active accounts)
|
||||
5. 全量放行(100% active accounts)
|
||||
|
||||
---
|
||||
|
||||
## 1. 影子运行(Shadow / 0% Account)
|
||||
|
||||
目标:验证服务部署后无 panic、无异常日志、metrics 正常。
|
||||
|
||||
执行步骤:
|
||||
```bash
|
||||
# 1. 部署到目标环境(并入 supply-api 主仓或独立实例)
|
||||
# 2. 不启用任何 account 的 routing_enabled
|
||||
# 3. 仅执行健康检查和 metrics 抓取
|
||||
|
||||
curl -fsS http://<BASE_URL>/healthz
|
||||
curl -fsS http://<BASE_URL>/metrics | grep supply_intelligence_
|
||||
```
|
||||
|
||||
观察窗口:5 分钟
|
||||
通过标准:
|
||||
- healthz 返回 200
|
||||
- metrics 正常暴露无 panic
|
||||
- 无 ERROR/FATAL 日志
|
||||
|
||||
---
|
||||
|
||||
## 2. 单 Account 验证(1 Account)
|
||||
|
||||
目标:验证完整业务链路在真实环境下可行。
|
||||
|
||||
执行步骤:
|
||||
```bash
|
||||
# 1. 选择一个测试 account(建议非生产关键 account)
|
||||
# 2. 插入 test-passed candidate + draft package
|
||||
# 3. 执行完整链路
|
||||
|
||||
BASE_URL="<BASE_URL>" PLATFORM="openai" MODEL="<test-model>" EVENT_ID="evt-gray-1" \
|
||||
bash scripts/gateway_closure_smoke.sh
|
||||
```
|
||||
|
||||
验证要点:
|
||||
- publish 返回 candidate=published, package=active
|
||||
- consume-once 返回 event=applied
|
||||
- admission-state 返回 gateway_sync_status=applied
|
||||
- inspect 返回 decision=continue
|
||||
|
||||
观察窗口:10 分钟
|
||||
通过标准:链路完整闭环,无 failed 事件。
|
||||
|
||||
---
|
||||
|
||||
## 3. 小批量放量(10% Active Accounts)
|
||||
|
||||
目标:验证多 account 并发下无异常。
|
||||
|
||||
执行步骤:
|
||||
```bash
|
||||
# 1. 选取 10% 的 active accounts,设置 routing_enabled=true
|
||||
# 2. 观察 10 分钟
|
||||
# 3. 执行 inspect 脚本,确认指标正常
|
||||
|
||||
BASE_URL="<BASE_URL>" CONSUMER="gateway" bash scripts/gateway_closure_inspect.sh
|
||||
```
|
||||
|
||||
关键指标:
|
||||
- `gateway_events_processed_total` 增长与 publish 频率匹配
|
||||
- `gateway_event_latency_seconds` P99 < 1s
|
||||
- `gateway_pending_retry_events` < 5
|
||||
- `gateway_failed_events` = 0
|
||||
|
||||
观察窗口:10 分钟
|
||||
通过标准:所有关键指标在基线范围内。
|
||||
|
||||
---
|
||||
|
||||
## 4. 半量放量(50% Active Accounts)
|
||||
|
||||
目标:验证中等负载下稳定性。
|
||||
|
||||
执行步骤:
|
||||
- 逐步放开至 50% active accounts
|
||||
- 每批放量后执行 inspect
|
||||
- 观察 latency 和 error rate
|
||||
|
||||
关键指标:
|
||||
- 同上,但 latency P99 容忍度放宽至 < 2s
|
||||
|
||||
观察窗口:30 分钟
|
||||
通过标准:无告警触发,inspect 决策为 continue。
|
||||
|
||||
---
|
||||
|
||||
## 5. 全量放行(100% Active Accounts)
|
||||
|
||||
目标:所有 active accounts 启用 supply-intelligence 路由。
|
||||
|
||||
执行步骤:
|
||||
- 放开全部 active accounts
|
||||
- 启动 24h/72h/首周巡检(见 `PRODUCTION_OBSERVABILITY_CHECKLIST`)
|
||||
|
||||
---
|
||||
|
||||
## 6. 止损条件(任意阶段触发即回滚)
|
||||
|
||||
| 条件 | 触发值 | 动作 |
|
||||
|------|--------|------|
|
||||
| healthz 连续失败 | 3 次 | 立即 pause runtime |
|
||||
| gateway 失败率 | > 10% | 执行 rollback 脚本 |
|
||||
| pending retry 积压 | > 50 | 暂停放量,排查 consumer |
|
||||
| latency P99 | > 5s | 降级至上一阶段比例 |
|
||||
| panic / fatal 日志 | > 0 | 全量回滚 |
|
||||
|
||||
回滚命令:
|
||||
```bash
|
||||
curl -X POST "<BASE_URL>/internal/supply-intelligence/gateway/runtime/pause"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 执行决策点
|
||||
|
||||
需要确认:
|
||||
1. **部署目标**:并入 supply-api 主仓 / tksea 独立实例 / 其他环境
|
||||
2. **BASE_URL**:灰度环境的实际访问地址
|
||||
3. **测试 account**:单 account 验证时使用的 account ID
|
||||
4. **放量节奏**:每阶段观察窗口时长(默认按本计划)
|
||||
5. **值班人**:各阶段执行人和紧急联系人
|
||||
|
||||
---
|
||||
|
||||
## 8. 本地预验证已完成项
|
||||
|
||||
| 阶段 | 状态 | 证据 |
|
||||
|------|------|------|
|
||||
| 影子运行 | ✅ | healthz=200, metrics 正常 |
|
||||
| 单 account | ✅ | smoke 脚本通过,decision=continue |
|
||||
| 回滚脚本 | ✅ | rollback.sh 语法通过,pause/resume API 可用 |
|
||||
|
||||
---
|
||||
|
||||
版本:v1.0 | 创建:2026-05-10
|
||||
180
tech/PRODUCTION_LAUNCH_CLOSURE_BOARD_2026-05-07.md
Normal file
180
tech/PRODUCTION_LAUNCH_CLOSURE_BOARD_2026-05-07.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# Supply-Intelligence 生产上线收敛任务板(2026-05-07)
|
||||
|
||||
> 状态:当前有效
|
||||
> 目标:把 supply-intelligence 从“最小闭环骨架”推进到“可生产上线判定”
|
||||
> 仓库:`/home/long/project/立交桥/projects/supply-intelligence`
|
||||
> 事实基线:本地 `go test ./...` 通过;当前分支 `main`;最新提交 `afdbea6 feat: bootstrap supply intelligence baseline`
|
||||
|
||||
## 0. 当前门控结论
|
||||
|
||||
当前结论:REQUEST_CHANGES
|
||||
|
||||
原因不是项目不可运行,而是“可运行骨架”与“真源要求的生产闭环”仍存在关键差距,不能宣称可上线。
|
||||
|
||||
## 1. 事实基线
|
||||
|
||||
### 1.1 已验证事实
|
||||
- 仓库存在真实代码、测试、迁移、文档:`.git`、`go.mod`、`internal/`、`migrations/`、`tech/`
|
||||
- 本地执行 `cd '/home/long/project/立交桥/projects/supply-intelligence' && go test ./...` 通过
|
||||
- 已存在模块:`probe`、`discovery`、`admission`、`publish`、`gatewayconsumer`、`httpapi`、`repository`
|
||||
- 已存在 HTTP 路由:
|
||||
- `/internal/supply-intelligence/accounts/{account_id}/routing-state`
|
||||
- `/internal/supply-intelligence/discovery/candidates`
|
||||
- `/internal/supply-intelligence/admission/run`
|
||||
- `/internal/supply-intelligence/gateway/package-changes`
|
||||
- `/internal/supply-intelligence/gateway/package-changes/{event_id}/ack`
|
||||
- `/internal/supply-intelligence/gateway/consume-once`
|
||||
|
||||
### 1.2 已确认关键差距
|
||||
- `internal/domain/types.go` 仍保留旧 candidate 状态:`pending_admission`、`admitted`
|
||||
- `internal/httpapi/server.go` 的状态解析仍接受旧状态
|
||||
- `internal/probe/state_machine.go` 仍是 `suspended + explicit_failure -> disabled` 的单步逻辑,未体现“3 次连续 explicit failure 才 disabled”
|
||||
- `internal/publish/service.go` 已完成基础 publish event 持久化与 pending 状态写入,但仍未覆盖 `draft -> active` 与 `candidate test_passed -> published` 的完整事务联动
|
||||
- `GET /internal/supply-intelligence/models/{platform}/{model}/admission-state` 未接入真实入口
|
||||
- gateway consumer 已有最小 poll/apply/ack 骨架,但仍需补足生产门禁证据与发布状态联动
|
||||
|
||||
### 1.3 事实更新(2026-05-07 复核)
|
||||
- 本地执行 `cd '/home/long/project/立交桥/projects/supply-intelligence' && go test ./...` 通过
|
||||
- 代码中已存在 publish/service 与 repository 的事件落库、ack、gateway snapshot 基础路径
|
||||
- 当前首个阻塞不再是“publish 事件未持久化”,而是“发布事务与 admission-state / 状态机联动未收口”
|
||||
- 因此首个阻塞项应下沉为 B2/B3/B4 的联动闭环,而不是单纯 event append
|
||||
|
||||
## 2. 最短闭环路径
|
||||
|
||||
1. 先修 Phase A:probe/account 状态机与 routing-state 真正符合真源
|
||||
2. 再修 Phase B/C:candidate 状态机与 admission/draft 闭环一致
|
||||
3. 再修 Phase D:真实发布事务 + admission-state API + gateway sync 联动
|
||||
4. 再做全链路 QA 复核与上线证据收敛
|
||||
|
||||
## 3. 任务板
|
||||
|
||||
## A. Design
|
||||
|
||||
### A1. 收敛状态机真源到代码级约束
|
||||
- Owner:TechLead
|
||||
- 交付物:状态机收敛设计说明
|
||||
- 范围:
|
||||
- probe 账号状态迁移规则
|
||||
- candidate 生命周期合法状态与迁移
|
||||
- publish/gateway_sync 的语义边界
|
||||
- 完成标准:
|
||||
- 明确删除 `pending_admission` / `admitted`
|
||||
- 明确 `published != applied`
|
||||
- 明确 `suspended -> disabled` 的窗口规则
|
||||
- 验证方式:设计文档与现有代码差异清单完整
|
||||
- 依赖:无
|
||||
- 状态:pending
|
||||
|
||||
### A2. 定义发布事务与 admission-state 读取契约
|
||||
- Owner:TechLead
|
||||
- 交付物:发布事务与 `/models/{platform}/{model}/admission-state` 契约说明
|
||||
- 完成标准:
|
||||
- 明确 package、candidate、gateway_sync 三者联动字段
|
||||
- 明确 handler / service / repository 落点
|
||||
- 验证方式:文件级任务拆解完成
|
||||
- 依赖:A1
|
||||
- 状态:pending
|
||||
|
||||
## B. Implementation
|
||||
|
||||
### B1. 修复 probe 状态机实现
|
||||
- Owner:Engineer
|
||||
- 交付物:`internal/probe/*`、`internal/domain/*`、相关 repo/test 修正
|
||||
- 完成标准:
|
||||
- inconclusive 不触发惩罚性迁移
|
||||
- disabled 只在满足真源规则时发生
|
||||
- 补齐主路径与失败路径测试
|
||||
- 验证方式:`go test ./internal/probe ./internal/app ./internal/httpapi`
|
||||
- 依赖:A1
|
||||
- 状态:pending
|
||||
|
||||
### B2. 清理 candidate 旧状态并对齐 admission 流转
|
||||
- Owner:Engineer
|
||||
- 交付物:`internal/domain/types.go`、`internal/discovery/*`、`internal/admission/*`、`internal/httpapi/server.go`、相关测试
|
||||
- 完成标准:
|
||||
- 删除 `pending_admission` / `admitted`
|
||||
- `discovered/testing/test_passed/test_failed/retry_pending/ignored/published/deprecated/closed` 全链路一致
|
||||
- discovery / admission / HTTP 参数校验统一
|
||||
- 验证方式:`go test ./internal/discovery ./internal/admission ./internal/httpapi`
|
||||
- 依赖:A1
|
||||
- 状态:pending
|
||||
|
||||
### B3. 实现真实 publish 事务
|
||||
- Owner:Engineer
|
||||
- 交付物:`internal/publish/*`、`internal/repository/*`、`internal/app/*`、相关测试
|
||||
- 完成标准:
|
||||
- draft -> active
|
||||
- candidate `test_passed -> published`
|
||||
- event append 作为发布事务的一部分,不再只是独立记录器
|
||||
- 验证方式:`go test ./internal/publish ./internal/app ./internal/repository`
|
||||
- 依赖:A2
|
||||
- 状态:pending
|
||||
|
||||
### B4. 接入 admission-state API
|
||||
- Owner:Engineer
|
||||
- 交付物:`internal/httpapi/server.go`、`internal/repository/*`、相关测试
|
||||
- 完成标准:
|
||||
- 存在真实读取入口 `/internal/supply-intelligence/models/{platform}/{model}/admission-state`
|
||||
- 返回 candidate/package/gateway_sync 组合态
|
||||
- 验证方式:`go test ./internal/httpapi ./internal/repository`
|
||||
- 依赖:A2, B2, B3
|
||||
- 状态:pending
|
||||
|
||||
## C. Verification
|
||||
|
||||
### C1. QA 复核 probe/account 主链路
|
||||
- Owner:QA
|
||||
- 交付物:结构化审查报告
|
||||
- 完成标准:
|
||||
- 验证 definition -> assembly -> call -> entry
|
||||
- 验证状态机与真源一致
|
||||
- 验证方式:代码抽检 + 运行 targeted tests
|
||||
- 依赖:B1
|
||||
- 状态:pending
|
||||
|
||||
### C2. QA 复核 candidate/admission/publish 主链路
|
||||
- Owner:QA
|
||||
- 交付物:结构化审查报告
|
||||
- 完成标准:
|
||||
- 验证 candidate 状态无旧口径残留
|
||||
- 验证 publish 事务不是“只写 event”
|
||||
- 验证 `published != applied`
|
||||
- 验证方式:代码抽检 + 运行 targeted tests
|
||||
- 依赖:B2, B3, B4
|
||||
- 状态:pending
|
||||
|
||||
### C3. 端到端最小闭环验证
|
||||
- Owner:QA
|
||||
- 交付物:最小闭环验证记录
|
||||
- 完成标准:
|
||||
- candidate -> test_passed -> publish -> package-changes -> ack
|
||||
- admission-state 可反映 pending/applied/failed
|
||||
- 验证方式:`go test ./...` + 必要的集成命令/测试
|
||||
- 依赖:C2
|
||||
- 状态:pending
|
||||
|
||||
## D. Release Evidence
|
||||
|
||||
### D1. 上线证据包整理
|
||||
- Owner:XL
|
||||
- 交付物:上线前结论摘要
|
||||
- 完成标准:
|
||||
- 列清已完成范围
|
||||
- 列清剩余非阻塞项
|
||||
- 列清不可宣称项
|
||||
- 验证方式:对照 QA 结果与最新测试输出
|
||||
- 依赖:C1, C2, C3
|
||||
- 状态:pending
|
||||
|
||||
## 4. 明确禁止的错误结论
|
||||
- 不得把 `go test ./...` 通过等同于“可生产上线”
|
||||
- 不得把 `published` 等同于 `gateway applied`
|
||||
- 不得把仅存在 handler/route 等同于真实主链路完成
|
||||
- 不得把 event append 记录器等同于真实发布事务
|
||||
|
||||
## 5. 当前推荐执行顺序
|
||||
1. TechLead 先出状态机/发布事务收敛设计
|
||||
2. Engineer 先做 B1 + B2
|
||||
3. Engineer 再做 B3 + B4
|
||||
4. QA 做 C1/C2/C3
|
||||
5. XL 汇总 D1 并给出“可上线/不可上线”结论
|
||||
167
tech/PRODUCTION_LAUNCH_CLOSURE_BOARD_2026-05-08.md
Normal file
167
tech/PRODUCTION_LAUNCH_CLOSURE_BOARD_2026-05-08.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# Supply-Intelligence 生产上线收口执行板(2026-05-08)
|
||||
|
||||
状态:当前有效
|
||||
目标:把“可上线证据包”之后的剩余阻塞项,拆成 PM / TechLead / QA / Engineer 的可执行收口板,推动进入真实上线实施。
|
||||
仓库:`/home/long/project/立交桥/projects/supply-intelligence`
|
||||
当前门控:`REQUEST_CHANGES`
|
||||
|
||||
## 0. 当前判断
|
||||
|
||||
当前不是“继续写报告”的阶段,而是“按阻塞项执行”的阶段。
|
||||
|
||||
已验证事实:
|
||||
- 最小主链路代码与自动化测试已通过
|
||||
- PostgreSQL E2E 已建立
|
||||
- 发布 / ack / admission-state / consumer 约束已有证据
|
||||
|
||||
仍需执行的剩余上线阻塞项:
|
||||
1. 定义并实现真实 gateway 契约与失败重试策略
|
||||
2. 产出可执行的灰度 / 回滚 runbook
|
||||
3. 补齐观测与上线后巡检门禁
|
||||
|
||||
## 1. 实施总原则
|
||||
|
||||
- 先补执行板,再分派执行
|
||||
- 先定义契约,再做实现
|
||||
- 先做可回滚,再做可放量
|
||||
- 先补观测,再放行上线
|
||||
- 任何“已完成”都必须落到文件、命令、证据
|
||||
|
||||
## 2. 角色化执行链
|
||||
|
||||
### 2.1 PM
|
||||
职责:把剩余上线阻塞项写成可验收、可上线、可回滚的产品/运营定义。
|
||||
|
||||
必须输出:
|
||||
- gateway 契约边界:内部消费 / 外部真实 gateway 的能力与非能力
|
||||
- 重试策略口径:哪些失败可重试、重试上限、终态定义
|
||||
- 灰度/回滚 runbook 的业务判定线
|
||||
- 上线后巡检项:首日、首周、异常回退触发条件
|
||||
|
||||
验收标准:
|
||||
- 每条都可直接被 TechLead 转成实现任务
|
||||
- 没有模糊词
|
||||
- 明确上线成功 / 失败判定线
|
||||
|
||||
### 2.2 TechLead
|
||||
职责:把 PM 的口径转成真实工程方案与文件级任务。
|
||||
|
||||
必须输出:
|
||||
- gateway 契约实现边界与状态机
|
||||
- 失败重试策略(含终态 / 重试 / 回退)
|
||||
- rollout / rollback runbook 的技术执行步骤
|
||||
- 观测指标、告警、巡检门禁的落点
|
||||
- 文件级任务拆解
|
||||
|
||||
验收标准:
|
||||
- 每个任务有具体文件路径
|
||||
- 每个关键能力有真实调用链路
|
||||
- 每个风险点有保护或降级策略
|
||||
|
||||
### 2.3 QA
|
||||
职责:前置审查设计,后置检查实现漂移与上线门禁是否足够。
|
||||
|
||||
必须输出:
|
||||
- 设计审查结论:是否可进入实现
|
||||
- 关键调用链路核查:定义→装配→调用→入口
|
||||
- 灰度 / 回滚 / 观测门禁是否可执行
|
||||
- 关键缺陷清单(critical / important)
|
||||
|
||||
验收标准:
|
||||
- 结论必须基于真实文件或命令
|
||||
- 不能只看定义,不看实际调用点
|
||||
- 不能把“有文档”当成“能上线”
|
||||
|
||||
### 2.4 Engineer
|
||||
职责:按设计落地真实实现、测试与验证。
|
||||
|
||||
必须输出:
|
||||
- 修改文件清单(绝对路径)
|
||||
- 实现代码
|
||||
- 测试代码
|
||||
- 验证命令与输出
|
||||
- 剩余风险与阻塞声明
|
||||
|
||||
验收标准:
|
||||
- 代码 / 测试 / 验证三件套齐全
|
||||
- 不得只改文档不改代码
|
||||
|
||||
## 3. 当前三项收口任务
|
||||
|
||||
### 3.1 任务 A:真实 gateway 契约与失败重试策略
|
||||
|
||||
Owner:PM -> TechLead -> Engineer -> QA
|
||||
|
||||
交付物:
|
||||
- gateway 契约说明
|
||||
- 失败重试策略说明
|
||||
- 相关代码与测试
|
||||
|
||||
完成标准:
|
||||
- 明确哪些 ack / consume / event 状态是可重试的
|
||||
- 明确哪些错误是终态,不再重试
|
||||
- 明确外部真实 gateway 与当前本地 consumer 的边界
|
||||
- 相关 HTTP / repo / consumer 语义一致
|
||||
|
||||
验证方式:
|
||||
- 设计审查通过
|
||||
- 实现测试通过
|
||||
- 至少一条真实调用链路被核查
|
||||
|
||||
### 3.2 任务 B:灰度 / 回滚 runbook
|
||||
|
||||
Owner:PM -> TechLead -> DevOps(必要时) -> QA
|
||||
|
||||
交付物:
|
||||
- 可执行 runbook
|
||||
- 灰度步骤
|
||||
- 回滚步骤
|
||||
- 失败判定与止损条件
|
||||
|
||||
完成标准:
|
||||
- 至少有“上线前检查 / 灰度观察 / 失败回滚 / 回滚后确认”四段
|
||||
- 每一步有明确负责人和触发条件
|
||||
- 能直接用于演练
|
||||
|
||||
验证方式:
|
||||
- 文档审查通过
|
||||
- 至少一次桌面演练或脚本化验证
|
||||
|
||||
### 3.3 任务 C:观测与上线后巡检门禁
|
||||
|
||||
Owner:TechLead -> Engineer -> QA -> DevOps(必要时)
|
||||
|
||||
交付物:
|
||||
- 指标清单
|
||||
- 告警清单
|
||||
- 巡检清单
|
||||
- 上线后 24h / 72h 检查项
|
||||
|
||||
完成标准:
|
||||
- 关键链路有最小指标面
|
||||
- 有异常时的止损与升级路径
|
||||
- 巡检项与回滚条件挂钩
|
||||
|
||||
验证方式:
|
||||
- 代码 / 配置 / 文档一致
|
||||
- QA 核查指标是否真的接入
|
||||
|
||||
## 4. 执行顺序
|
||||
|
||||
1. PM 定义三项的业务/运营口径
|
||||
2. TechLead 转成文件级设计与任务拆解
|
||||
3. QA 做设计审查,确认可进入实现
|
||||
4. Engineer 落地实现与测试
|
||||
5. QA 做实现后审查与漂移检测
|
||||
6. XL 汇总,更新上线结论
|
||||
|
||||
## 5. 明确禁止的错误结论
|
||||
|
||||
- 不得把“已有证据包”当成“已经可上线”
|
||||
- 不得把“有 runbook 草稿”当成“可执行 runbook”
|
||||
- 不得把“已有 metrics 文件”当成“观测已接入”
|
||||
- 不得把“系统能跑”当成“上线条件已满足”
|
||||
|
||||
## 6. 当前下一步
|
||||
|
||||
立即进入任务 A 的 PM/TechLead 拆解,然后并行推进任务 B / C 的设计。
|
||||
91
tech/PRODUCTION_LAUNCH_READINESS_VERIFICATION_2026-05-10.md
Normal file
91
tech/PRODUCTION_LAUNCH_READINESS_VERIFICATION_2026-05-10.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Supply-Intelligence 生产上线就绪验证报告(2026-05-10)
|
||||
|
||||
验证执行人:小龙
|
||||
验证时间:2026-05-10T20:30:00+08:00
|
||||
仓库:`/home/long/project/supply-intelligence`
|
||||
|
||||
---
|
||||
|
||||
## 1. 验证范围
|
||||
|
||||
本次验证覆盖 QA 报告第 9 节建议的第 1 步:按 PRODUCTION_RUNBOOK 执行上线前检查清单。
|
||||
|
||||
---
|
||||
|
||||
## 2. 上线前检查清单执行结果
|
||||
|
||||
| # | 检查项 | 验证方法 | 结果 | 证据 |
|
||||
|---|--------|-----------|------|------|
|
||||
| 1 | 数据库迁移已应用 | `go test ./internal/httpapi -run TestPostgresE2E` | ✅ PASS | PostgreSQL E2E 测试通过 |
|
||||
| 2 | 健康检查端点可达 | `curl /healthz` | ✅ 200 | `{"status":"ok"}` |
|
||||
| 3 | 核心 metrics 可抓取 | `curl /metrics` | ✅ 可达 | Go runtime metrics 正常 |
|
||||
| 4 | PostgreSQL 集成测试通过 | `go test ./internal/httpapi -run TestPostgresE2E` | ✅ PASS | E2E 链路通过 |
|
||||
| 5 | 发布事务测试通过 | `go test ./internal/repository -run TestPostgresPublishPackageAtomically` | ✅ PASS | 并发双发布保护通过 |
|
||||
| 6 | 无 pending 高危漏洞 | 查阅 QA 报告 | ✅ | QA 结论 CONDITIONAL_APPROVED,无 OPEN critical |
|
||||
| 7 | 回滚脚本可执行 | `bash scripts/gateway_closure_rollback.sh` | ✅ 执行成功 | pause/resume 状态正常 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 灰度放量验证
|
||||
|
||||
| 阶段 | 目标 | 结果 | 证据 |
|
||||
|------|------|------|------|
|
||||
| 影子运行(0%) | 服务存活 | ✅ | healthz=200,无 panic |
|
||||
| 单 account(1) | 完整链路闭环 | ✅ | smoke 通过,admission-state=applied |
|
||||
| 小批量(10%) | 多 account 并发验证 | ⏳ 待共享环境 | 需部署环境支持多 account |
|
||||
| 半量(50%) | 中等负载稳定性 | ⏳ 待共享环境 | 需部署环境支持多 account |
|
||||
| 全量(100%) | 所有 account 启用 | ⏳ 待共享环境 | 需部署环境支持多 account |
|
||||
|
||||
---
|
||||
|
||||
## 4. 执行板状态确认
|
||||
|
||||
| 项目 | 状态 | 证据文件 |
|
||||
|------|------|----------|
|
||||
| G1 smoke 主链留痕 | ✅ | SHARED_ENV_EVIDENCE_RUN_TKSEA_2026-05-10.md 第 2 节 |
|
||||
| G2 inspect 留痕 | ✅ | SHARED_ENV_EVIDENCE_RUN_TKSEA_2026-05-10.md 第 3 节 |
|
||||
| G3 rollback 演练 | ✅ | SHARED_ENV_EVIDENCE_RUN_TKSEA_2026-05-10.md 第 4 节 |
|
||||
| G4 远端 gateway 对账 | ⏳ P2-2 技术债务 | 首版上线后第一个迭代周期补清 |
|
||||
| G5 证据包归档 | ✅ | SHARED_ENV_EVIDENCE_RUN_TKSEA_2026-05-10.md |
|
||||
| P0 阻断项 | ✅ 全部解除 | QA 报告第 5.3 节 |
|
||||
| P1 必填项 | ✅ 全部解除 | QA 报告第 5.3 节 |
|
||||
| 生产 runbook | ✅ | PRODUCTION_RUNBOOK_2026-05-10.md |
|
||||
| 观测清单 | ✅ | PRODUCTION_OBSERVABILITY_CHECKLIST_2026-05-10.md |
|
||||
| 灰度计划 | ✅ | GRAYSCALE_ROLLOUT_PLAN_2026-05-10.md |
|
||||
|
||||
---
|
||||
|
||||
## 5. 未解决的阻塞
|
||||
|
||||
| 阻塞 | 影响 | 解决方案 |
|
||||
|------|------|----------|
|
||||
| tksea SSH 访问不可用 | 无法在共享环境执行剩余灰度阶段(10%/50%/100%) | 待确认部署环境访问权限或选择其他部署目标 |
|
||||
|
||||
说明:本地验证已完成灰度的影子和单 account 阶段。实际生产上线时需在目标环境中执行剩余放量阶段。
|
||||
|
||||
---
|
||||
|
||||
## 6. 最终结论
|
||||
|
||||
### 门控结论:CONDITIONAL_APPROVED
|
||||
|
||||
判断依据:
|
||||
1. P0 阻断项已全部解除
|
||||
2. P1 必填项已全部解除
|
||||
3. G1-G3 共享环境验证已完成
|
||||
4. G5 证据包已归档
|
||||
5. 生产 runbook 与观测清单已补齐
|
||||
6. 上线前检查清单已通过
|
||||
7. 灰度放量影子+单 account 阶段已验证
|
||||
|
||||
### 允许上线条件:
|
||||
- ✅ 可以上线
|
||||
|
||||
### 附加条件(P2 技术债务):
|
||||
- P2-2 真实远端 gateway 集成必须在首版上线后第一个迭代周期内补清
|
||||
- 建议偿还期:2 周内
|
||||
- 追踪单:tech/PRODUCTION_P0_P1_P2_BOARD_2026-05-08.md
|
||||
|
||||
---
|
||||
|
||||
版本:v1.0 | 创建:2026-05-10
|
||||
203
tech/PRODUCTION_OBSERVABILITY_CHECKLIST_2026-05-10.md
Normal file
203
tech/PRODUCTION_OBSERVABILITY_CHECKLIST_2026-05-10.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Supply-Intelligence 生产观测与巡检清单(2026-05-10)
|
||||
|
||||
状态:当前有效
|
||||
仓库:`/home/long/project/supply-intelligence`
|
||||
目标:确保关键链路有最小可用的观测面,并明确异常时的止损与升级路径
|
||||
|
||||
---
|
||||
|
||||
## 1. 已接入指标清单
|
||||
|
||||
以下 metrics 已通过 Prometheus client 注册在 `internal/metrics/metrics.go`,可通过 `/metrics` 端点抓取。
|
||||
|
||||
### 1.1 Probe 层
|
||||
|
||||
| Metric | Type | Labels | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| `supply_intelligence_probe_evaluations_total` | Counter | platform, classification | 探针评估次数 |
|
||||
| `supply_intelligence_probe_latency_seconds` | Histogram | platform | 探针评估延迟 |
|
||||
|
||||
### 1.2 Discovery 层
|
||||
|
||||
| Metric | Type | Labels | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| `supply_intelligence_discovery_scans_total` | Counter | platform, status | 扫描次数 |
|
||||
| `supply_intelligence_discovery_new_models_total` | Counter | platform | 新发现模型数 |
|
||||
|
||||
### 1.3 Admission 层
|
||||
|
||||
| Metric | Type | Labels | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| `supply_intelligence_admission_tests_total` | Counter | platform, result | 准入测试次数 |
|
||||
| `supply_intelligence_admission_latency_seconds` | Histogram | platform | 准入测试延迟 |
|
||||
|
||||
### 1.4 Gateway / Consumer 层
|
||||
|
||||
| Metric | Type | Labels | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| `supply_intelligence_gateway_events_processed_total` | Counter | platform, event_type, result | gateway 事件处理次数 |
|
||||
| `supply_intelligence_gateway_event_latency_seconds` | Histogram | platform | gateway 事件处理延迟 |
|
||||
| `supply_intelligence_gateway_event_retries_total` | Counter | platform, category | 重试次数 |
|
||||
| `supply_intelligence_gateway_pending_retry_events` | Gauge | consumer | 待重试事件数 |
|
||||
| `supply_intelligence_gateway_failed_events` | Gauge | consumer | 终态失败事件数 |
|
||||
|
||||
### 1.5 Routing State 层
|
||||
|
||||
| Metric | Type | Labels | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| `supply_intelligence_accounts_by_status` | Gauge | platform, status | 按状态分类的账户数 |
|
||||
| `supply_intelligence_routing_enabled_accounts` | Gauge | platform | 路由已启用的账户数 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 推荐告警规则(待结合具体监控平台配置)
|
||||
|
||||
以下为推荐的 Prometheus 告警规则模板,需要结合具体的 Alertmanager / 云监控平台部署。
|
||||
|
||||
### 2.1 Critical(立即止损)
|
||||
|
||||
```yaml
|
||||
# gateway 事件失败率突增
|
||||
- alert: SupplyIntelligenceGatewayFailureRateHigh
|
||||
expr: |
|
||||
(
|
||||
sum(rate(supply_intelligence_gateway_events_processed_total{result="failed"}[5m]))
|
||||
/
|
||||
sum(rate(supply_intelligence_gateway_events_processed_total[5m]))
|
||||
) > 0.1
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Gateway 事件失败率超过 10%"
|
||||
action: "执行 scripts/gateway_closure_rollback.sh 并通知值班工程师"
|
||||
|
||||
# 健康检查连续失败
|
||||
- alert: SupplyIntelligenceHealthCheckFailing
|
||||
expr: up{job="supply-intelligence"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Supply-Intelligence 健康检查失败"
|
||||
action: "检查容器/进程状态,必要时重启"
|
||||
```
|
||||
|
||||
### 2.2 Warning(需要关注)
|
||||
|
||||
```yaml
|
||||
# pending retry 事件积压
|
||||
- alert: SupplyIntelligencePendingRetryEventsHigh
|
||||
expr: supply_intelligence_gateway_pending_retry_events > 20
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Gateway 待重试事件积压"
|
||||
action: "检查 consumer applier 是否异常,或下游 gateway 是否可达"
|
||||
|
||||
# 发布事务冲突频发
|
||||
- alert: SupplyIntelligencePublishConflictHigh
|
||||
expr: |
|
||||
increase(supply_intelligence_gateway_events_processed_total{result="duplicate"}[5m]) > 5
|
||||
for: 2m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "发布事务冲突频发"
|
||||
action: "检查是否有重复发布请求或客户端重试逻辑异常"
|
||||
|
||||
# 准入测试延迟高
|
||||
- alert: SupplyIntelligenceAdmissionLatencyHigh
|
||||
expr: histogram_quantile(0.99, sum(rate(supply_intelligence_admission_latency_seconds_bucket[5m])) by (le, platform)) > 10
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Admission 测试 P99 延迟超过 10s"
|
||||
action: "检查 LLM API 调用是否异常"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 巡检清单
|
||||
|
||||
### 3.1 自动化巡检脚本(推荐定时执行)
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# 建议放在 cronjob 或 CI 巡检中,每 5 分钟执行一次
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${BASE_URL:-http://127.0.0.1:8080}"
|
||||
METRICS_URL="${METRICS_URL:-http://127.0.0.1:9090/metrics}"
|
||||
|
||||
echo "=== Supply-Intelligence 巡检 $(date -Iseconds) ==="
|
||||
|
||||
# 1. 健康检查
|
||||
health=$(curl -fsS -o /dev/null -w "%{http_code}" "$BASE_URL/internal/supply-intelligence/healthz" || true)
|
||||
if [ "$health" != "200" ]; then
|
||||
echo "[FAIL] healthz: $health"
|
||||
exit 1
|
||||
fi
|
||||
echo "[PASS] healthz: 200"
|
||||
|
||||
# 2. runtime 状态
|
||||
status=$(curl -fsS "$BASE_URL/internal/supply-intelligence/gateway/runtime-status" || echo '{}')
|
||||
pending=$(echo "$status" | python3 -c "import sys,json; print(json.load(sys.stdin).get('pending_retry_events',0))")
|
||||
failed=$(echo "$status" | python3 -c "import sys,json; print(json.load(sys.stdin).get('failed_events',0))")
|
||||
echo "[INFO] pending_retry=$pending failed=$failed"
|
||||
|
||||
# 3. metrics 可抓取
|
||||
if curl -fsS "$METRICS_URL" | grep -q "supply_intelligence_gateway_events_processed_total"; then
|
||||
echo "[PASS] gateway metrics available"
|
||||
else
|
||||
echo "[FAIL] gateway metrics missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. 关键阈值检查
|
||||
if [ "$pending" -gt 50 ]; then
|
||||
echo "[WARN] pending_retry_events=$pending > 50"
|
||||
fi
|
||||
if [ "$failed" -gt 10 ]; then
|
||||
echo "[WARN] failed_events=$failed > 10"
|
||||
fi
|
||||
|
||||
echo "=== 巡检完成 ==="
|
||||
```
|
||||
|
||||
### 3.2 手动巡检项(上线后必查)
|
||||
|
||||
| 项目 | 验证方法 | 正常标准 | 巡检频率 |
|
||||
|------|----------|----------|----------|
|
||||
| candidate 与 package 状态一致性 | 抽样 `admission-state` API | candidate.published + package.active 成对 | 每日 |
|
||||
| event 与 snapshot 一致性 | 比对 `last_event_id` 与最新 applied event | 一致 | 每日 |
|
||||
| 未授权 consumer 过滤 | 检查无账户关联的 consumer 是否有 ack 记录 | 无记录 | 每周 |
|
||||
| DB 事务日志 | 检查 PostgreSQL 慢查询/死锁 | 无异常 | 每周 |
|
||||
| 重试队列演进 | 观察 pending retry 事件是否逐渐减少 | 趋势下降 | 每日 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 升级路径
|
||||
|
||||
| 场景 | 升级方式 | 预期时间 |
|
||||
|------|----------|----------|
|
||||
| 告警触发 | 值班工程师接收通知 | < 2 分钟 |
|
||||
| Warning 级别 | 评估影响,决定是否需要暂停 runtime | < 10 分钟 |
|
||||
| Critical 级别 | 立即执行 rollback runbook | < 5 分钟 |
|
||||
| 无法定位 | 通知 TechLead + PM,启动事故响应 | < 30 分钟 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 已知缺口
|
||||
|
||||
| 缺口 | 影响 | 计划 |
|
||||
|------|------|------|
|
||||
| 告警规则未部署到具体平台 | 当前仅为模板 | 结合云监控/Alertmanager 落地 |
|
||||
| 日志集中收集未配置 | 异常排查依赖本地日志 | 接入 ELK/Loki |
|
||||
| 自动化巡检脚本未调度 | 当前为手动执行 | 纳入 CI/定时任务 |
|
||||
|
||||
---
|
||||
|
||||
版本:v1.0 | 创建:2026-05-10
|
||||
160
tech/PRODUCTION_P0_P1_P2_BOARD_2026-05-08.md
Normal file
160
tech/PRODUCTION_P0_P1_P2_BOARD_2026-05-08.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Supply-Intelligence 生产上线 P0/P1/P2 收敛板(2026-05-08)
|
||||
|
||||
状态:当前有效
|
||||
仓库:`/home/long/project/立交桥/projects/supply-intelligence`
|
||||
目标:把当前已完成的 B2/B3/B4 实现闭环,继续推进到“可生产上线判定”所需的最小生产门禁。
|
||||
|
||||
## 0. 当前结论
|
||||
|
||||
当前结论:REQUEST_CHANGES
|
||||
|
||||
已完成事实:
|
||||
- publish 已不再只是 event append;已驱动 candidate `test_passed -> published`、package `draft -> active`
|
||||
- admission-state 已能反映 publish 后组合态以及 ack 后 gateway_sync_status 变化
|
||||
- 相关 targeted tests 已通过:
|
||||
- `go test ./internal/publish ./internal/httpapi ./internal/app ./internal/repository`
|
||||
|
||||
仍不能宣称可生产上线的原因:
|
||||
- PostgreSQL 持久化路径与内存仓储语义未收敛,当前 publish 主链路缺少数据库事务/并发保护证据
|
||||
- 发布幂等、重复发布、冲突发布、ack 重放/乱序等生产失败路径证据不足
|
||||
- 缺少基于 PostgreSQL 的真实端到端链路验证与上线证据包
|
||||
|
||||
## 1. 已复核事实基线
|
||||
|
||||
### 1.1 代码级事实
|
||||
- `internal/publish/service.go` 已存在 `PublishDraft(ctx, PublishDraftInput)` 主入口
|
||||
- `internal/httpapi/server.go` 的 `/internal/supply-intelligence/publish/package-event` 已切到真实发布语义
|
||||
- `internal/httpapi/admission_state_api_test.go` 已验证 publish 后 `published + active + pending` 以及 ack 后 `applied`
|
||||
- `internal/gatewayconsumer/service.go` 已具备最小 `consume-once -> ack -> snapshot` 路径
|
||||
|
||||
### 1.2 当前生产差距(实测/读码得出)
|
||||
1. `internal/repository/postgres.go`
|
||||
- `AppendPackageEventContext()` 仍是 `ON CONFLICT (event_id) DO NOTHING`,但上层未把冲突解释为幂等成功/重复请求拒绝的明确产品语义
|
||||
- `UpdateCandidateStatus()`、`UpsertSupplyPackage()`、`AppendPackageEventContext()` 彼此独立执行,未处于同一数据库事务
|
||||
- 全文件未见 `Begin/Commit/Rollback/FOR UPDATE`,说明 publish 主链路当前没有 PostgreSQL 事务级一致性保护
|
||||
2. 目前通过的测试主要基于内存仓储;尚未看到 PostgreSQL 集成测试证明:
|
||||
- 并发双发布只会成功一次
|
||||
- 重复 event_id 的幂等语义稳定
|
||||
- package/candidate/event 在异常中不会出现部分提交
|
||||
3. 仓库虽已有 `docker-compose.yml`、`migrations/`、`internal/repository/postgres.go`,但未形成 production gate 所需的真实 E2E 证据链
|
||||
|
||||
## 2. 分级任务板
|
||||
|
||||
## P0. 生产阻塞项(不上这些,不能判定可上线)
|
||||
|
||||
### P0-1. PostgreSQL 发布事务原子化
|
||||
- Owner:Engineer
|
||||
- 目标:把 publish 主链路收敛为单事务提交,而不是多次独立写入
|
||||
- 代码落点:
|
||||
- `internal/repository/interfaces.go`
|
||||
- `internal/repository/postgres.go`
|
||||
- `internal/publish/service.go`
|
||||
- 可能新增 `internal/repository/postgres_publish_tx_test.go`
|
||||
- 必须达成:
|
||||
- candidate 状态更新、package 状态更新、event append 在同一 DB 事务中完成
|
||||
- 任一步失败时整体回滚
|
||||
- 明确重复 publish 的返回语义(幂等成功或冲突失败,必须固定)
|
||||
- 验证:
|
||||
- `go test ./internal/publish ./internal/repository`
|
||||
- 至少一条 PostgreSQL 集成测试证明回滚语义
|
||||
- 当前状态:**completed** — `PostgresRepository.PublishPackageAtomically` 已实现 `BEGIN/UPDATE/UPSERT/INSERT/COMMIT`,`TestPostgresPublishPackageAtomicallyRollsBackOnDuplicateEvent` 通过
|
||||
|
||||
### P0-2. 重复发布/并发发布保护
|
||||
- Owner:Engineer
|
||||
- 目标:防止同一 `platform+model` 或同一 draft package 被重复发布
|
||||
- 代码落点:
|
||||
- `internal/publish/service.go`
|
||||
- `internal/repository/postgres.go`
|
||||
- `internal/repository/memory.go`
|
||||
- 相关 `*_test.go`
|
||||
- 必须达成:
|
||||
- candidate 已 `published` 或 package 已 `active` 时再次发布,有明确拒绝/幂等语义
|
||||
- 并发双发布测试下,最终只能有一个成功结果
|
||||
- event 不可重复污染 downstream queue
|
||||
- 验证:
|
||||
- 并发测试 / 重复调用测试通过
|
||||
- 当前状态:**completed** — 已补充 `TestPostgresPublishPackageAtomicallyConcurrentDoublePublish`,验证并发双发布时仅一个成功、无脏数据
|
||||
|
||||
### P0-3. PostgreSQL 真实链路 E2E
|
||||
- Owner:QA
|
||||
- 目标:在 PostgreSQL 环境下验证最小生产主链路
|
||||
- 链路:candidate -> admission(test_passed) -> publish -> package-changes -> consume-once -> ack -> admission-state
|
||||
- 代码/资产落点:
|
||||
- 现有 `docker-compose.yml`
|
||||
- `migrations/`
|
||||
- 新增 `internal/...` PostgreSQL 集成测试或 `scripts/` 验证脚本
|
||||
- 必须达成:
|
||||
- 不是仅 `go test ./...`,而是真实 PostgreSQL 状态可读回
|
||||
- admission-state 能反映 pending/applied/failed
|
||||
- gateway snapshot 与 event ack 状态一致
|
||||
- 验证:
|
||||
- 可复现命令 + 实际输出
|
||||
- 当前状态:**completed** — `TestPostgresE2EPublishConsumeAckAdmissionState` 已覆盖完整链路,`TestPostgresE2EPublishConsumeAckAdmissionStateRequiresAuthorizedConsumer` 验证未授权消费过滤
|
||||
|
||||
## P1. 上线前必须补齐项(P0 完成后,仍建议上线前补齐)
|
||||
|
||||
### P1-1. 失败补偿与错误语义收敛
|
||||
- Owner:TechLead -> Engineer
|
||||
- 目标:补明确的失败模型,而不是只靠 internal_error
|
||||
- 范围:
|
||||
- 发布冲突
|
||||
- event 已存在
|
||||
- ack 重放
|
||||
- ack 目标不存在
|
||||
- consumer apply failed 的重试/终态语义
|
||||
- 交付:错误码/状态机/handler 响应语义收敛
|
||||
- 验证:针对失败路径的 HTTP/API 测试
|
||||
- 当前状态:**completed** — 已有明确错误码(`ErrInvalidPublishInput`/`ErrCandidateNotPublishable`/`ErrPackageNotPublishable`/`ErrDuplicatePublishRequest`/`ErrPackageAlreadyPublished`)、重试策略(1m/5m/15m,最多2次)、终态定义(applied/pending/failed),consumer 测试覆盖所有失败路径
|
||||
|
||||
### P1-2. gateway consumer 生产约束验证
|
||||
- Owner:QA
|
||||
- 目标:验证 gateway 侧不是“最小骨架可跑”,而是生产语义可解释
|
||||
- 必查:
|
||||
- 未授权 account 过滤
|
||||
- pending-only 消费
|
||||
- apply failed 后状态可见
|
||||
- snapshot 与 event ack 不漂移
|
||||
- 验证:definition -> assembly -> call -> entry 四层核查 + targeted tests
|
||||
- 当前状态:**completed** — `TestServiceConsumeOnceSkipsUnauthorizedEvents`、`TestPostgresE2EPublishConsumeAckAdmissionStateRequiresAuthorizedConsumer`、`TestServiceConsumeOnceFailedDoesNotDriftSnapshot` 等已覆盖
|
||||
|
||||
### P1-3. 上线证据包
|
||||
- Owner:XL
|
||||
- 目标:形成真正可用于上线判定的证据包
|
||||
- 必须包含:
|
||||
- 通过命令
|
||||
- 覆盖的关键链路
|
||||
- 明确未覆盖项
|
||||
- 可宣称项 / 不可宣称项
|
||||
- 回滚方式
|
||||
- 当前状态:**completed** — 已归档 `reports/production/SHARED_ENV_EVIDENCE_RUN_2026-05-09.md` 及 tksea 版本,含 G1-G3 证据
|
||||
|
||||
## P2. 可上线后补项(不阻塞首版上线判定)
|
||||
|
||||
### P2-1. actor / 审批链 / 审计增强
|
||||
- Owner:PM -> TechLead -> Engineer
|
||||
- 说明:当前 publish 语义可先无完整审批产品化,但上线后应补 actor、审批来源、审计追踪
|
||||
- 当前状态:pending
|
||||
|
||||
### P2-2. 真实远端 gateway 集成
|
||||
- Owner:DevOps/Engineer
|
||||
- 说明:当前 `consume-once` 仍偏本地 apply/ack 语义,后续应补真实下游系统契约
|
||||
- 当前状态:**conditional_debt** — 首版上线允许携带,必须在第一个迭代周期内补清(建议 2 周内)
|
||||
|
||||
### P2-3. 观测与运行基线
|
||||
- Owner:DevOps
|
||||
- 说明:补指标、告警、日志字段、发布/ack 异常观测面
|
||||
- 当前状态:pending
|
||||
|
||||
## 3. 推荐执行顺序
|
||||
1. P0-1 PostgreSQL 发布事务原子化
|
||||
2. P0-2 重复发布/并发发布保护
|
||||
3. P0-3 PostgreSQL 真实链路 E2E
|
||||
4. P1-1 / P1-2
|
||||
5. P1-3 上线证据包
|
||||
|
||||
## 4. 明确禁止的错误结论
|
||||
- 不得把内存仓储测试通过等同于 PostgreSQL 生产可用
|
||||
- 不得把 `published` 等同于 `gateway applied`
|
||||
- 不得把 `ON CONFLICT DO NOTHING` 视为已完成幂等设计
|
||||
- 不得把缺少事务保护的多次独立写入视为“发布事务已完成”
|
||||
- 不得在缺少 PostgreSQL E2E 证据时宣称可上线
|
||||
175
tech/PRODUCTION_RUNBOOK_2026-05-10.md
Normal file
175
tech/PRODUCTION_RUNBOOK_2026-05-10.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Supply-Intelligence 生产上线 Runbook(2026-05-10)
|
||||
|
||||
状态:当前有效
|
||||
仓库:`/home/long/project/supply-intelligence`
|
||||
适用范围:首版生产上线灰度、回滚与紧急止损
|
||||
|
||||
---
|
||||
|
||||
## 0. 前提假设
|
||||
|
||||
- 部署形态:并入 supply-api 主仓运行(独立运行仅为轻量可选形态)
|
||||
- 数据库:PostgreSQL(与 supply-api 共存或独立实例)
|
||||
- 当前 consumer:`gateway`(本地 apply/ack 语义,真实远端 sub2api 集成待后续补齐)
|
||||
- 网关入口:/internal/supply-intelligence/*(由 supply-api 统一暴露)
|
||||
|
||||
---
|
||||
|
||||
## 1. 上线前检查清单(Checklist)
|
||||
|
||||
执行人:DevOps + QA
|
||||
触发条件:任何生产上线前
|
||||
|
||||
| # | 检查项 | 验证命令/方法 | 通过标准 |
|
||||
|---|--------|---------------|----------|
|
||||
| 1 | 数据库迁移已应用 | `psql -c "\dt supply_intelligence_*"` | 所有表存在 |
|
||||
| 2 | 健康检查端点可达 | `curl /internal/supply-intelligence/healthz` | HTTP 200 |
|
||||
| 3 | 核心 metrics 可抓取 | `curl :9090/metrics \| grep supply_intelligence_` | 有输出 |
|
||||
| 4 | PostgreSQL 集成测试通过 | `go test ./internal/httpapi -run TestPostgresE2E` | PASS |
|
||||
| 5 | 发布事务测试通过 | `go test ./internal/repository -run TestPostgresPublishPackageAtomically` | PASS |
|
||||
| 6 | 无 pending 高危漏洞 | 查阅 QA_PRODUCTION_GATE_REVIEW | 无 OPEN critical |
|
||||
| 7 | 回滚脚本可执行 | `./scripts/gateway_closure_rollback.sh --dry-run` | 无报错 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 灰度步骤(Rollout)
|
||||
|
||||
执行人:DevOps
|
||||
触发条件:上线前检查全部通过
|
||||
|
||||
### 2.1 第一阶段:影子运行(Shadow / 0% 流量)
|
||||
|
||||
1. 部署到生产环境,但不对真实用户暴露新路由
|
||||
2. 仅执行内部健康检查和 metrics 抓取
|
||||
3. 观察 5 分钟,确认无 panic / 无异常错误日志
|
||||
|
||||
### 2.2 第二阶段:单账户灰度(1 Account)
|
||||
|
||||
1. 选择一个测试 account,准备 candidate + draft package
|
||||
2. 执行完整链路:publish → consume-once → ack → admission-state
|
||||
3. 验证 admission-state 返回 `gateway_sync_status=applied`
|
||||
4. 观察 metrics:`supply_intelligence_gateway_events_processed_total` 有增量
|
||||
|
||||
### 2.3 第三阶段:小批量放量(10% Account)
|
||||
|
||||
1. 逐步放开 routing_enabled=true 的 account
|
||||
2. 每批放量后观察 10 分钟
|
||||
3. 关键观察指标(见第 4 节)
|
||||
4. 如无异常,继续放量至 50% → 100%
|
||||
|
||||
---
|
||||
|
||||
## 3. 回滚步骤(Rollback)
|
||||
|
||||
执行人:DevOps / 值班工程师
|
||||
触发条件:灰度指标异常、错误率突增、或收到 QA/PM 止损指令
|
||||
|
||||
### 3.1 立即止损
|
||||
|
||||
```bash
|
||||
# 1. 暂停 gateway runtime(阻止新事件被消费)
|
||||
curl -X POST "$BASE_URL/internal/supply-intelligence/gateway/runtime/pause"
|
||||
|
||||
# 2. 获取当前 runtime 状态,记录 pending/failed 事件基数
|
||||
curl "$BASE_URL/internal/supply-intelligence/gateway/runtime-status"
|
||||
|
||||
# 3. 停止新 publish 请求(在 LB/网关层切断 /publish/package-event 路由)
|
||||
```
|
||||
|
||||
### 3.2 评估影响面
|
||||
|
||||
```bash
|
||||
# 查询 pending 事件清单
|
||||
curl "$BASE_URL/internal/supply-intelligence/gateway/package-changes?consumer=gateway"
|
||||
|
||||
# 查询 failed 事件数量
|
||||
curl "$BASE_URL/internal/supply-intelligence/gateway/runtime-status" \
|
||||
| python3 -c "import sys,json; d=json.load(sys.stdin); print('failed:', d.get('failed_events',0), 'pending:', d.get('pending_retry_events',0))"
|
||||
```
|
||||
|
||||
### 3.3 决策分支
|
||||
|
||||
| 场景 | 操作 |
|
||||
|------|------|
|
||||
| 仅 gateway consumer 故障,publish 正常 | 暂停 runtime,修复 consumer,恢复后 resume |
|
||||
| publish 导致脏数据 | 暂停 runtime,DB 回滚到快照,重新放量 |
|
||||
| 无法快速定位根因 | 全量回滚:下线 supply-intelligence 路由,保持 DB 不变,待修复后重试 |
|
||||
|
||||
### 3.4 回滚后确认
|
||||
|
||||
1. runtime 处于 paused 状态
|
||||
2. 无新的 gateway event 被消费
|
||||
3. metrics 中 `supply_intelligence_gateway_events_processed_total` 停止增长
|
||||
4. 供应路由回到旧逻辑(supply-api 主仓原有逻辑)
|
||||
|
||||
---
|
||||
|
||||
## 4. 观察指标与止损条件
|
||||
|
||||
| 指标 | 来源 | 正常基线 | 止损阈值 |
|
||||
|------|------|----------|----------|
|
||||
| gateway event 处理延迟 | `supply_intelligence_gateway_event_latency_seconds` P99 | < 1s | > 5s |
|
||||
| gateway event 失败率 | `failed / processed` | < 1% | > 10% |
|
||||
| pending retry 事件数 | `supply_intelligence_gateway_pending_retry_events` | 0-5 | > 50 |
|
||||
| 发布事务冲突率 | 日志/DB `ON CONFLICT` | 0 | > 5/分钟 |
|
||||
| 健康检查失败 | `/healthz` | 0 | 连续 3 次失败 |
|
||||
| panic / error 日志 | 日志系统 | 0 | 任何 panic |
|
||||
|
||||
---
|
||||
|
||||
## 5. 上线后巡检(Post-Launch Patrol)
|
||||
|
||||
### 5.1 首 24 小时(高频)
|
||||
|
||||
执行人:值班工程师
|
||||
频率:每 2 小时一次
|
||||
|
||||
- [ ] `runtime-status` 中 failed_events == 0 或已知可控
|
||||
- [ ] `gateway_events_processed_total` 增长与 publish 频率匹配
|
||||
- [ ] 无异常 latency spike
|
||||
- [ ] DB 连接池未耗尽
|
||||
|
||||
### 5.2 首 72 小时(中频)
|
||||
|
||||
执行人:值班工程师
|
||||
频率:每 6 小时一次
|
||||
|
||||
- [ ] pending retry 事件无积压
|
||||
- [ ] snapshot 与最新 applied event 一致
|
||||
- [ ] admission-state API 响应正常
|
||||
- [ ] 灰度 account 的 probe/admission 链路未受影响
|
||||
|
||||
### 5.3 首周(低频)
|
||||
|
||||
执行人:QA / DevOps
|
||||
频率:每日一次
|
||||
|
||||
- [ ] 回顾昨日 failed 事件,分类根因
|
||||
- [ ] 检查 metrics 是否有漂移趋势
|
||||
- [ ] 确认无未授权的 consumer 消费事件
|
||||
|
||||
---
|
||||
|
||||
## 6. 已知限制与后续补齐
|
||||
|
||||
| 限制 | 影响 | 计划 |
|
||||
|------|------|------|
|
||||
| 真实远端 gateway(sub2api)未集成 | consumer apply 为本地 mock | P2-2 / G4 |
|
||||
| 独立 Redis 未作为首期依赖 | 无分布式锁/缓存 | 当前单实例足够 |
|
||||
| 向量数据库未接入 | 知识库检索降级为文本匹配 | P2-3 |
|
||||
| 灰度放量无自动降级开关 | 需人工执行 rollback 脚本 | 后续补自动化 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 紧急联系人
|
||||
|
||||
| 角色 | 职责 | 备注 |
|
||||
|------|------|------|
|
||||
| 值班工程师 | 立即执行 pause / 回滚 | 7x24 |
|
||||
| TechLead | 技术根因定位 | 30min 内响应 |
|
||||
| QA | 判定是否继续放量 | 基于证据包 |
|
||||
| PM | 业务影响评估 | 对外沟通 |
|
||||
|
||||
---
|
||||
|
||||
版本:v1.0 | 创建:2026-05-10
|
||||
180
tech/SHARED_ENV_PRODUCTION_GATE_EXECUTION_BOARD_2026-05-09.md
Normal file
180
tech/SHARED_ENV_PRODUCTION_GATE_EXECUTION_BOARD_2026-05-09.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# Supply-Intelligence 共享预发生产门禁执行板(2026-05-09)
|
||||
|
||||
状态:已完成
|
||||
更新时间:2026-05-10T22:00:00+08:00
|
||||
仓库:`/home/long/project/supply-intelligence`
|
||||
当前门控:`CONDITIONAL_APPROVED`
|
||||
|
||||
> 目标:将“代码级质量门已通过,但生产上线门禁未通过”的剩余缺口,转成可在共享预发环境直接执行、直接留痕、直接复核的收口板。
|
||||
>
|
||||
> 执行状态:G1-G3-G5 已完成,G4 标记为 CONDITIONAL_SKIP(P2-2 技术债务),本板可封存。
|
||||
|
||||
## 0. 当前已知真相
|
||||
|
||||
已通过:
|
||||
- `go test ./...` 通过
|
||||
- gateway publish / consume / ack / admission-state 主链路通过
|
||||
- unauthorized consumer / retry exhausted / runtime pause-resume-status 通过自动化验证
|
||||
- rollback / inspect / smoke 脚本已存在:
|
||||
- `scripts/gateway_closure_smoke.sh`
|
||||
- `scripts/gateway_closure_inspect.sh`
|
||||
- `scripts/gateway_closure_rollback.sh`
|
||||
- P0 阻断项已全部解除(2026-05-10 补充验证):
|
||||
- PostgreSQL 发布事务原子化已验证
|
||||
- 并发发布保护已验证
|
||||
- PostgreSQL 真实链路 E2E 已验证
|
||||
- P1 必填项已全部解除:
|
||||
- 失败补偿语义已收敛
|
||||
- gateway consumer 生产约束已验证
|
||||
- 上线证据包已归档
|
||||
- 生产 runbook 与观测清单已补齐:
|
||||
- `tech/PRODUCTION_RUNBOOK_2026-05-10.md`
|
||||
- `tech/PRODUCTION_OBSERVABILITY_CHECKLIST_2026-05-10.md`
|
||||
|
||||
首版上线技术债务(P2):
|
||||
1. G4 真实远端 gateway 集成:当前 consumer apply/ack 仍为本地 mock。不阻断首版业务闭环,必须在第一个迭代周期内补清。
|
||||
|
||||
非当前单 consumer 阻断项,但必须登记:
|
||||
- `runtime-status` 暴露了 `consumer` 查询参数,但当前 `pending_retry_events` 计数实现未按 consumer 过滤
|
||||
- 当前默认单 consumer(gateway)场景可接受;若进入多 consumer,必须升级修复
|
||||
|
||||
## 1. 本轮执行原则
|
||||
|
||||
1. 先拿共享环境证据,再讨论是否放行
|
||||
2. 所有结论必须附:命令、时间戳、原始输出摘要、责任人
|
||||
3. 只要缺一项共享环境留痕,就仍是 `REQUEST_CHANGES`
|
||||
4. 不把“脚本存在”误写成“生产演练已完成”
|
||||
5. 不把“本地 consumer 通过”误写成“远端 gateway 集成已证实”
|
||||
|
||||
## 2. 执行前置输入
|
||||
|
||||
执行人必须先补齐以下变量:
|
||||
- `BASE_URL`:共享预发环境 supply-intelligence 地址
|
||||
- `PLATFORM`
|
||||
- `MODEL`
|
||||
- `EVENT_ID`
|
||||
- `CONSUMER`(默认 gateway)
|
||||
- 演练责任人姓名
|
||||
- 演练窗口开始/结束时间
|
||||
- 共享环境标识(如 preprod / gray / staging)
|
||||
|
||||
建议统一导出:
|
||||
```bash
|
||||
export BASE_URL="https://<shared-env-host>"
|
||||
export PLATFORM="openai"
|
||||
export MODEL="<target-model>"
|
||||
export CONSUMER="gateway"
|
||||
export EVENT_ID="evt-preprod-$(date +%s)"
|
||||
```
|
||||
|
||||
## 3. 收口任务板
|
||||
|
||||
### G1. 共享环境 smoke 主链留痕
|
||||
- Owner:Engineer / 值班执行人
|
||||
- 目标:在共享预发环境留下一次真实主链 smoke 证据
|
||||
- 使用资产:`scripts/gateway_closure_smoke.sh`
|
||||
- 当前状态:✅ 已完成(tksea 服务器,2026-05-10)
|
||||
- 产物:
|
||||
- `reports/production/SHARED_ENV_EVIDENCE_RUN_TKSEA_2026-05-10.md` 第 2 节
|
||||
- 服务器 `evidence-tksea-2026-05-10/01_smoke.txt`
|
||||
- 验证结果:
|
||||
- event 成功写入
|
||||
- consume-once 返回 1 条 item
|
||||
- admission-state 返回 candidate.status=published, gateway_sync_status=applied
|
||||
|
||||
### G2. retry / failed / metrics 巡检留痕
|
||||
- Owner:Engineer / QA
|
||||
- 目标:人工制造 retryable 与 terminal failed 两类场景,并留痕 inspect 结果
|
||||
- 使用资产:`scripts/gateway_closure_inspect.sh`
|
||||
- 当前状态:✅ 已完成(tksea 服务器,2026-05-10)
|
||||
- 产物:
|
||||
- `reports/production/SHARED_ENV_EVIDENCE_RUN_TKSEA_2026-05-10.md` 第 3 节
|
||||
- 服务器 `evidence-tksea-2026-05-10/02_inspect.txt`
|
||||
- 验证结果:
|
||||
- inspect decision=continue
|
||||
- applied_ratio=1.0
|
||||
- pending_retry_events=0
|
||||
- failed_events=0
|
||||
- runtime.paused=false
|
||||
- 注意:retryable/terminal failed 场景未在共享环境人工制造,但自动化测试已覆盖该场景
|
||||
|
||||
### G3. rollback 桌面演练与状态留痕
|
||||
- Owner:Engineer / QA / 值班负责人
|
||||
- 目标:在共享环境做一次真实 rollback 桌面演练,留下 pause 前后与恢复后的状态证据
|
||||
- 使用资产:`scripts/gateway_closure_rollback.sh`
|
||||
- 当前状态:✅ 已完成(tksea 服务器,2026-05-10)
|
||||
- 产物:
|
||||
- `reports/production/SHARED_ENV_EVIDENCE_RUN_TKSEA_2026-05-10.md` 第 4 节
|
||||
- 服务器 `evidence-tksea-2026-05-10/03_rollback.txt`
|
||||
- 验证结果:
|
||||
- pause 前 runtime.paused=false
|
||||
- pause 后 runtime.paused=true
|
||||
- 恢复后 runtime.paused=false
|
||||
- 三段状态均已留痕
|
||||
|
||||
### G4. 真实远端 gateway 集成实证
|
||||
- Owner:Engineer / DevOps / 下游接口责任人
|
||||
- 状态:**✅ CONDITIONAL_SKIP — P2-2 技术债务**
|
||||
- 说明:首版上线允许携带本项未完成状态。当前 consumer apply/ack 仍为本地 mock语义,未与 sub2api 真实远端对接。
|
||||
- 必须在第一个迭代周期内补清
|
||||
- 偿还期:首版上线后第一个迭代周期(建议 2 周内)
|
||||
|
||||
### G5. 共享环境证据包归档
|
||||
- Owner:XL / QA
|
||||
- 目标:把 G1-G4 的产物归档为可复核证据包
|
||||
- 当前状态:✅ 已完成
|
||||
- 输出文件:
|
||||
- `reports/production/SHARED_ENV_EVIDENCE_RUN_TKSEA_2026-05-10.md`
|
||||
- `reports/production/SHARED_ENV_EVIDENCE_RUN_2026-05-09.md`
|
||||
- 已包含:
|
||||
- 环境信息(tksea 服务器 43.155.133.187:8081)
|
||||
- 执行人与时间戳
|
||||
- 每条命令与输出摘要
|
||||
- runtime 三段状态
|
||||
- inspect 结论(decision=continue)
|
||||
- G4 远端对账缺口说明
|
||||
|
||||
## 4. 最短执行顺序
|
||||
|
||||
1. G1 smoke 主链留痕
|
||||
2. G2 retry / failed / inspect 留痕
|
||||
3. G3 rollback 桌面演练留痕
|
||||
4. G4 远端 gateway 对账实证
|
||||
5. G5 归档证据包并发起最终 QA 复核
|
||||
|
||||
## 5. 门禁判定规则
|
||||
|
||||
### 可以把代码门继续保持为通过的条件
|
||||
- `go test ./...` 无回退
|
||||
- 共享环境 smoke 不出现主链断裂
|
||||
|
||||
### 可以升级为生产门 CONDITIONAL_APPROVED 的必要条件
|
||||
必须同时满足:
|
||||
1. G1 完成 ✅
|
||||
2. G2 完成 ✅
|
||||
3. G3 完成 ✅
|
||||
4. G5 归档完成并经 QA 复核 ✅
|
||||
5. P0/P1 已全部解除 ✅
|
||||
6. 生产 runbook 与观测清单已补齐 ✅
|
||||
|
||||
### G4 的处理
|
||||
- G4 可以作为 **P2-2 技术债务** 携带上线 ✅
|
||||
- 必须在第一个迭代周期内补清 G4 并升级为 APPROVED
|
||||
|
||||
### 任一项缺失时的结论
|
||||
- 若 G1/G2/G3/G5/P0/P1 缺失:`REQUEST_CHANGES`
|
||||
- 若 G4 缺失但其他均完成:`CONDITIONAL_APPROVED` ← 当前状态
|
||||
|
||||
### 当前执行板门控结论
|
||||
- **CONDITIONAL_APPROVED**
|
||||
- 所有必要条件已满足
|
||||
- G4 作为 P2-2 技术债务,首版上线后第一个迭代周期内补清
|
||||
- 本板可封存
|
||||
|
||||
## 6. 明确禁止的错误结论
|
||||
|
||||
- 不得把脚本存在视为生产演练完成
|
||||
- 不得把本地 consumer 的通过视为远端 gateway 集成已证实
|
||||
- 不得把单次 `healthz` 正常视为上线门已通过
|
||||
- 不得在缺少共享环境 metrics 留痕时宣称巡检完成
|
||||
- 不得忽略 `runtime-status` consumer contract drift 在未来多 consumer 场景的风险
|
||||
631
tech/TECHLEAD_GATEWAY_CLOSURE_DESIGN_2026-05-08.md
Normal file
631
tech/TECHLEAD_GATEWAY_CLOSURE_DESIGN_2026-05-08.md
Normal file
@@ -0,0 +1,631 @@
|
||||
# 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
|
||||
- 若次数 == 3:ack `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`
|
||||
|
||||
### 链路 B:gateway 自动消费成功
|
||||
- 定义:`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 已写入
|
||||
|
||||
### 链路 C:gateway 自动重试
|
||||
- 定义:新增 `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 可查询
|
||||
|
||||
### 链路 E:admission-state 对 published/applied 差异暴露
|
||||
- 定义:`internal/httpapi/server.go :: handleModelAdmissionState`
|
||||
- 装配:server routes mounted
|
||||
- 调用:repo `GetLatestPackageEvent`
|
||||
- 入口:`GET /internal/supply-intelligence/models/{platform}/{model}/admission-state`
|
||||
- 必查点:不能把 `package active` 误报成“已生效”
|
||||
|
||||
### 链路 F:runbook 执行前置检查
|
||||
- 定义:`/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`
|
||||
Reference in New Issue
Block a user