# 任务取消与结果确认 状态:已实现 HTTP 取消意图、开始前取消、WSS 通知、重连核对及 Go Worker 自动停止。取消记录、审计、事件和回执使用 MySQL 事务;运行结果仍以有效尝试的确认或到期核查为准。 ## 1. HTTP 接口 `POST /v1/tasks/{task_id}:cancel`。需要当前 `tasks:cancel` 动作和目标队列权限;Bearer、JSON 和 Idempotency-Key 规则同 [任务接口](tasks.md)。适用于普通任务和计划生成任务;流程拥有的任务由流程控制管理,此入口返回 409。 ```json {"expected_revision":"3","reason":"用户终止本次处理"} ``` | 当前状态 | 提交后行为 | HTTP 状态 | | --- | --- | --- | | WAITING | 阻止后续尝试,通常成为 CANCELLED | 200 | | LEASED | 与开始事务竞争;尚未授权时结束尝试并释放许可 | 200 | | RUNNING | 保持 RUNNING,记录 cancel_requested_at;许可暂不释放 | 202 | | NEEDS_REVIEW | 保持核查状态,保存取消意图,不清除未知效果 | 200 | | 已有取消意图或终态 | 新操作身份返回 STATE_CONFLICT;原键仍返回首次回执 | 409 或原回执状态 | 成功的 data 为 `{"task":<操作提交时的完整任务快照>,"deduplicated":false}`,与 CancelTaskResult 一致。202 只表示取消意图已经可靠保存,不能解释为处理器已经停止。 WAITING/LEASED 若承接先前未知效果,取消后进入 NEEDS_REVIEW,保留 effect_uncertain=true 和空 completed_at。本次未开始不能证明此前尝试没有产生效果。 请求结果不明时,保留原键、原 expected_revision 和原 reason 重报。只有明确收到 STATE_CONFLICT 后,才读取新状态并创建新的管理操作;任务的续租也会更新 revision。已保存的 RUNNING 取消回执即使任务后来结束,重报仍是原快照和 202,当前状态用 GET 查询。 取消与开始竞争有两种有效结果:取消先提交则 Start 不获授权;Start 先提交则旧 expected_revision 冲突,重新查询后可记录运行中取消。权限、名称空间、幂等回执、版本和审计都在事务内核对。 ## 2. Worker 协议 Hello 可选 `cancellation_notifications:true` 表示接收异步通知。低层 SDK 的 WorkerRegistration 默认为 false,自动 NewWorker 显式开启。未开启的连接不会收到新通知类型。 服务端通知示例,session_id 是当前连接,消息没有 reply_to: ```json { "protocol_version":"1", "type":"task.cancel", "message_id":"11111111111111111111111111111111", "session_id":"22222222222222222222222222222222", "body":{ "task_id":"33333333333333333333333333333333", "attempt_id":"44444444444444444444444444444444", "epoch":"1", "requested_at":"2026-09-17T08:00:00.123456Z" } } ``` 每次连接按数据库事实扫描当前拥有的运行尝试,通常约一秒发现取消,实际延迟受数据库和网络影响。通知允许重复、迟到、断线丢失,只作用于 TaskID/AttemptID/Epoch 全部匹配的尝试。单次读取最多 256 条,连接发送与 SDK 接收缓冲有界;慢消费者断开后重新核对。 Start/Renew/Resume 返回的当前 Lease 也包含可选 `cancel_requested_at`;即使原 Start 回执是已批准执行,当前 Lease 中的取消意图也不能忽略。Active=true 表示原尝试仍可在有效期内提交权威结果,**有取消意图时不得继续业务处理**。SDK 的 ExecutionDeadline 在此情况下返回 false。 取消后 Renew 不延长任何执行期限。处理器应尽快停止并提交已知结果;原租约或尝试截止到期后,未确认效果转 UNKNOWN/NEEDS_REVIEW,禁止因任务幂等声明自动再试。服务停止、通知丢失或 Worker 不配合,都不会自动成为 CANCELLED。 结果继续使用 `task.finish`,新增 outcome=CANCELLED;error_code 必填稳定分类,retryable 不得为 true,result 是非 null JSON 或核查证据引用。不存在已保存取消意图时,此结果被拒绝。 | 有效尝试上报 | 任务结果 | | --- | --- | | SUCCEEDED | SUCCEEDED,成功不被取消意图覆盖 | | FAILED | FAILED,停止后续自动重试;先前效果未知则仍需核查 | | UNKNOWN | NEEDS_REVIEW,不能把 context 错误当作撤销证明 | | CANCELLED | 本次确认停止且无遗留效果;若此前存在未知效果,仍为 NEEDS_REVIEW | | 租约已失效或结果冲突 | 追加 EVIDENCE_ONLY,不覆盖权威状态 | 新版要求先停用旧服务、应用全部迁移(000007 扩展取消证据,000008 移除外键),再启动新版服务,最后升级自动 Worker。旧服务会拒绝新增 Hello 字段或 CANCELLED 结果;不能将带新能力的连接随机路由至旧节点。旧低层客户端可以继续原协议,但必须升级后才能依赖自动取消通知;取消后的续租不延长期限,同样适用于旧客户端。数据库升级细节见 [数据模型](../07-data-model.md)。 ## 3. Go SDK 接入 自动 Worker 将业务 context 的 cause 设置为 `orbit.ErrTaskCancellationRequested`。仅当接入方能确认本次已停止且无未结清效果时返回 `*orbit.WorkerCancellationConfirmed`: ```go <-ctx.Done() if !errors.Is(context.Cause(ctx), orbit.ErrTaskCancellationRequested) { return nil, ctx.Err() } // 先完成本次外部操作核实;此处的类型是业务声明,不替代核实。 return nil, &orbit.WorkerCancellationConfirmed{ Details: json.RawMessage(`{"evidence_ref":"objects/stopped"}`), } ``` 普通 ctx.Err()、panic、网络错误和无效结果仍属于 UNKNOWN。处理器已经成功时返回真实结果,即使此时 context 也被取消,平台仍按有效结果确认。开始批准已提交但 ACK 丢失时,SDK 重连先检查取消意图;如果处理器尚未被调用,可以明确报告本次没有副作用,禁止重启处理器。 低层接口设置 WorkerRegistration.CancellationNotifications 后,使用 NextCancellation 持续读取,并同时检查每次 Start/Renew/Resume 的 Lease。NextCancellation 的等待 context 取消只停止等待,不关闭连接。取消响应丢失后的 Finish 重报必须保留相同消息身份和结果。 任务取消不等同于取消 Run context:前者停止指定业务处理器,后者要求整个 Worker 排空。无法强杀不配合 context 的 goroutine;它仍占本地物理槽,ShutdownTimeout 到期会报告未收尾数量。 ## 4. 可运行场景 源码:[cancellation/main.go](../../sdk/go/examples/cancellation/main.go)。使用 [Worker 初始化](../sdk/worker-runtime.md) 的私有凭据文件,需 tasks:submit/read/cancel、workers:connect/consume 和队列范围。此示例只等待停止提示,没有外部业务副作用。 ```sh cd sdk/go ORBIT_URL=http://127.0.0.1:8080 \ ORBIT_CREDENTIALS_FILE=/private/path/worker.credentials.json \ go run ./examples/cancellation ``` 预期输出: ```text task=<任务ID> cancel_requested=true accepted_state=RUNNING final_state=CANCELLED attempts=1 worker_drained=true running=0 ``` 每次运行使用新任务/处理器身份;正常执行保留任务、尝试和审计记录,不删除历史。错误时非零退出并尝试有界收尾。网络结果不明时示例停止,真实集成应保存原幂等身份以便核对。