feat: add durable safe retries with uncertainty tracking and SDK examples
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Go SDK 接入说明
|
||||
|
||||
已实现健康查询、任务提交/查询/分页/待执行取消、队列创建/查询、[自动 Worker](worker-runtime.md)、[Worker 底层协议 SDK](worker.md) 和 [执行历史查询](../api/execution-history.md)。流程构建等后续接口按 [开发计划](../08-development-plan.md) 继续实现。
|
||||
已实现健康查询、任务提交/查询/分页/待执行取消、队列创建/查询、[自动 Worker](worker-runtime.md)、[Worker 底层协议 SDK](worker.md) 和 [执行历史查询](../api/execution-history.md)。[安全重试](../api/retries.md) 已提供有限策略、WorkerFailure.Retryable、历史查询及完整例子。流程构建等后续接口按 [开发计划](../08-development-plan.md) 继续实现。
|
||||
|
||||
module:`git.yuntuo.hk/k2safe/orbit/sdk/go`。SDK 使用独立 go.mod,依赖 Go 标准库与 `github.com/coder/websocket v1.8.15`,不引用服务端模型或数据库实现。
|
||||
|
||||
@@ -97,7 +97,7 @@ if err != nil {
|
||||
fmt.Println(cancelled.Task.State)
|
||||
```
|
||||
|
||||
取消重报必须保留第一次取消时的 expected_revision,不能将响应中的新版本塞回同一幂等请求。当前只取消 WAITING 且不归属流程的任务。
|
||||
取消重报必须保留第一次取消时的 expected_revision,不能将响应中的新版本塞回同一幂等请求。当前只取消 WAITING 且不归属流程的任务。若此前存在未知效果,取消结果为 NEEDS_REVIEW,不能解释为已撤销外部效果。
|
||||
|
||||
业务错误返回 *orbit.APIError,支持 errors.As 转为 *orbit.HTTPError 查看状态。健康错误或无法识别的网关错误使用 *orbit.HTTPError,不带未知响应正文。网络/上下文错误按标准库错误返回;超时并不能证明服务端没有提交。
|
||||
|
||||
@@ -176,4 +176,4 @@ task=<任务ID> state=SUCCEEDED attempts=1 evidence=1 reason=RESULT_CONFLICT dis
|
||||
|
||||
## 7. 后续交付
|
||||
|
||||
P2 已提供底层协议、[自动 Worker 运行器及示例](worker-runtime.md) 与历史查询,并通过 [Schema 及进程故障阶段验收](../reports/phase-2.md);P3 提供安全重试、计划与结果订阅;P4–P5 提供流程构建发布、信号、等待、轮询与人工恢复。每项随实现同步交付可运行示例及异常回归。
|
||||
P2 已提供底层协议、[自动 Worker 运行器及示例](worker-runtime.md) 与历史查询,并通过 [Schema 及进程故障阶段验收](../reports/phase-2.md);P3 已实现 [安全重试与示例](../api/retries.md),计划和结果订阅继续开发;P4–P5 提供流程构建发布、信号、等待、轮询与人工恢复。每项随实现同步交付可运行示例及异常回归。
|
||||
|
||||
@@ -65,7 +65,7 @@ SDK 的 Run 每个实例只能调用一次,包括已经正常退出的实例
|
||||
| ReconnectMinDelay | 200 毫秒;至少 10 毫秒 | 连接失败退避起点 |
|
||||
| ReconnectMaxDelay | 5 秒;不小于起点,最多 30 秒 | 指数退避上限,区间抖动;不增加任务尝试次数 |
|
||||
|
||||
`Run(ctx)` 正常排空返回 nil;同实例再次运行返回 ErrWorkerAlreadyRun。`Stats()` 返回进程内 Running、Started、RenewalsConfirmed、ReportsConfirmed、EvidenceConfirmed、Reconnects、DroppedEvents 快照,不是持久化业务成功率。
|
||||
`Run(ctx)` 正常排空返回 nil;同实例再次运行返回 ErrWorkerAlreadyRun。`Stats()` 返回进程内 Running、Started、RenewalsConfirmed、ReportsConfirmed、EvidenceConfirmed、Reconnects、DroppedEvents 快照,不是持久化业务成功率。ReportsConfirmed 包含已确认的失败/未知尝试报告,一条重试任务可能计多次。
|
||||
|
||||
`Events()` 是 128 条缓冲的只读本地通知流,不读取也不会阻塞运行;缓冲满时丢弃并计数,Run 返回时关闭。通知只含类型、任务/尝试 ID 与稳定分类,没有业务结果或秘密。它不能代替审计、可靠结果订阅或任务查询。
|
||||
|
||||
@@ -78,15 +78,16 @@ SDK 的 Run 每个实例只能调用一次,包括已经正常退出的实例
|
||||
- 重连复用实例和尝试身份,在开启容量之前 Resume 所有已知尝试;待确认 start/renew/finish 使用原消息 ID。认证、权限及协议错误终止 Run;临时服务/连接错误按退避核对。
|
||||
- 结果未确认或处理器失去租约时,运行器降低对外声明的容量。仍未返回的函数占用真实并发槽;已在传输中的预占会有界接收,已知尝试最多为配置容量的两倍。
|
||||
|
||||
Run 的重连是控制消息恢复,不是业务自动重试。当前明确失败仍成为 FAILED;策略化业务重试在 P3 实现。
|
||||
Run 的重连恢复同一次控制操作。平台的 [安全重试](../api/retries.md) 使用同一 Task.ID 派发新的 AttemptID/Epoch,运行器按新的有效授权调用处理器,预算与退避由服务端持久化管理。
|
||||
|
||||
## 5. 结果与错误分类
|
||||
|
||||
| 处理器结果 | SDK 报告 / 服务端行为 |
|
||||
| --- | --- |
|
||||
| 合法非 null JSON + nil error | SUCCEEDED |
|
||||
| `&orbit.WorkerFailure{Code:"OBJECT_REJECTED", Details:...}` | FAILED;仅用于能确认的业务失败,Details 为空时为 `{}` |
|
||||
| 普通 error | UNKNOWN / HANDLER_ERROR,进入 NEEDS_REVIEW |
|
||||
| `&orbit.WorkerFailure{Code:"OBJECT_REJECTED", Details:...}` | FAILED;默认 Retryable=false,Details 为空时为 `{}` |
|
||||
| `&orbit.WorkerFailure{Code:"TEMPORARY_REJECTION", Retryable:true}` | 明确失败可安全重试,平台仍核验次数、总时限和历史不确定效果 |
|
||||
| 普通 error | UNKNOWN / HANDLER_ERROR;默认转核查,仅 IDEMPOTENT 且预算允许时可自动重试 |
|
||||
| panic / runtime.Goexit | UNKNOWN / HANDLER_PANIC 或 HANDLER_ABORTED |
|
||||
| 非法 JSON、超限结果、不支持数值或嵌套 | UNKNOWN / INVALID_HANDLER_RESULT |
|
||||
| 处理器因租约取消返回错误 | UNKNOWN / LEASE_LOST |
|
||||
@@ -94,7 +95,7 @@ Run 的重连是控制消息恢复,不是业务自动重试。当前明确失
|
||||
|
||||
原始异常和 panic 内容不写入结果或通知。Result/Details 最多 256 KiB,应优先使用对象引用。SDK 保留精确数字文本,并按服务端数字范围及完整帧的嵌套上限预检,避免产生永远无法接受的报告。
|
||||
|
||||
普通网络错误不能证明副作用未发生,因此不会自动解释为明确失败。UNKNOWN 和迟到证据的后续核查由平台管理能力与业务对账共同处理。
|
||||
普通网络错误不能证明副作用未发生,因此不会自动解释为明确失败。UNKNOWN 由服务端结合真实幂等声明决定是否重试;迟到证据不能清除历史不确定效果。完整规则及可运行示例见 [安全重试](../api/retries.md)。
|
||||
|
||||
## 6. 退出与故障边界
|
||||
|
||||
@@ -102,7 +103,7 @@ Run 的重连是控制消息恢复,不是业务自动重试。当前明确失
|
||||
|
||||
ShutdownTimeout 从取消信号发生时开始计时,包含网络等待和处理器收尾。到期后取消控制请求及处理器 context,返回 `*WorkerRunError`;可用 `errors.Is(err, orbit.ErrWorkerShutdownTimeout)` 判断,Unconfirmed 和 Running 给出终止收尾时的快照。其他致命错误也带这些计数,便于识别未核对的执行。
|
||||
|
||||
Go 无法杀死忽略 context 的 goroutine。Run 可以有界返回,但这类函数可能仍在进程中执行;不得把返回错误解释为副作用已停止,也不要在同一进程盲目重建 Worker 叠加执行。服务端对失联执行按租约回收并进入核查。
|
||||
Go 无法杀死忽略 context 的 goroutine。Run 可以有界返回,但这类函数可能仍在进程中执行;不得把返回错误解释为副作用已停止,也不要在同一进程盲目重建 Worker 叠加执行。服务端对失联执行按租约回收,依冻结安全策略决定重试或进入核查。
|
||||
|
||||
身份、尚未确认的结果和消息 ID 当前保存在内存,支持同进程重连;进程被强杀会失去这些内容。持久化租约、操作回执、最终结果和审计在 MySQL 中,业务外部效果仍需要业务幂等或查询核对。这一边界不等于端到端 exactly-once。
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ task=<32位任务ID> final_state=SUCCEEDED attempts=1 result={"text":"HELLO ORBI
|
||||
|
||||
[完整源码](../../sdk/go/examples/worker-protocol/main.go) 实际提交 `{"text":" Hello Orbit "}`,领取并取得开始授权,执行去首尾空格/转大写,报告结果、查询当前状态并排空。没有生成文件或调用外部服务。每次运行创建独立任务与临时处理器名,避免消费前次示例遗留任务;历史和审计保留,不硬删除。
|
||||
|
||||
安全重试可通过 `WorkerFinish.Retryable=true` 声明明确失败允许再试,仅适用于 WorkerFailed;省略为 false。新尝试由服务端决定,详见 [安全重试](../api/retries.md)。
|
||||
|
||||
示例最多对开始/结果操作重连重报两次,保持原进程身份、尝试证明、消息 ID 和内容;业务函数只在首次取得有效开始确认后调用一次。网络结果仍不明时非零退出,不能推断服务端回滚。身份与待确认结果只保留在当前进程内,进程被终止后的未知结果由服务端回收进入核查。本示例处理很短的纯计算,不演示长任务自动续租。
|
||||
|
||||
## 2. 创建进程身份与连接
|
||||
@@ -105,7 +107,7 @@ defer cancel()
|
||||
| Finish 请求结果不明 | 保留原 finishID、Outcome、Result、ErrorCode,重连核对后原样重报 |
|
||||
| 明确业务成功 | `WorkerSucceeded`,不带 ErrorCode |
|
||||
| 可确认的业务失败 | `WorkerFailed`,带稳定 ErrorCode |
|
||||
| 超时、失联或不能确认外部效果 | `WorkerUnknown`,带稳定 ErrorCode,进入核查 |
|
||||
| 超时、失联或不能确认外部效果 | `WorkerUnknown`,带稳定 ErrorCode;默认转核查,仅 IDEMPOTENT 且预算允许时自动重试 |
|
||||
| `Disposition=EVIDENCE_ONLY` | 内容已留作证据;不能当作任务成功,另查 GetTask 的权威状态 |
|
||||
|
||||
Result 是不超过 256 KiB 的非 null JSON,可传对象引用;原始异常不应写入稳定错误分类。SDK 拒绝非法 Unicode、重复键和过深 JSON,保留数字文本;数值范围仍由服务端最终校验。
|
||||
|
||||
Reference in New Issue
Block a user