把「已提交但没写下记录」的崩溃窗口写进文档

- docs/technical Tripo 集成方案:at-most-once submit 段新增「崩溃窗口(已知并接受,消不掉)」一条——submit 受理与 checkpoint 落库之间崩溃时库里没有任何记录,该窗口加判断消不掉;固定 max_attempts = 1 保证不会二次 submit / 二次计费,代价是这笔生成按失败退款、provider task id 也没留下
- docs/后端数据契约:provider checkpoint 那条把「重新 claim 一定能读到」的保证收窄为「checkpoint 已写下之后」,并指向集成方案的这一段
This commit is contained in:
2026-09-24 21:06:59 +08:00
parent 8fa2bfc3ed
commit d4416e8b88
2 changed files with 3 additions and 1 deletions
@@ -82,6 +82,8 @@ canvasCompletion? 画布占位框回填,只在项目资源落点下生效
3. 已有 `providerTaskId` 的 job 只允许 `get_task`、下载与落库,绝不允许再次 submit;
4. submit 成功但 checkpoint 写入失败的 attempt 只能终态失败,不得退回可重试队列,否则重试会二次 submit;该 job 保留脱敏对账信息供人工处理。
**崩溃窗口(已知并接受,消不掉)**:submit 的真实顺序是「先向 provider 提交(不可撤销、已计费)→ 再把 provider task id 落成 checkpoint」,两步之间进程崩溃时,库里没有任何记录能证明这次提交发生过。这个窗口不能靠加判断消掉——写 checkpoint 这一步本身也可能失败或崩溃,多加一层只会把窗口挪个位置。当前的口径是:Tripo job 固定 `max_attempts = 1`,租约耗尽的 job 直接终态失败、不会被重新 claim,所以**不会二次 submit、不会二次计费**;代价是这一笔 provider 侧已经跑起来的生成没有结果可用(按失败收口并退款),而且连 provider task id 都没留下,人工对账也缺凭据,净亏损由平台承担。因此「重新 claim 一定能读到已提交记录」只在 checkpoint 已经写下之后成立,不能当成从 submit 那一刻起的保证;要缩小窗口只能改 provider 侧(让提交本身可重放,或先申请再提交这类两阶段流程),不在本方案的范围内。
失败重试沿用现有语义:`fail` 先按 attempt 冲正扣费,`attempt < maxAttempts` 时回到 `pending` 并延迟重试,否则终态 `failed`。Tripo job 固定 `max_attempts = 1`,这条路径退化为“第一次失败即终态”,永不进入延迟重试。
**provider 成功但落库失败按失败处理**job 失败、该 attempt 退款,客户端看到 failed。没有正式资源引用就不算交付成功;checkpoint 保留,人工对账可证明这次生成确实发生过。由此产生的“provider 已消耗、用户已退款”净亏损是已知并接受的成本。
@@ -294,7 +294,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复
- 2026-08-06 原子提交对上述 source-only / slice 条款的修正:“provider 原图已持久化”只能解释为 OSS 对象已上传并验证,不再表示 project resource / account asset 已先行入库。source-only、透明整图和成功切片必须先完成最终选择,再作为一份 prepared commit 连同 canvas/job/receipt 一次提交;未选中或失败分支不得留下正式 resource / asset 部分记录。
- 2026-09-21 provider checkpoint 追加:主表末尾追加可选 `provider_kind` / `provider_task_id`,只服务外部 provider 异步任务的 at-most-once submit。`set_external_generation_job_provider_checkpoint_and_return` 复用与 phase 完全相同的 `job_id + worker_id + lease_token` running 租约栅栏,写入成功时追加一条 `provider_checkpoint` 事件;目标行已有 checkpoint 时直接拒绝覆盖,因此崩溃后重新 claim 的 attempt 只能读到既有 provider task id 继续查询与落库,不会二次 submit、二次扣费。checkpoint 不写入 `request_payload_json`,也不进入任何用户可见 read model、BFF 响应或公开契约,provider task id 只作服务端 checkpoint 与对账;`external_generation_job_summary` 不投影这两个字段,轻量摘要按无 checkpoint 返回。
- 2026-09-21 provider checkpoint 追加:主表末尾追加可选 `provider_kind` / `provider_task_id`,只服务外部 provider 异步任务的 at-most-once submit。`set_external_generation_job_provider_checkpoint_and_return` 复用与 phase 完全相同的 `job_id + worker_id + lease_token` running 租约栅栏,写入成功时追加一条 `provider_checkpoint` 事件;目标行已有 checkpoint 时直接拒绝覆盖,因此崩溃后重新 claim 的 attempt 只能读到既有 provider task id 继续查询与落库,不会二次 submit、二次扣费。这条保证的前提是 **checkpoint 已经写下**submit 已经被 provider 受理、但 checkpoint 还没落库时进程崩溃,库里就没有任何 provider 侧记录,这个窗口消不掉(写记录这一步本身也可能失败或崩溃),具体代价与当前应对见 `technical/【技术方案】Tripo 3D生成API集成-2026-09-21.md` 的「at-most-once submit」段。checkpoint 不写入 `request_payload_json`,也不进入任何用户可见 read model、BFF 响应或公开契约,provider task id 只作服务端 checkpoint 与对账;`external_generation_job_summary` 不投影这两个字段,轻量摘要按无 checkpoint 返回。
### `external_generation_job_summary`