feat: add audited task review and linked manual retries
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Orbit 功能说明:Pulse 与公共能力
|
||||
|
||||
> 版本 0.2 · 2026-09-17。Pulse 与 Weave 首版功能范围已确认;标注“建议”的细节待评审。Weave 具体范围另见流程设计。基础、任务执行、调度、资源控制和可靠订阅已实现;批量受理、独立任务核查/人工重试及查询筛选等继续开发,实现状态见持续开发计划。
|
||||
> 版本 0.2 · 2026-09-17。Pulse 与 Weave 首版功能范围已确认;标注“建议”的细节待评审。Weave 具体范围另见流程设计。基础、任务执行、调度、资源控制和可靠订阅已实现;批量受理及独立任务核查/人工重试已实现,查询筛选等继续开发,实现状态见持续开发计划。
|
||||
|
||||
## 1. 目标与责任边界
|
||||
|
||||
@@ -41,7 +41,7 @@ Weave 有界查询的一轮对应一个 Pulse 任务;查询得到“处理中
|
||||
- **立即执行**:受理后尽快进入可派发范围,实际开始时间受资源和排队影响。
|
||||
- **延迟执行**:在指定等待时长之后才允许开始。
|
||||
- **指定时间执行**:在指定时间之后才允许开始,不承诺硬实时触发。
|
||||
- **批量提交**:多项普通任务的提交方式;建议逐项返回结果,每项独立幂等,失败项独立补交。
|
||||
- **批量提交**:多项普通任务的提交方式;已实现最多32项逐项返回结果,每项独立幂等,失败项独立补交,整批请求不超过512 KiB。
|
||||
|
||||
任务至少说明:归属、队列、处理器与版本、参数版本、执行时间、时限、重试规则和提交幂等标识。已受理参数与生效策略保存快照;修改配置不静默改写历史任务。
|
||||
|
||||
@@ -58,7 +58,7 @@ Weave 有界查询的一轮对应一个 Pulse 任务;查询得到“处理中
|
||||
| 人工重试 | 校验权限、错误类别及业务确认条件,保留原历史,记录操作人和原因 |
|
||||
| 修改业务参数 | 新建关联任务,原任务参数不变 |
|
||||
|
||||
**设计建议:人工重试创建带 `retry_of` 的新任务。** 原任务保留终态及结果,避免外部订阅方已经消费终态后任务又悄悄恢复。业务幂等键应按原操作语义保留;平台新任务 ID 不代表允许重复产生业务副作用。此项在协议冻结前确认。
|
||||
**当前契约:人工重试创建带 `retry_of` 的新任务。** 原任务保留终态及结果,避免外部订阅方已经消费终态后任务又悄悄恢复。业务幂等键应按原操作语义保留;平台新任务 ID 不代表允许重复产生业务副作用。每个原任务最多一个直接后继,继续重试沿后继任务推进;需要明确安全声明、版本、证据和审计,详见[人工恢复接口](api/task-recovery.md)。
|
||||
|
||||
流程关联任务的人工恢复统一经过 Weave,校验流程版本、步骤归属和当前恢复条件;不能直接从普通任务重试入口生成无人接续的流程任务。
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ stateDiagram-v2
|
||||
|
||||
核查操作需要证据引用、权限和审计。核查前的迟到结果可以作为证据保存,但无效尝试不能直接改写任务终态。
|
||||
|
||||
人工重试的建议是创建关联新任务,旧任务终态不回退;此项参见 [待确认决策](README.md#需要继续确认的事项)。
|
||||
人工重试已冻结为创建retry_of关联新任务,旧任务终态不回退;每个原任务最多一个直接后继。核查和重试分别要求显式执行结束/安全重试声明,具体权限、证据、版本与预算见[人工恢复接口](api/task-recovery.md)。
|
||||
|
||||
管理审计随对应状态变更同事务追加,应用日常权限不允许改写历史审计。更正通过新的关联记录表达;归档、导出和到期清理另设权限与审计。数据库管理员级别的防篡改证据及长期留存方案在生产审计要求确定后另行设计,不能将普通日志等同于业务审计证据。
|
||||
|
||||
|
||||
@@ -160,3 +160,11 @@ P2 服务端已新增会话隔离、两步派发、租约、并发许可、结
|
||||
## 15. 独立接收方示例模型
|
||||
|
||||
[result-inbox示例](examples/result-inbox.md) 在业务方自己的数据库建立receiver_inbox和receiver_task_views,2个实体、17个Go/SQL中文注释字段,无外键。它们属于独立示例模块,不计入平台35个实体,也不加入平台迁移。Inbox去重记录与业务视图同事务提交;视图仅按更高任务版本推进,重复和迟到事件不能重复更新或回退业务结果。
|
||||
|
||||
## 000015 人工恢复及重试关联
|
||||
|
||||
复用现有Task.RetryOf与AuditEvent.EvidenceRef,仍为35个实体、289个直接声明的持久化字段。RetryOf中文注释/SQL COMMENT明确“同命名空间每个原任务最多一个直接后继”。迁移将历史普通索引fk_pulse_tasks_3替换为相同列(namespace_id,retry_of)的唯一索引uq_task_retry_parent,并增加禁止自身关联的CHECK;该历史名字表示留下的索引,不是重新增加外键。
|
||||
|
||||
核查和重试按认证→应用额度→操作幂等→队列→任务/尝试持锁,事务内校验状态、父任务/计划归属和无活动执行。核查的状态/result、额度释放、审计、回执及结果日志一起提交,结果流锁最后取得。人工重试创建新任务与受理事件,审计关联保存在原任务上;旧终态、旧尝试和迟到证据不覆盖。详见[接口契约](api/task-recovery.md)。
|
||||
|
||||
真实v7升级回归检查70个外键全部删除、原任务保留、323个未替换索引组件不变,并单独核验新的重试唯一索引覆盖原两列。已有非法重复重试关联或自关联不会被静默清理;迁移失败需核查实际数据。后续仍禁止外键及级联。
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
| P0 基础与数据模型 | Go workspace、独立 SDK module、公共/Pulse/Weave 模型、MySQL 迁移、配置、健康检查 | 编译/竞态/静态检查;真实 MySQL 建表、隔离、唯一性、预算、不可变版本、迁移失败防护;SDK 健康调用 | 已完成并通过阶段回归(见 P0 报告) |
|
||||
| P1 任务受理闭环 | 应用凭据与作用域权限、队列、幂等提交、查询、待执行取消、受理事件;SDK 客户端 | API/SDK 创建查询闭环;重复键与内容冲突、响应未确认重报、跨命名空间拒绝、参数边界 | 已完成并通过阶段回归(见 P1 报告;真实断路/提交 ACK 丢失注入留后续故障阶段) |
|
||||
| P2 Worker 执行闭环 | WSS 接入、能力/容量、领取与开始、租约、续租、结果确认、排空与恢复;Worker SDK | 真 Worker 执行;失联/重连/重复确认/旧尝试拒绝;业务效果未知转核查;节点故障接管 | 已完成并通过阶段回归:服务端、自动 SDK、历史查询、完整 Schema 与真实进程强杀恢复;测试拓扑和剩余生产故障矩阵见 P2 验收报告 |
|
||||
| P3 Pulse 完整能力 | 安全重试、延时、Cron/固定间隔、漏跑策略、计划版本、运行中取消、并发许可、配额/背压、可靠结果订阅、批量受理、独立任务核查/人工重试、查询筛选与事务Outbox接入 | 多实例领取/触发竞争、重试上限、取消竞争、队列隔离、积压恢复、事件重复交付 | 进行中:安全重试、计划调度、运行中取消、队列控制/限速/积压背压、跨命名空间应用配额、显式资源键许可及 SDK 场景已实现并通过全量回归;优先级及节点公平派发已实现并通过全量回归;可靠订阅核心、HTTP/WSS、管理 SDK 和管理示例已实现并回归;消费 SDK、协议示例及两项节点强杀回归已完成;业务 Inbox 场景、COMMIT响应丢失及消费者进程故障回归已完成;批量受理、SDK、接口与部分补交示例已完成全量回归;独立任务核查/人工重试、业务键/时间筛选与事务Outbox接入待完成 |
|
||||
| P3 Pulse 完整能力 | 安全重试、延时、Cron/固定间隔、漏跑策略、计划版本、运行中取消、并发许可、配额/背压、可靠结果订阅、批量受理、独立任务核查/人工重试、查询筛选与事务Outbox接入 | 多实例领取/触发竞争、重试上限、取消竞争、队列隔离、积压恢复、事件重复交付 | 进行中:安全重试、计划调度、运行中取消、队列控制/限速/积压背压、跨命名空间应用配额、显式资源键许可及 SDK 场景已实现并通过全量回归;优先级及节点公平派发已实现并通过全量回归;可靠订阅核心、HTTP/WSS、管理 SDK 和管理示例已实现并回归;消费 SDK、协议示例及两项节点强杀回归已完成;业务 Inbox 场景、COMMIT响应丢失及消费者进程故障回归已完成;批量受理、SDK、接口与部分补交示例已完成全量回归;独立任务核查/人工重试、审计查询、两次真实HTTP回执丢失及完整示例已通过全量回归;业务键/时间筛选与事务Outbox接入待完成 |
|
||||
| P4 Weave 主流程 | SDK 定义构建与发布校验、实例、顺序/分支/结构化并行汇合、信号/定时等待、版本绑定 | 重启接续、早到/重复信号、并行竞争、内部事件重放、发布版本兼容;端到端通用流程 | 待开发 |
|
||||
| P5 Weave 恢复与查询 | 有界查询、暂停继续、取消、人工核查恢复、控制门控与审计 | 轮次与尝试独立预算;暂停/开始/回执/超时竞争;恢复保留预算;未知结果不盲目重复 | 待开发 |
|
||||
| P6 生产验证与运维 | 完整监控、凭据轮换、备份恢复、滚动升级、运行手册、可复现负载和故障工具 | 对照生产验收清单执行,报告环境/指标/遗留项;容量、RPO/RTO 按真实拓扑验证 | 待开发 |
|
||||
@@ -42,7 +42,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
|
||||
| 核对项 | 当前证据与后续交付 |
|
||||
| --- | --- |
|
||||
| 批量任务受理 | 已实现[批量API](api/batch-tasks.md)、独立SDK及[补交示例](examples/batch.md);真实MySQL部分失败与HTTP响应丢失用例已通过,同键并发20轮及全量回归已通过,见[报告](reports/phase-3-batch-tasks.md) |
|
||||
| 独立任务人工核查与重试 | [重试API](api/retries.md) 仍未提供解除不确定性的核查接口;需证据、权限、版本、审计和历史保留,人工重试身份按关联新任务实现并冻结契约 |
|
||||
| 独立任务人工核查与重试 | 已实现[管理API及SDK](api/task-recovery.md)、专门权限、显式安全声明、证据/版本/审计和关联新任务;[完整场景](examples/recovery.md)及[回归报告](reports/phase-3-task-recovery.md)同步交付 |
|
||||
| 任务查询筛选 | [当前ListTasks](../sdk/go/types.go) 仅队列/状态/分页;需按业务关联键及创建时间范围查询,游标绑定所有筛选,保持授权与有界索引扫描 |
|
||||
| 业务到平台的事务Outbox接入 | [事务Outbox设计](04-reliability-and-data.md#61-业务系统到平台) 尚缺完整接入示例;需业务变更与待发请求同事务、稳定提交身份、响应不明核对和发送恢复,保持核心SDK无ORM/数据库依赖 |
|
||||
|
||||
@@ -64,6 +64,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
|
||||
| 优先级与应用隔离 | 近同时到期按优先级 → 旧任务等待补偿 → 应用/队列轮转 | P3,已实现完整示例 |
|
||||
| 可靠结果订阅 | 冻结结果 → 持久化交付 → Inbox 去重 → 确认/补发 → 耗尽后人工重试 | P3,核心、HTTP/WSS、管理及消费 SDK、独立Inbox示例与真实进程故障回归已完成 |
|
||||
| 批量文件作业受理 | 逐项身份 → 部分结果 → 同键核对 → 释放额度 → 只补交失败项 | P3,已实现完整示例 |
|
||||
| 独立任务人工恢复 | 未知结果 → 证据核查 → 保留原任务 → 关联新任务 → 审计查询 | P3,已实现完整示例 |
|
||||
| 并行生成结果 | 发布流程 → 并行执行 → 汇合 → 完成 | P4 |
|
||||
| 审核与外部回执 | 提交流程 → 等待信号 → 授权信号注入 → 继续 | P4 |
|
||||
| 外部作业轮询 | 提交一次 → 有界查询处理中状态 → 回执或最终结果 → 超限核查 | P5 |
|
||||
@@ -96,6 +97,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
|
||||
- [P3 消费 SDK 与节点故障回归](reports/phase-3-event-consumer-sdk.md)
|
||||
- [P3 业务 Inbox 与消费者故障回归](reports/phase-3-result-inbox.md)
|
||||
- [P3 批量任务受理回归](reports/phase-3-batch-tasks.md)
|
||||
- [P3 人工核查与重试回归](reports/phase-3-task-recovery.md)
|
||||
- [接口手册](api/README.md)
|
||||
- [SDK 接入](sdk/README.md)
|
||||
- [通用场景示例](examples/README.md)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
- 文档版本:0.2
|
||||
- 整理日期:2026-09-17
|
||||
- 阶段:P0–P2 已通过阶段验收;P3 安全重试、计划调度、运行中取消、队列资源控制和应用共享配额已通过回归,资源键许可及真实进程恢复已通过全量回归,优先级/节点公平派发及真实隔离场景已通过全量回归,批量任务受理已实现,数据库已改为无外键,P3–P7 继续推进,尚未完成全平台生产验收
|
||||
- 阶段:P0–P2 已通过阶段验收;P3 安全重试、计划调度、运行中取消、队列资源控制和应用共享配额已通过回归,资源键许可及真实进程恢复已通过全量回归,优先级/节点公平派发及真实隔离场景已通过全量回归,批量任务受理和独立任务人工核查/重试已实现,数据库已改为无外键,P3–P7 继续推进,尚未完成全平台生产验收
|
||||
- 适用范围:Orbit 统一平台,包含 Pulse 任务调度模块、Weave 流程编排模块与公共能力。平台面向多个业务系统复用。
|
||||
|
||||
## 阅读顺序
|
||||
@@ -35,6 +35,7 @@
|
||||
| WS 职责 | Worker 接入、能力与容量上报、任务派发、续租、结果上报、取消通知 |
|
||||
| 执行位置 | 业务处理器位于业务项目自己的 Worker 中 |
|
||||
| 外部依赖范围 | 首版以 MySQL 为必需的外部存储依赖;运行不要求 Redis、独立消息队列或独立协调服务 |
|
||||
| 人工重试的任务身份 | 创建带retry_of的关联新任务,原任务保留;自动重试保持原任务ID,见[接口](api/task-recovery.md) |
|
||||
| Pulse 与公共功能范围 | 任务管理、定时计划、Worker 与恢复、资源控制、多项目权限、审计可观测、SDK 与运维 |
|
||||
| Weave 定位与定义方式 | 同一平台内承担流程编排,研发通过 Go SDK 描述并发布版本,业务处理器仍在业务 Worker 中运行 |
|
||||
| Weave 首版流程能力 | 顺序、条件分支、有限并行与汇合、等待信号、定时等待、有界重复查询 |
|
||||
@@ -58,7 +59,6 @@ Orbit 不内置特定行业规则;Pulse 管理执行,Weave 管理步骤依
|
||||
| HTTP / WS 的最终字段、路径、错误码 | 以本稿作为协议草案,评审后冻结 v1 | [协议](03-http-websocket-protocol.md) |
|
||||
| Weave 技术细则 | 功能范围已确认;SDK/定义 Schema、暂停门控与计时、查询预算、并行失败策略、人工恢复身份仍需评审 | [流程](06-workflow-spec.md) |
|
||||
| WS 库及后续依赖版本 | 当前基础使用 Go 1.27.1、MySQL 8.4+ 与显式 SQL;WS 已锁定 coder/websocket v1.8.15,后续依赖需单独验证 | [模型基线](07-data-model.md) |
|
||||
| 人工重试的任务身份 | 建议创建关联的新任务,自动重试保持原任务 ID | [功能](01-functional-spec.md) |
|
||||
| Cron、固定间隔与夏令时细节 | 已实现五段 Cron、fixed-rate;缺失时刻跳过、重复时刻按 UTC 分开;时区快照随版本冻结 | [计划接口](api/schedules.md) |
|
||||
| 批量大小、消息上限、心跳、租约、扫描周期 | 均需设置有限上限;数值通过原型与压测确定 | [验收](05-production-acceptance.md) |
|
||||
| 幂等、事件、审计的保留周期 | 按恢复与业务追溯要求确定,不能仅随任务清理 | [数据](04-reliability-and-data.md) |
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
| `GET /health/ready` | 已实现,验证数据库、迁移记录及零外键要求 | [健康检查](health.md) |
|
||||
| `/v1/tasks` 提交、查询、分页与待执行取消 | P1 已实现 | [任务接口](tasks.md) |
|
||||
| `POST /v1/tasks:batch` | P3 已实现逐项受理、独立幂等、部分结果与补交 | [批量任务](batch-tasks.md) |
|
||||
| `/v1/tasks/{task_id}:review` / `:retry` 与 `/operations` | P3 已实现带证据核查、关联新任务与操作审计 | [人工恢复](task-recovery.md) |
|
||||
| `/v1/tasks/{task_id}/attempts` 与尝试详情 | P2 已实现 | [执行历史](execution-history.md) |
|
||||
| `/v1/tasks/{task_id}/attempts/{attempt_id}/evidence` 与证据详情 | P2 已实现 | [执行历史](execution-history.md) |
|
||||
| `/v1/queues` 创建、查询及 `:configure` 控制 | 已实现队列并发、开始速率、积压背压与暂停/恢复 | [队列接口与 SDK 场景](queues.md) |
|
||||
|
||||
@@ -58,4 +58,6 @@ P2 增加 workers:connect 和 workers:consume。bootstrap 只有显式提供 --w
|
||||
|
||||
P3 应用额度管理新增 `application:resources:read` 和 `application:resources:manage`。只有显式 `--resource-access` 才授予,作用域是所属应用的全部命名空间,普通队列管理不能替代。查询返回聚合计数,不开放其他命名空间的任务数据。`bootstrap --application-id <现有应用ID>` 可由数据库管理员增加命名空间,与 `--application` 二选一;验证应用启用及额度记录存在后,同事务创建新资源,所有秘密交付约定保持不变。应用默认未终结上限 1000000、并发 10000、开始速率 100000/秒;详见 [应用共享配额](application-resources.md)。
|
||||
|
||||
P3 独立任务核查、人工重试和操作审计分别增加 `tasks:review`、`tasks:retry`、`tasks:audit:read`,bootstrap显式 `--recovery-access` 才授予三项;默认凭据和 `--worker-access` 都不自动获得。核查/重试记录真实认证凭据及证据,读取任务详情仍需tasks:read,见[人工恢复](task-recovery.md)。
|
||||
|
||||
P3 结果订阅核心新增 `events:read`、`events:manage` 和 `events:consume`,bootstrap 仅显式 `--event-access` 时授予。订阅绑定创建凭据与单个队列,不能用同命名空间的另一张凭据消费或管理。当前 HTTP/WSS、管理及消费 SDK 已接入;凭据重绑继续开发,详见 [订阅接口](subscriptions.md)。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 安全重试 v1
|
||||
|
||||
状态:P3 的安全重试已实现。使用现有任务提交与 Worker 结果接口,无独立的自动重试路由;[Cron/固定间隔计划](schedules.md) 和 [运行中取消](cancellation.md) 已接入;[可靠结果订阅](subscriptions.md) 与业务Inbox已实现;独立任务人工核查与重试继续按 [计划](../08-development-plan.md) 开发。验证证据见 [本阶段报告](../reports/phase-3-safe-retry.md)。
|
||||
状态:P3 的安全重试已实现。使用现有任务提交与 Worker 结果接口,无独立的自动重试路由;[Cron/固定间隔计划](schedules.md) 和 [运行中取消](cancellation.md) 已接入;[可靠结果订阅](subscriptions.md) 与业务Inbox已实现;[独立任务人工核查与重试](task-recovery.md) 已实现,剩余能力继续按 [计划](../08-development-plan.md) 开发。验证证据见 [本阶段报告](../reports/phase-3-safe-retry.md)。
|
||||
|
||||
## 1. 三种不同的重报
|
||||
|
||||
@@ -31,7 +31,7 @@ Worker 的 `task.finish` 新增可选布尔字段 `retryable`,省略为 false
|
||||
|
||||
存在历史不确定效果时,只有 IDEMPOTENT 允许继续自动尝试。此声明要求跨进程、跨尝试、并发及迟到执行都具有业务幂等保障,例如持久化操作键、唯一约束、返回原结果及资源侧执行代次校验。Task.ID 在自动重试中稳定;AttemptID 会变化,不能作为跨尝试的业务幂等键。平台不会因为新尝试开始而保证旧进程已经停止。
|
||||
|
||||
`effect_uncertain` 是任务详情、摘要和派发任务中的可选布尔字段,false 时省略。只要曾发生尚未核实的授权执行,后来的明确失败、未开始即到期、总时限耗尽或取消都不会把该事实抹掉。当前有效尝试成功可自动清除;迟到成功证据不能清除。人工核查解除的管理接口尚未实现。
|
||||
`effect_uncertain` 是任务详情、摘要和派发任务中的可选布尔字段,false 时省略。只要曾发生尚未核实的授权执行,后来的明确失败、未开始即到期、总时限耗尽或取消都不会把该事实抹掉。当前有效尝试成功可自动清除;迟到成功证据不能清除。已实现[带证据核查](task-recovery.md):获授权管理主体确认历史执行结束及真实结果后,提交版本与结论;迟到证据本身不能代替核查。
|
||||
|
||||
## 3. 退避、预算与持久化
|
||||
|
||||
|
||||
232
docs/api/task-recovery.md
Normal file
232
docs/api/task-recovery.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# 人工核查、人工重试与任务操作审计
|
||||
|
||||
状态:P3 已实现服务端、Go SDK 和[可运行场景](../examples/recovery.md)。与[自动安全重试](retries.md)不同,人工操作需要专门权限、原因、证据引用和当前版本。验证范围见[阶段报告](../reports/phase-3-task-recovery.md)。
|
||||
|
||||
## 1. 公共规则
|
||||
|
||||
- HTTPS `Authorization: Bearer <token>`,写入带 `Content-Type: application/json` 与稳定 `Idempotency-Key`;查询不带幂等键。
|
||||
- 权限分别是 `tasks:review`、`tasks:retry`、`tasks:audit:read`,均校验任务当前命名空间和队列范围。默认提交及 Worker 凭据不包含它们;开发初始化显式 `--recovery-access` 才添加。真实生产应分离执行与管理身份。
|
||||
- ID 为32位小写十六进制;`expected_revision` 是非零十进制字符串。正文最多512 KiB,拒绝未知字段、重复键、大小写别名及非允许的null。
|
||||
- 写操作幂等作用域:命名空间 + `task.review` / `task.retry` + 原任务ID + key。同键不同内容409;当前鉴权通过后优先返回原回执,不因任务已终结或额度已满重新执行。幂等保留约定与[任务受理](tasks.md)相同。
|
||||
- 网络/提交结果不明时持久保留**原键、原版本和完整正文**核对;不要换键。回执是操作当时快照,最新状态用 `GetTask`。SDK不自动生成身份或自动重试。
|
||||
- `reason`:非空白UTF-8,1–1024个Unicode字符,无NUL。`evidence_ref`:1–512个Unicode字符,无空白/控制字符;使用受控记录引用,禁止放入凭据秘密。平台只保存引用,不请求地址或自动判定证据真实性。
|
||||
|
||||
## 2. POST /v1/tasks/{task_id}:review
|
||||
|
||||
权限 `tasks:review`。仅 `NEEDS_REVIEW` 且版本匹配、无当前租约、无活跃尝试和未释放许可时允许。普通独立任务可核查;合法计划任务需同事务核验计划与触发记录归属。流程所属任务或有门控的任务拒绝,恢复须经对应上层控制器。
|
||||
|
||||
| 字段 | 类型/要求 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| expected_revision | string,必填 | 当前待核查任务版本 |
|
||||
| reason / evidence_ref | string,必填 | 上述原因和证据边界 |
|
||||
| execution_settled | bool,必须显式true | 声明已核实全部历史执行已结束、不会继续产生效果 |
|
||||
| outcome | string,必填 | 仅SUCCEEDED或FAILED |
|
||||
| result | JSON,必填非null | 核查结论或业务结果引用,最多256 KiB,保留精确数值 |
|
||||
|
||||
先收集执行历史、迟到证据及外部系统凭证,核实所有旧执行和实际效果,再提交结论。仅“数据库租约已过期”不足以声明执行已经物理停止;平台无法杀死远程goroutine,也无法证明外部系统没有副作用。
|
||||
|
||||
请求示例,原状态为 NEEDS_REVIEW/revision=4:
|
||||
|
||||
```json
|
||||
{
|
||||
"expected_revision":"4",
|
||||
"reason":"已核实旧执行全部结束,操作未完成",
|
||||
"evidence_ref":"evidence:reconciliation-001",
|
||||
"execution_settled":true,
|
||||
"outcome":"FAILED",
|
||||
"result":{"confirmed":true,"sequence":9007199254740993}
|
||||
}
|
||||
```
|
||||
|
||||
首次及去重均返回HTTP200;完整示意响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "44444444444444444444444444444444",
|
||||
"data": {
|
||||
"task": {
|
||||
"id": "22222222222222222222222222222222",
|
||||
"namespace_id": "33333333333333333333333333333333",
|
||||
"queue_id": "11111111111111111111111111111111",
|
||||
"state": "FAILED",
|
||||
"revision": "5",
|
||||
"handler": "object.prepare",
|
||||
"handler_version": "v1",
|
||||
"payload_version": "v1",
|
||||
"payload": {
|
||||
"object_ref": "objects/input",
|
||||
"sequence": 9007199254740993
|
||||
},
|
||||
"business_key": "object:operation-001",
|
||||
"available_at": "2026-09-17T00:00:00Z",
|
||||
"deadline_at": "2026-09-17T00:01:00Z",
|
||||
"recovery_safety": "RECONCILE_ONLY",
|
||||
"retry": {
|
||||
"max_attempts": 1,
|
||||
"initial_delay_ms": 0,
|
||||
"max_delay_ms": 0,
|
||||
"attempt_timeout_ms": 10000
|
||||
},
|
||||
"attempt_count": 1,
|
||||
"result": {
|
||||
"confirmed": true,
|
||||
"sequence": 9007199254740993
|
||||
},
|
||||
"completed_at": "2026-09-17T00:00:20Z",
|
||||
"created_at": "2026-09-17T00:00:00Z",
|
||||
"updated_at": "2026-09-17T00:00:20Z"
|
||||
},
|
||||
"audit_id": "55555555555555555555555555555555",
|
||||
"deduplicated": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
事务内保存新终态/result、清除effect_uncertain、revision加一、设置completed_at,释放一次未终结额度;同时保存幂等回执、不可变审计及新的结果事件。失败整体回滚,不能只更新状态后补写审计。旧尝试及UNKNOWN结果不改写,迟到报告仍只追加证据;已有needs_review结果事件保持冻结,订阅会再收到更高版本的最终结果。串行计划可以在核查终结后推进后续触发。
|
||||
|
||||
SDK:`ReviewTask(ctx, taskID, key, orbit.ReviewTaskRequest)` 返回 `ReviewTaskResult`。
|
||||
|
||||
## 3. POST /v1/tasks/{task_id}:retry
|
||||
|
||||
权限 `tasks:retry`。只接受**没有上层所有者的独立任务**,原状态FAILED或CANCELLED,effect_uncertain=false,版本匹配,无租约/活跃尝试/未释放许可。NEEDS_REVIEW先核查,SUCCEEDED不能重试;计划任务和流程任务均拒绝直接生成后继。
|
||||
|
||||
| 字段 | 类型/要求 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| expected_revision | string,必填 | 原终态任务的当前版本 |
|
||||
| reason / evidence_ref | string,必填 | 重执行原因与安全依据 |
|
||||
| safe_to_retry | bool,必须显式true | 声明已确认旧执行结束,按原业务身份重新执行安全 |
|
||||
| task_timeout_ms | uint64,必填1–2592000000 | 新任务从受理起的总预算,最多30天;至少覆盖冻结的attempt_timeout_ms |
|
||||
| available_at | time/null,可选 | 新任务最早可执行时间,默认当前数据库时间 |
|
||||
| start_deadline_at | time/null,可选 | 新任务最晚开始时间,默认无独立限制,受新总截止时间约束 |
|
||||
|
||||
时间采用UTC RFC3339、微秒精度、年份2000–9998。available_at只允许最多1秒过去容差,必须早于新总截止;start_deadline_at必须晚于现在、不早于available_at、不晚于新总截止,详见[受理时间约束](tasks.md)。不继承原任务已经过期的绝对时间。
|
||||
|
||||
```json
|
||||
{
|
||||
"expected_revision":"5",
|
||||
"reason":"已核实旧执行不会继续产生效果,允许重新执行",
|
||||
"evidence_ref":"evidence:safe-retry-001",
|
||||
"safe_to_retry":true,
|
||||
"task_timeout_ms":60000
|
||||
}
|
||||
```
|
||||
|
||||
首次HTTP201,原键重报HTTP200且deduplicated=true。完整示意响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "77777777777777777777777777777777",
|
||||
"data": {
|
||||
"source_task_id": "22222222222222222222222222222222",
|
||||
"task": {
|
||||
"id": "66666666666666666666666666666666",
|
||||
"namespace_id": "33333333333333333333333333333333",
|
||||
"queue_id": "11111111111111111111111111111111",
|
||||
"state": "WAITING",
|
||||
"revision": "1",
|
||||
"handler": "object.prepare",
|
||||
"handler_version": "v1",
|
||||
"payload_version": "v1",
|
||||
"payload": {
|
||||
"object_ref": "objects/input",
|
||||
"sequence": 9007199254740993
|
||||
},
|
||||
"business_key": "object:operation-001",
|
||||
"available_at": "2026-09-17T00:00:30Z",
|
||||
"deadline_at": "2026-09-17T00:01:30Z",
|
||||
"recovery_safety": "RECONCILE_ONLY",
|
||||
"retry": {
|
||||
"max_attempts": 1,
|
||||
"initial_delay_ms": 0,
|
||||
"max_delay_ms": 0,
|
||||
"attempt_timeout_ms": 10000
|
||||
},
|
||||
"attempt_count": 0,
|
||||
"created_at": "2026-09-17T00:00:30Z",
|
||||
"updated_at": "2026-09-17T00:00:30Z",
|
||||
"retry_of": "22222222222222222222222222222222"
|
||||
},
|
||||
"audit_id": "88888888888888888888888888888888",
|
||||
"deduplicated": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
新任务保留原队列、处理器与输入版本、payload、business_key、concurrency_key、priority、recovery_safety和冻结重试策略;新ID、revision=1、attempt_count=0和新时间预算,`retry_of`指回原任务。原任务终态、版本、结果和尝试次数均保持不变。
|
||||
|
||||
每个原任务最多一个直接重试后继;不同键竞争时只有一个成功,其余409,继续人工重试应沿失败/取消的后继任务操作。同键重报返回同一子任务和audit_id。唯一索引 `(namespace_id,retry_of)` 兜底,关联存在与归属由事务校验,**无数据库外键**。
|
||||
|
||||
新任务占用原队列与应用额度,仍受背压/暂停/并发/限速/资源键控制;满额不创建子任务、不遗留幂等身份。人工声明不能替代业务幂等:业务操作身份仍须保持稳定,新taskID不能直接用作外部业务的去重身份。
|
||||
|
||||
`task.retry`审计记在原任务上,after包含retry_task_id;子任务的task.accepted事件包含retry_of和audit_id。任务详情、单项回执和Worker分配中的retry_of为新增可选字段;任务摘要列表没有该字段。
|
||||
|
||||
SDK:`RetryTask(ctx, taskID, key, orbit.RetryTaskRequest)` 返回 `RetryTaskResult`。
|
||||
|
||||
## 4. 查询不可变操作审计
|
||||
|
||||
权限 `tasks:audit:read`,与普通tasks:read分开;当前凭据与队列范围逐次校验,不返回其他任务的审计或秘密。
|
||||
|
||||
`GET /v1/tasks/{task_id}/operations?limit=20&cursor=...`:limit默认20,1–100,cursor最长1024;参数只能出现一次且不能传未知筛选。按(created_at DESC,id DESC)分页;游标绑定命名空间、任务和查询种类。不是跨页快照,新的并发操作需从首页重新查询。
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id":"44444444444444444444444444444444",
|
||||
"data":{
|
||||
"items":[{
|
||||
"id":"88888888888888888888888888888888",
|
||||
"task_id":"22222222222222222222222222222222",
|
||||
"operation_id":"99999999999999999999999999999999",
|
||||
"actor_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"action":"task.retry",
|
||||
"created_at":"2026-09-17T00:00:30Z"
|
||||
}],
|
||||
"next_cursor":"<服务端返回的不透明游标>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`GET /v1/tasks/{task_id}/operations/{audit_id}`:200详情在上述摘要基础上增加reason、可选evidence_ref、before和after;不存在或不属该任务返回404。
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id":"44444444444444444444444444444444",
|
||||
"data":{
|
||||
"id":"88888888888888888888888888888888",
|
||||
"task_id":"22222222222222222222222222222222",
|
||||
"operation_id":"99999999999999999999999999999999",
|
||||
"actor_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"action":"task.retry",
|
||||
"created_at":"2026-09-17T00:00:30Z",
|
||||
"reason":"已核实旧执行不会继续产生效果,允许重新执行",
|
||||
"evidence_ref":"evidence:safe-retry-001",
|
||||
"before":{"state":"FAILED","revision":"5"},
|
||||
"after":{
|
||||
"source_task_id":"22222222222222222222222222222222",
|
||||
"retry_task_id":"66666666666666666666666666666666",
|
||||
"safe_to_retry":true,
|
||||
"task_timeout_ms":60000
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
核查after包含state、revision、effect_uncertain=false、execution_settled=true及result_digest。摘要不复制完整业务输入;详情也不返回租约/凭据秘密。核查/重试的actor_id由实际认证凭据确定,不能在正文自报。审计可含受理、取消、执行结果等其他动作;查询操作记录不等于获得任务执行权。
|
||||
|
||||
SDK:`ListTaskOperations(ctx, taskID, HistoryPageRequest)`、`GetTaskOperation(ctx, taskID, auditID)`。
|
||||
|
||||
## 5. 错误与兼容
|
||||
|
||||
| HTTP/错误码 | 处理 |
|
||||
| --- | --- |
|
||||
| 400 INVALID_ARGUMENT | 修正字段、显式确认、预算、JSON或游标,不触及任务 |
|
||||
| 401 UNAUTHENTICATED | 核对凭据到期/撤销与所属应用启用情况 |
|
||||
| 403 PERMISSION_DENIED | 当前凭据未获该管理/审计权限 |
|
||||
| 404 NOT_FOUND | 任务/操作不存在,或命名空间/队列/任务归属不匹配 |
|
||||
| 409 STATE_CONFLICT | 状态/版本/所有者/门控不允许,或已有直接重试后继;先读当前状态和审计 |
|
||||
| 409 IDEMPOTENCY_CONFLICT | 同操作身份正文变化,核对已保存原请求 |
|
||||
| 429 QUEUE_FULL / APPLICATION_FULL | 新任务受理背压,释放额度后原请求重试;错误携带retry_after_ms提示 |
|
||||
| 503 UNAVAILABLE 或网络错误 | 操作可能已提交,保持原身份核对 |
|
||||
|
||||
完整通用错误边界与请求大小限制见[任务接口](tasks.md)。新接口调用旧服务返回404,不静默改成重新SubmitTask。旧SDK忽略retry_of响应字段;使用additionalProperties=false的旧版严格Schema验证器需更新Schema。需要恢复接口与关联追踪时升级当前SDK。Worker协议仍为v1,retry_of是可选新增字段。
|
||||
|
||||
迁移000015不新增模型字段:明确中文retry_of语义,将相同列的历史非唯一索引替换为唯一索引,并增加禁止自关联CHECK。停止旧写入节点后迁移并统一升级;迁移遇到历史非法重复或自关联应失败,不能自动删除数据。跨版本混合负载和生产滚动发布尚未验收。
|
||||
@@ -92,7 +92,7 @@ curl -i "$ORBIT_URL/v1/tasks" \
|
||||
}
|
||||
```
|
||||
|
||||
可选响应字段:start_deadline_at、result、cancel_requested_at、completed_at、business_key、concurrency_key,未设置时省略。effect_uncertain=true 表示存在尚未核实的授权执行效果,false 时省略;详情、摘要和 Worker 派发均可返回。等待重试时 result/completed_at 省略,历史结果在尝试中保留。终态有 completed_at。状态集合为 WAITING / LEASED / RUNNING / NEEDS_REVIEW / SUCCEEDED / FAILED / CANCELLED;P1 仅主动产生 WAITING 和 CANCELLED。
|
||||
可选响应字段:start_deadline_at、result、cancel_requested_at、completed_at、business_key、concurrency_key、retry_of,未设置时省略。effect_uncertain=true 表示存在尚未核实的授权执行效果,false 时省略;详情、摘要和 Worker 派发均可返回。retry_of仅在任务详情、受理回执和Worker派发中标识人工重试的原任务,列表摘要不包含。等待重试时 result/completed_at 省略,历史结果在尝试中保留。终态有 completed_at。状态集合为 WAITING / LEASED / RUNNING / NEEDS_REVIEW / SUCCEEDED / FAILED / CANCELLED;P1 仅主动产生 WAITING 和 CANCELLED。
|
||||
|
||||
### 幂等与失败处理
|
||||
|
||||
@@ -190,4 +190,4 @@ SDK:`Client.CancelTask(ctx, taskID, key, request)`。此操作不承诺回滚
|
||||
{"request_id":"44444444444444444444444444444444","error":{"code":"IDEMPOTENCY_CONFLICT","message":"idempotency key was used with different content"}}
|
||||
```
|
||||
|
||||
有限自动重试已实现,具体分类、退避、回执和升级边界见 [安全重试](retries.md)。[运行中取消](cancellation.md) 已实现;[批量受理](batch-tasks.md) 已实现逐项结果与原身份补交;独立任务人工重试仍待实现。[执行历史](execution-history.md) 已提供尝试与证据的列表和详情。完整可运行程序见 [SDK 任务示例](../../sdk/go/examples/tasks/main.go),已验证异常场景见 [P1 回归报告](../reports/phase-1.md) 及 [取消回归](../reports/phase-3-cancellation.md)。SDK 忽略未知响应字段;更改已有字段语义需升级契约和回归。
|
||||
有限自动重试已实现,具体分类、退避、回执和升级边界见 [安全重试](retries.md)。[运行中取消](cancellation.md) 已实现;[批量受理](batch-tasks.md) 已实现逐项结果与原身份补交;[独立任务核查与关联新任务重试](task-recovery.md) 已实现。[执行历史](execution-history.md) 已提供尝试与证据的列表和详情。完整可运行程序见 [SDK 任务示例](../../sdk/go/examples/tasks/main.go),已验证异常场景见 [P1 回归报告](../reports/phase-1.md) 及 [取消回归](../reports/phase-3-cancellation.md)。SDK 忽略未知响应字段;更改已有字段语义需升级契约和回归。
|
||||
|
||||
@@ -35,6 +35,7 @@ ACK 的 body 根据原请求选择:worker.capacity → welcome,worker.resume
|
||||
- hello.concurrency_keys 可选且默认 false,未声明时不派发带资源键任务;task.assign.task.concurrency_key 与非零字符串 concurrency_token 必须同时出现。Schema 检查字段配对,SDK 另核验支持声明和键格式,事务维护许可;见 [资源键契约](keyed-concurrency.md)。
|
||||
- finish.retryable 为可选布尔值,默认 false;true 仅允许 FAILED,Schema 与服务端都校验。派发任务可带 effect_uncertain=true,表示此前授权执行的效果尚未核实。业务重试条件见 [安全重试](retries.md)。
|
||||
- 返回时间使用 UTC RFC 3339,最多微秒精度。验证器应开启 format 断言以校验实际日历日期。
|
||||
- task.assign.task.retry_of为可选原任务ID,表示人工重试的直接来源;SDK校验合法ID及非自身关联。旧SDK忽略未知响应字段,使用旧版严格Schema的客户端需更新Schema;业务幂等身份不因新taskID变化,见[人工恢复](task-recovery.md)。
|
||||
- task.assign.task.priority 为可选 0–9 整数,零值省略;平台在实际到期后结合等待时间排序,详见 [优先级与公平派发](priority-and-fairness.md)。
|
||||
|
||||
P2 收紧了开发期此前允许的省略控制字段、非规范计数字符串和数组内字段别名。当前 SDK 一直发送完整规范字段,不需迁移;其他自研客户端应按本文件更新。SDK 尚未发布首个版本。将来扩展消息类型或改变字段语义必须同时更新 Schema、服务端、SDK 与兼容回归。
|
||||
|
||||
@@ -138,3 +138,11 @@
|
||||
- 预期:accepted=2、replayed=2、retried_items=1、unfinished=0。
|
||||
- 异常:逐项区分REJECTED/UNKNOWN/NOT_ATTEMPTED;真实HTTP响应丢失后恢复无重复,保留当前权限和额度控制。
|
||||
- 范围:示例只演示受理,不转换文件、不提供业务Outbox;失败非零退出,每项60秒总时限,保留队列及审计。
|
||||
|
||||
## 已可运行:独立任务人工核查与关联重试
|
||||
|
||||
- 源码:[recovery](../../sdk/go/examples/recovery/main.go),[完整运行与失败说明](recovery.md)。
|
||||
- 步骤:模拟UNKNOWN → 人工声明历史执行结束 → 核查FAILED → 原键核对 → 创建retry_of关联新任务 → 执行成功 → 查询原任务及操作审计。
|
||||
- 预期:原任务FAILED、新任务SUCCEEDED,原尝试和审计保留;管理凭据显式--recovery-access。
|
||||
- 异常:真实HTTP在核查/重试提交后丢响应,原身份恢复同一审计和新任务;迟到成功只进入证据。
|
||||
- 范围:本例不操作外部系统,不能替代真实证据核查、业务幂等或持久化管理操作Outbox。
|
||||
|
||||
77
docs/examples/recovery.md
Normal file
77
docs/examples/recovery.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# 未知结果的核查与人工重试示例
|
||||
|
||||
完整源码:[SDK recovery/main.go](../../sdk/go/examples/recovery/main.go)。只处理本地模拟对象,不调用任何真实外部业务系统。接口细节见[人工恢复手册](../api/task-recovery.md)。
|
||||
|
||||
## 准备与运行
|
||||
|
||||
从仓库根目录执行,ORBIT_MYSQL_DSN通过环境指向专用开发库;已有基础服务时使用当前迁移及二进制。初始化创建独立演示应用,输出文件必须不存在:
|
||||
|
||||
```sh
|
||||
go run ./cmd/orbit-migrate up
|
||||
mkdir -p .local
|
||||
chmod 700 .local
|
||||
go run ./cmd/orbit-admin bootstrap \
|
||||
--application recovery-demo --namespace default --environment development \
|
||||
--queue recovery --worker-access --recovery-access \
|
||||
--output .local/recovery.credentials.json
|
||||
go run ./cmd/orbit
|
||||
```
|
||||
|
||||
另开终端,从仓库根目录执行:
|
||||
|
||||
```sh
|
||||
cd sdk/go
|
||||
ORBIT_URL=http://127.0.0.1:8080 \
|
||||
ORBIT_CREDENTIALS_FILE=../../.local/recovery.credentials.json \
|
||||
go run ./examples/recovery
|
||||
```
|
||||
|
||||
演示凭据为了完整运行同时包含Worker与人工管理权限;生产将它们分开。凭据文件0600、token不输出;也接受ORBIT_TOKEN/ORBIT_QUEUE_ID,环境值优先。
|
||||
|
||||
## 实际调用链
|
||||
|
||||
1. 用独立处理器名提交一个对象引用,领取/开始后报告UNKNOWN,任务进入NEEDS_REVIEW。
|
||||
2. 模拟人工核实全部旧执行结束且操作未完成,带证据引用和版本ReviewTask,核查为FAILED。
|
||||
3. 原键重报核查,确认同audit_id和deduplicated=true。
|
||||
4. RetryTask声明可安全重新执行并指定新总预算60秒,取得retry_of指向原任务的新任务。
|
||||
5. Worker开始新任务、报告SUCCEEDED;查询原任务仍FAILED、新任务SUCCEEDED。
|
||||
6. 查询原任务操作列表及重试审计详情,排空并关闭Worker连接。
|
||||
|
||||
预期输出,标识由实际执行生成:
|
||||
|
||||
```text
|
||||
source=<原任务ID> state=FAILED retry=<新任务ID> state=SUCCEEDED audit=<审计ID> operations=4
|
||||
```
|
||||
|
||||
操作数包含本例受理、原结果、核查和人工重试;以本次实际查询为准。示例不删除任务、审计或尝试。重复运行创建独立演示任务,每次保留原历史。
|
||||
|
||||
## 接入片段
|
||||
|
||||
下方在已有客户端和已核实的current任务上下文中调用;完整imports/main及错误处理见源码。
|
||||
|
||||
```go
|
||||
review, err := client.ReviewTask(ctx, current.ID, reviewKey, orbit.ReviewTaskRequest{
|
||||
ExpectedRevision: current.Revision,
|
||||
Reason: "已核实全部旧执行结束,操作未完成",
|
||||
EvidenceRef: "evidence:reconciliation-001",
|
||||
ExecutionSettled: true,
|
||||
Outcome: orbit.TaskFailed,
|
||||
Result: json.RawMessage(`{"confirmed":true}`),
|
||||
})
|
||||
if err != nil { return err }
|
||||
retry, err := client.RetryTask(ctx, current.ID, retryKey, orbit.RetryTaskRequest{
|
||||
ExpectedRevision: review.Task.Revision,
|
||||
Reason: "同一业务操作可以安全重新执行",
|
||||
EvidenceRef: "evidence:safe-retry-001",
|
||||
SafeToRetry: true,
|
||||
TaskTimeoutMS: 60000,
|
||||
})
|
||||
if err != nil { return err }
|
||||
fmt.Println(retry.Task.ID, *retry.Task.RetryOf, retry.AuditID)
|
||||
```
|
||||
|
||||
生产先持久化每次操作的key与完整请求,再发HTTP;响应丢失必须用原键、原版本、原结论重报。不能将同键回执中的新revision塞回原请求,不能看到UNKNOWN就直接RetryTask,也不能让审核按钮默认勾选“旧执行已结束”。
|
||||
|
||||
本程序为了清楚展示调用而使用单连接协议SDK及20秒总运行上限;错误非零退出,不包含持久化管理操作Outbox。失败后的任务须查询状态/历史再处理,60秒时限不意味着不确定效果会自动变成失败。持续Worker运行使用[自动Worker SDK](../sdk/worker-runtime.md)。
|
||||
|
||||
`make integration` 在隔离MySQL中运行本例及真实HTTP故障:核查和重试都在服务端提交后丢失响应,再原身份核对同一审计和子任务;旧Worker迟到成功只进入证据。外部证据的真实性与物理停止仍由管理流程核实。
|
||||
85
docs/reports/phase-3-task-recovery.md
Normal file
85
docs/reports/phase-3-task-recovery.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# P3 人工核查、关联重试与操作审计回归
|
||||
|
||||
验证日期:2026-09-17。服务端、SDK、接口及示例已实现,完整回归已通过,脚本终态退出码0。P3剩余查询筛选与事务Outbox接入继续推进。
|
||||
|
||||
## 1. 实现范围
|
||||
|
||||
- [服务契约](../../pulse/service/recovery.go):显式历史执行结束/安全重试声明、原因、证据、版本及预算校验。
|
||||
- [数据库事务](../../internal/storage/mysql/task_recovery.go):NEEDS_REVIEW核查为SUCCEEDED/FAILED,原尝试保留;独立FAILED/CANCELLED任务生成retry_of关联后继,原任务不改写。
|
||||
- [审计查询](../../internal/storage/mysql/task_operations.go):专门权限与父资源范围,摘要游标分页、详情原因和证据,真实凭据身份。
|
||||
- [Go SDK](../../sdk/go/recovery.go)、[接口手册](../api/task-recovery.md)、[完整运行场景](../examples/recovery.md)及[源码](../../sdk/go/examples/recovery/main.go)。
|
||||
- [迁移000015](../../internal/storage/mysql/migrations/000015_task_manual_recovery.up.sql):现有RetryOf字段中文语义、同父后继唯一索引与禁止自身关联CHECK。35个模型、289个持久化字段,无新增字段,无外键/级联。
|
||||
|
||||
## 2. 已覆盖的数据库与协议场景
|
||||
|
||||
[真实MySQL测试](../../internal/storage/mysql/task_recovery_integration_test.go)包含八组场景:
|
||||
|
||||
1. 核查保存新权威结果、保留旧尝试,释放一次额度;同键重报原audit_id,旧needs_review快照不改写,新终态单独交付,迟到成功只记录证据。
|
||||
2. 普通凭据不能核查或读取管理审计;显式授权后可用。跨命名空间/错误父任务拒绝,游标绑定任务/种类;撤销队列范围后原回执也不能绕过权限。
|
||||
3. 审计INSERT触发器故障整体回滚核查、额度、幂等及新结果事件;两个不同键竞争同一版本仅一个成功。
|
||||
4. 未核查任务不能重试,队列满额不残留身份/子任务;释放后原键成功,新任务新预算、原载荷/冻结策略,原任务不变;迟到报告不覆盖核查。
|
||||
5. 人工重试审计故障回滚新任务及额度;八个不同键竞争同一父任务仅一个直接后继和一条task.retry审计。
|
||||
6. 真实串行计划因未知结果停止推进;合法归属核查后可推进下一触发,但计划任务不允许独立人工重试。
|
||||
7. 等待、运行、成功、带门控或无效计划关联的恢复均拒绝,不能借无外键设计绕过所有权。
|
||||
8. 流程所属任务拒绝直接核查,归属恢复后普通任务可核查;计划任务拒绝独立派生。
|
||||
|
||||
[HTTP输入边界](../../internal/transport/httpserver/recovery_test.go)与[服务参数边界](../../pulse/service/recovery_test.go)覆盖缺失/null/false声明、未知actor_id/owner_id、重复JSON键、非规范revision、证据空白/长度、结论/结果/预算限制。
|
||||
|
||||
[SDK测试](../../sdk/go/recovery_test.go)核验原幂等头、字符串版本精度、审计游标编码、非法路径拒绝、矛盾核查回执及缺少重试来源时拒绝确认成功。
|
||||
|
||||
## 3. 无外键与迁移回归
|
||||
|
||||
真实v7→当前:70个历史外键全部删除,旧任务数据不变;323个未替换索引组件保留,单独核验(namespace_id,retry_of)新唯一索引。新装库外键为0。一次性库注入违规外键后启动结构检查拒绝就绪,移除后恢复。
|
||||
|
||||
本轮初次升级测试发现MySQL在新增同列唯一索引时会撤去原隐式非唯一索引fk_pulse_tasks_3。新迁移明确用唯一索引替换该冗余索引,测试分别核验替换对象和其他索引,避免将有意替换误报为数据损坏;没有增加数据库外键。
|
||||
|
||||
全量回归还发现旧v9升级夹具没有撤除新迁移15的约束,导致重放时重复CHECK名称;已修正一次性库夹具的还原步骤,保留真实生产迁移内容和校验和规则。
|
||||
|
||||
## 4. 环境与复现
|
||||
|
||||
macOS、Go1.27.1、Docker单实例MySQL8.4.10,隔离临时数据库与回环端口,结束自动清理。
|
||||
|
||||
```sh
|
||||
make fmt test vet build
|
||||
make integration
|
||||
```
|
||||
|
||||
完整脚本为人工恢复单独初始化--worker-access与--recovery-access凭据,普通Worker测试凭据不扩权。集成SDK通过[真实HTTP代理](../../sdk/go/recovery_integration_test.go)在核查和重试提交后丢弃响应,再原身份核对;并在真实WSS派发中验证retry_of。
|
||||
|
||||
| 验证组 | 本轮已取得的结果 |
|
||||
| --- | --- |
|
||||
| fmt / race / vet / build | PASS |
|
||||
| 人工恢复八组数据库用例 | PASS,5.32秒 |
|
||||
| v7无外键升级 / 全量MySQL集成 | PASS,4.85 / 104.575秒 |
|
||||
| SDK Worker完整组 | PASS,87.918秒 |
|
||||
| SDK计划 / 队列 / 批量 | PASS,5.659 / 4.536 / 1.366秒 |
|
||||
| SDK订阅管理 / 事件消费 | PASS,33.379 / 51.798秒 |
|
||||
| 新人工恢复SDK及两次HTTP丢回执 | PASS,3.371秒(含包开销) |
|
||||
| Inbox六组真实数据库与消费者故障 | PASS,35.606秒 |
|
||||
| SDK应用配额 | PASS,1.357秒 |
|
||||
| 九项节点/Worker进程故障 | PASS,60.644秒;连同Inbox消费者为十项真实进程故障 |
|
||||
| 全部运行示例及脚本终态 | 16个核心SDK示例 + 1个独立Inbox示例通过,完整脚本正常退出0 |
|
||||
|
||||
新人工恢复完整示例已通过;核心SDK依赖图未包含服务端包或MySQL驱动,文档575个本地链接、45个JSON块检查通过。最终结果取自已经结束的进程,未将中途日志当作全量通过。单次耗时仅为回归记录,不作为性能指标。
|
||||
|
||||
## 5. 实际HTTP响应丢失与运行结果
|
||||
|
||||
SDK集成代理先完整收到服务端成功响应,再主动关闭客户端连接。核查和人工重试各丢一次响应;测试关闭HTTP连接复用,确保不会因传输层隐式重发掩盖首次网络错误。原键重报后分别恢复同一audit_id和同一子任务,审计分页确认仅一次task.review和一次task.retry。
|
||||
|
||||
WSS接收的新任务带有正确retry_of,执行成功后原任务仍FAILED。旧Worker再报成功时返回EVIDENCE_ONLY,原任务版本/结果不变;原执行尝试也未被核查改写。
|
||||
|
||||
完整示例实际输出模式:
|
||||
|
||||
```text
|
||||
source=<原任务ID> state=FAILED retry=<新任务ID> state=SUCCEEDED audit=<审计ID> operations=4
|
||||
```
|
||||
|
||||
本例四条审计对应受理、原结果、核查与人工重试,开始事件本身不额外增加一条管理审计。没有自动删除历史。
|
||||
|
||||
## 6. 生产边界
|
||||
|
||||
- 人工执行结束与安全重试是获授权主体基于外部证据的声明;平台不会自动证明外部物理停止或恰好一次。旧迟到报告保留证据,不重新打开已核查终态。
|
||||
- 计划生成任务可核查但不能独立人工重试;流程所属任务/门控恢复留Weave控制器,不声称本阶段已实现流程恢复。
|
||||
- 独立新任务仍受原应用/队列配额、冻结自动重试策略和执行权限约束;业务幂等身份必须沿原操作保持,不能用新taskID盲目调用外部接口。
|
||||
- 任务详情和派发新增可选retry_of;旧SDK忽略未知响应字段,旧严格Schema需更新。迁移前停旧写入节点,不回改历史迁移,不自动清理非法历史关联。
|
||||
- 本机正确性/进程故障不等于生产多故障域高可用、MySQL切换或百万租户容量验收;P4–P7仍按持续目标推进。
|
||||
@@ -1,6 +1,6 @@
|
||||
# Go SDK 接入说明
|
||||
|
||||
已实现健康查询、任务单项与[批量提交](../api/batch-tasks.md)/查询/分页/待执行取消、队列创建/查询、[自动 Worker](worker-runtime.md)、[Worker 底层协议 SDK](worker.md) 和 [执行历史查询](../api/execution-history.md)。[安全重试](../api/retries.md) 已提供有限策略、WorkerFailure.Retryable、历史查询及完整例子。[计划调度](../api/schedules.md) 已支持创建、查询、版本发布、暂停/恢复、触发历史与完整示例。[订阅管理](../api/subscriptions.md) 已支持创建、查询、暂停恢复、交付历史与人工重试;[事件消费 SDK](event-consumer.md) 已支持连接、自动心跳、重连与确认。流程构建等后续接口按 [开发计划](../08-development-plan.md) 继续实现。
|
||||
已实现健康查询、任务单项与[批量提交](../api/batch-tasks.md)/查询/分页/待执行取消、队列创建/查询、[自动 Worker](worker-runtime.md)、[Worker 底层协议 SDK](worker.md) 和 [执行历史查询](../api/execution-history.md)。[人工核查与重试](../api/task-recovery.md) 已提供 ReviewTask、RetryTask 与任务操作审计查询。[安全重试](../api/retries.md) 已提供有限策略、WorkerFailure.Retryable、历史查询及完整例子。[计划调度](../api/schedules.md) 已支持创建、查询、版本发布、暂停/恢复、触发历史与完整示例。[订阅管理](../api/subscriptions.md) 已支持创建、查询、暂停恢复、交付历史与人工重试;[事件消费 SDK](event-consumer.md) 已支持连接、自动心跳、重连与确认。流程构建等后续接口按 [开发计划](../08-development-plan.md) 继续实现。
|
||||
|
||||
module:`git.yuntuo.hk/k2safe/orbit/sdk/go`。SDK 使用独立 go.mod,依赖 Go 标准库与 `github.com/coder/websocket v1.8.15`,不引用服务端模型或数据库实现。
|
||||
|
||||
@@ -105,6 +105,8 @@ fmt.Println(cancelled.Task.State)
|
||||
|
||||
| 方法 | 请求 / 返回 | 文档 |
|
||||
| --- | --- | --- |
|
||||
| ReviewTask / RetryTask | 原任务ID、稳定key、显式声明、证据与版本;返回任务及audit_id | [人工恢复](../api/task-recovery.md) / [完整场景](../examples/recovery.md) |
|
||||
| ListTaskOperations / GetTaskOperation | 操作摘要分页、原因、证据与前后状态 | [任务审计](../api/task-recovery.md) |
|
||||
| SubmitTasks | SubmitTasksRequest / SubmitTasksResult,32项独立幂等与结果 | [批量受理](../api/batch-tasks.md) / [运行场景](../examples/batch.md) |
|
||||
| CreateSubscription / GetSubscription / ListSubscriptions | 订阅受理、当前配置与队列分页 | [订阅](../api/subscriptions.md) |
|
||||
| ConfigureSubscription | 显式暂停/继续与版本、原因 | [订阅](../api/subscriptions.md) |
|
||||
@@ -185,7 +187,7 @@ task=<任务ID> state=SUCCEEDED attempts=1 evidence=1 reason=RESULT_CONFLICT dis
|
||||
|
||||
## 7. 后续交付
|
||||
|
||||
P2 已提供底层协议、[自动 Worker 运行器及示例](worker-runtime.md) 与历史查询,并通过 [Schema 及进程故障阶段验收](../reports/phase-2.md);P3 已实现 [安全重试](../api/retries.md)、[计划调度](../api/schedules.md)、[运行中取消](../api/cancellation.md) 、[队列资源控制及示例](../api/queues.md) 和 [应用共享配额及示例](../api/application-resources.md),[资源键互斥和单调序号示例](../api/keyed-concurrency.md) 已实现,[优先级场景和节点公平派发](../api/priority-and-fairness.md) 已实现,结果订阅继续开发;P4–P5 提供流程构建发布、信号、等待、轮询与人工恢复。每项随实现同步交付可运行示例及异常回归。
|
||||
P2 已提供底层协议、[自动 Worker 运行器及示例](worker-runtime.md) 与历史查询,并通过 [Schema 及进程故障阶段验收](../reports/phase-2.md);P3 已实现 [安全重试](../api/retries.md)、[计划调度](../api/schedules.md)、[运行中取消](../api/cancellation.md) 、[队列资源控制及示例](../api/queues.md) 和 [应用共享配额及示例](../api/application-resources.md),[资源键互斥和单调序号示例](../api/keyed-concurrency.md) 已实现,[优先级场景和节点公平派发](../api/priority-and-fairness.md) 已实现,结果订阅及消费SDK、独立Inbox场景已实现,人工核查与重试也已提供完整示例;P4–P5 提供流程构建发布、信号、等待、轮询与人工恢复。每项随实现同步交付可运行示例及异常回归。
|
||||
|
||||
## 订阅管理示例
|
||||
|
||||
|
||||
Reference in New Issue
Block a user