feat: add reliable event consumer SDK and process recovery tests
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 数据模型与实现基线
|
||||
|
||||
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度、队列限速/背压和应用配额;优先级及节点公平领取已实现;可靠订阅核心、HTTP/WSS 与管理 SDK 已实现,消费 SDK 及流程编排继续开发。
|
||||
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度、队列限速/背压和应用配额;优先级及节点公平领取已实现;可靠订阅核心、HTTP/WSS、管理与消费 SDK 已实现,Inbox 场景及流程编排继续开发。
|
||||
|
||||
## 1. 模块与依赖
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
|
||||
禁止“先查后另开事务写入”。需要防止并发状态改变的关联行在事务内持锁,统一锁顺序并保留必要索引。当前不提供硬删除;将来增加清理时,必须核验在途任务、引用、幂等恢复窗口与审计保留期,按有限批次处理,不能依赖数据库级联。直接绕过服务写 SQL 不受应用校验保护。
|
||||
|
||||
P1 已完成动作/队列授权、幂等摘要与原回执、受理事件及取消 CAS/审计事务;P3 扩展到运行中取消。订阅已实现服务层及持久化核心,包含事务内队列、所有者和交付归属校验;HTTP/WSS 和管理 SDK 已接入,消费 SDK 待完成。流程目前仍只具备模型,后续引擎也必须补齐事务内父资源归属验证。
|
||||
P1 已完成动作/队列授权、幂等摘要与原回执、受理事件及取消 CAS/审计事务;P3 扩展到运行中取消。订阅已实现服务层及持久化核心,包含事务内队列、所有者和交付归属校验;HTTP/WSS、管理与消费 SDK 已接入,业务 Inbox 示例待完成。流程目前仍只具备模型,后续引擎也必须补齐事务内父资源归属验证。
|
||||
|
||||
P2 服务端已新增会话隔离、两步派发、租约、并发许可、结果回执/证据和恢复扫描,见 [执行协议](api/worker.md)。底层及自动 Worker SDK、尝试/证据查询、Schema 与进程故障恢复已通过 [P2 阶段验收](reports/phase-2.md)。
|
||||
|
||||
@@ -151,7 +151,7 @@ P2 服务端已新增会话隔离、两步派发、租约、并发许可、结
|
||||
|
||||
## 13. 可靠结果订阅核心
|
||||
|
||||
000013_result_subscriptions 新增 EventStream、ResultEvent,扩展 Subscription 和 Delivery。289 个直接声明字段均有中文注释。生产事务在独立流行锁下分配序号,与任务、原事件和结果快照一起提交;消费事务不锁流行、任务、队列或应用额度。交付记录和扫描位置一起落库,每条交付单独持有租约和证明。重复确认不重复扣减积压,耗尽保留记录,人工追加预算不重置历史。详见 [订阅核心](09-result-subscription-core.md)。对外 HTTP/WSS 与管理 SDK 已实现,消费 SDK 与完整场景继续推进。
|
||||
000013_result_subscriptions 新增 EventStream、ResultEvent,扩展 Subscription 和 Delivery。289 个直接声明字段均有中文注释。生产事务在独立流行锁下分配序号,与任务、原事件和结果快照一起提交;消费事务不锁流行、任务、队列或应用额度。交付记录和扫描位置一起落库,每条交付单独持有租约和证明。重复确认不重复扣减积压,耗尽保留记录,人工追加预算不重置历史。详见 [订阅核心](09-result-subscription-core.md)。对外 HTTP/WSS、管理与消费 SDK 已实现,持久化 Inbox 完整场景继续推进。
|
||||
|
||||
## 14. 交付历史查询索引
|
||||
|
||||
|
||||
@@ -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/固定间隔、漏跑策略、计划版本、运行中取消、并发许可、配额/背压、可靠结果订阅 | 多实例领取/触发竞争、重试上限、取消竞争、队列隔离、积压恢复、事件重复交付 | 进行中:安全重试、计划调度、运行中取消、队列控制/限速/积压背压、跨命名空间应用配额、显式资源键许可及 SDK 场景已实现并通过全量回归;优先级及节点公平派发已实现并通过全量回归;可靠订阅核心、HTTP/WSS、管理 SDK 和管理示例已实现并回归;消费 SDK、Inbox 场景及订阅进程故障回归继续开发 |
|
||||
| P3 Pulse 完整能力 | 安全重试、延时、Cron/固定间隔、漏跑策略、计划版本、运行中取消、并发许可、配额/背压、可靠结果订阅 | 多实例领取/触发竞争、重试上限、取消竞争、队列隔离、积压恢复、事件重复交付 | 进行中:安全重试、计划调度、运行中取消、队列控制/限速/积压背压、跨命名空间应用配额、显式资源键许可及 SDK 场景已实现并通过全量回归;优先级及节点公平派发已实现并通过全量回归;可靠订阅核心、HTTP/WSS、管理 SDK 和管理示例已实现并回归;消费 SDK、协议示例及两项节点强杀回归已完成;业务 Inbox 场景及消费者进程故障回归继续开发 |
|
||||
| P4 Weave 主流程 | SDK 定义构建与发布校验、实例、顺序/分支/结构化并行汇合、信号/定时等待、版本绑定 | 重启接续、早到/重复信号、并行竞争、内部事件重放、发布版本兼容;端到端通用流程 | 待开发 |
|
||||
| P5 Weave 恢复与查询 | 有界查询、暂停继续、取消、人工核查恢复、控制门控与审计 | 轮次与尝试独立预算;暂停/开始/回执/超时竞争;恢复保留预算;未知结果不盲目重复 | 待开发 |
|
||||
| P6 生产验证与运维 | 完整监控、凭据轮换、备份恢复、滚动升级、运行手册、可复现负载和故障工具 | 对照生产验收清单执行,报告环境/指标/遗留项;容量、RPO/RTO 按真实拓扑验证 | 待开发 |
|
||||
@@ -48,7 +48,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
|
||||
| 跨命名空间应用配额 | first 受理 → second 全局满额 → 释放 → 原键成功 → 恢复配置 | P3,已实现完整示例 |
|
||||
| 跨队列共享资源 | 两队列同键 → 一个有效许可 → 递增序号 → 资源端拒绝旧写入 | P3,已实现完整示例 |
|
||||
| 优先级与应用隔离 | 近同时到期按优先级 → 旧任务等待补偿 → 应用/队列轮转 | P3,已实现完整示例 |
|
||||
| 可靠结果订阅 | 冻结结果 → 持久化交付 → Inbox 去重 → 确认/补发 → 耗尽后人工重试 | P3,核心、HTTP/WSS、管理 SDK/示例已实现;消费 SDK 与 Inbox 场景待完成 |
|
||||
| 可靠结果订阅 | 冻结结果 → 持久化交付 → Inbox 去重 → 确认/补发 → 耗尽后人工重试 | P3,核心、HTTP/WSS、管理 SDK/示例已实现;消费 SDK 已实现,持久化 Inbox 场景待完成 |
|
||||
| 并行生成结果 | 发布流程 → 并行执行 → 汇合 → 完成 | P4 |
|
||||
| 审核与外部回执 | 提交流程 → 等待信号 → 授权信号注入 → 继续 | P4 |
|
||||
| 外部作业轮询 | 提交一次 → 有界查询处理中状态 → 回执或最终结果 → 超限核查 | P5 |
|
||||
@@ -78,6 +78,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
|
||||
- [可靠结果订阅核心](09-result-subscription-core.md)
|
||||
- [P3 订阅核心回归](reports/phase-3-result-subscription-core.md)
|
||||
- [P3 订阅通信与管理 SDK 回归](reports/phase-3-result-subscription-transport.md)
|
||||
- [P3 消费 SDK 与节点故障回归](reports/phase-3-event-consumer-sdk.md)
|
||||
- [接口手册](api/README.md)
|
||||
- [SDK 接入](sdk/README.md)
|
||||
- [通用场景示例](examples/README.md)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 可靠结果订阅:持久化核心
|
||||
|
||||
状态:P3 的存储与服务层已实现并通过本地全量回归,见 [验证记录](reports/phase-3-result-subscription-core.md)。[HTTP/WSS、管理 SDK 与管理示例](api/subscriptions.md) 已接入。事件连接 SDK、自动消费和持久化 Inbox 示例继续开发;本页的存储方法属于服务端内部模块。
|
||||
状态:P3 的存储与服务层已实现并通过本地全量回归,见 [验证记录](reports/phase-3-result-subscription-core.md)。[HTTP/WSS、管理 SDK 与管理示例](api/subscriptions.md) 已接入。[事件连接 SDK 与自动消费器](sdk/event-consumer.md) 已实现,持久化 Inbox 示例继续开发;本页的存储方法属于服务端内部模块。
|
||||
|
||||
## 1. 已实现范围
|
||||
|
||||
@@ -91,4 +91,4 @@ retained_from_sequence 初始且目前固定为 1。未来实现清理后,如
|
||||
|
||||
## 8. 下一步
|
||||
|
||||
HTTP 创建/查询/分页、暂停/继续、交付与耗尽记录查询、人工重试,以及 orbit.events.v1 WSS、消息 Schema、管理 SDK 和管理示例已实现,见 [接口手册](api/subscriptions.md)。接下来完成事件连接 SDK、自动消费及持久化 Inbox 去重示例,并补 SDK 断线/ACK 丢失和节点进程强杀回归,再验收 P3 的可靠订阅能力。
|
||||
HTTP 创建/查询/分页、暂停/继续、交付与耗尽记录查询、人工重试,以及 orbit.events.v1 WSS、消息 Schema、管理 SDK 和管理示例已实现,见 [接口手册](api/subscriptions.md)。[事件连接 SDK 和自动消费器](sdk/event-consumer.md) 已实现;继续完成持久化 Inbox 去重示例与消费者进程故障回归,再验收 P3 的可靠订阅能力。
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
| `/v1/tasks/{task_id}:cancel` 与 `task.cancel` 通知 | P3 运行中取消已实现 | [取消及 SDK 场景](cancellation.md) |
|
||||
| `/v1/schedules`、不可变版本、暂停/恢复及触发查询 | P3 已实现 | [计划接口与 SDK 场景](schedules.md) |
|
||||
| `/v1/subscriptions` 及管理、交付查询与重试 | 已实现 HTTP、管理 SDK 和完整管理示例 | [订阅手册](subscriptions.md) |
|
||||
| `/v1/events/connect` | 已实现服务端 WSS 与消息 Schema;消费 SDK 待交付 | [消息协议](subscriptions.md#5-websocket-建连与边界) |
|
||||
| `/v1/events/connect` | 已实现 WSS、消息 Schema、连接 SDK 和自动消费器 | [消息协议](subscriptions.md#5-websocket-建连与边界) / [消费 SDK](../sdk/event-consumer.md) |
|
||||
| `/v1/workflows` 与流程管理 | 计划 P4–P5,尚未实现 | [流程接口规划](workflows.md) |
|
||||
|
||||
未实现路由返回 404。健康检查就绪不表示任务执行、定时调度或流程引擎已经可用。
|
||||
|
||||
@@ -58,4 +58,4 @@ 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 结果订阅核心新增 `events:read`、`events:manage` 和 `events:consume`,bootstrap 仅显式 `--event-access` 时授予。订阅绑定创建凭据与单个队列,不能用同命名空间的另一张凭据消费或管理。当前 HTTP/WSS 和管理 SDK 已接入;消费 SDK、凭据重绑继续开发,详见 [订阅接口](subscriptions.md)。
|
||||
P3 结果订阅核心新增 `events:read`、`events:manage` 和 `events:consume`,bootstrap 仅显式 `--event-access` 时授予。订阅绑定创建凭据与单个队列,不能用同命名空间的另一张凭据消费或管理。当前 HTTP/WSS、管理及消费 SDK 已接入;凭据重绑继续开发,详见 [订阅接口](subscriptions.md)。
|
||||
|
||||
@@ -322,6 +322,6 @@ _, err = client.ConfigureSubscription(ctx, current.ID, pauseKey, orbit.Configure
|
||||
return err
|
||||
```
|
||||
|
||||
[完整管理示例](../../sdk/go/examples/subscriptions/main.go) 的启动、输入输出、失败和保留规则见 [场景文档](../examples/subscriptions.md)。它演示管理与幂等,不启动结果消费者。WSS 连接 SDK、自动重连和持久化 Inbox 示例仍在下一阶段开发,不能把测试中的原始 WebSocket 辅助代码当成正式 SDK。
|
||||
[完整管理示例](../../sdk/go/examples/subscriptions/main.go) 的启动、输入输出、失败和保留规则见 [场景文档](../examples/subscriptions.md)。它演示管理与幂等,不启动结果消费者。正式 [事件连接 SDK 与自动消费器](../sdk/event-consumer.md) 已提供接收、ACK、心跳、重连与有界收尾;[完整协议示例](../examples/event-protocol.md) 验证跨连接重报原确认。持久化 Inbox 示例继续开发。
|
||||
|
||||
机器可读消息契约见 [events.schema.json](../../protocol/eventsv1/events.schema.json),使用 Draft 2020-12,clientFrame/serverFrame 分方向约束。运行时额外拒绝重复 JSON 键、超范围 uint64 及非规范 base64url。Schema 不代替授权、状态机、租约及事务校验。
|
||||
|
||||
@@ -114,4 +114,11 @@
|
||||
- 步骤:创建 → 同键核对 → 暂停 → 恢复 → 查询与分页 → 最终暂停。
|
||||
- 预期:duplicate=true、resumed_revision=3、final_enabled=false、revision=4、pending=0。
|
||||
- 保留:每次运行留下一个已暂停订阅及审计;没有硬删除,受64个订阅上限约束。
|
||||
- 范围:本例只演示管理SDK;自动消费、断线重连与持久化Inbox例子继续开发。
|
||||
- 范围:本例只演示管理SDK;自动消费 SDK 已实现,持久化Inbox例子继续开发。
|
||||
|
||||
## 已可运行:结果消费协议与跨连接确认
|
||||
|
||||
- 源码:[事件协议示例](../../sdk/go/examples/event-protocol/main.go),[启动与输出说明](event-protocol.md)。
|
||||
- 操作:创建订阅 → 提交并取消任务 → 接收冻结结果 → ACK → 重连重报原证明 → 核对原确认时间 → 暂停订阅。
|
||||
- 输出:ack_replayed=true、same_receipt=true、pending=0、subscription_enabled=false。
|
||||
- 范围:本例只做读取验证,不写业务数据;[自动消费器](../sdk/event-consumer.md) 的生产接入须提供事务Inbox,不能把示例当作业务恰好一次保证。
|
||||
|
||||
30
docs/examples/event-protocol.md
Normal file
30
docs/examples/event-protocol.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# 结果消费协议示例
|
||||
|
||||
[完整代码](../../sdk/go/examples/event-protocol/main.go) 使用正式 Go SDK 创建取消结果订阅,提交并取消一个待执行任务,接收并验证冻结结果,然后 ACK;关闭连接后在新连接中重报原证明,验证原确认时间和去重回执,最后暂停本次订阅。
|
||||
|
||||
本例只读取并验证结果,没有业务数据库写入。生产业务应使用 [自动消费器及事务去重约定](../sdk/event-consumer.md)。持久化 Inbox 完整示例尚待交付。
|
||||
|
||||
## 启动
|
||||
|
||||
先按 [订阅管理示例](subscriptions.md) 建立隔离示例队列和带 --event-access 的凭据,启动 Orbit。凭据需要 tasks:submit、tasks:cancel、events:manage/read/consume 及该队列范围;订阅绑定创建凭据,不能临时换凭据消费。
|
||||
|
||||
从 sdk/go 目录运行:
|
||||
|
||||
```sh
|
||||
ORBIT_URL=http://127.0.0.1:8080 \
|
||||
ORBIT_CREDENTIALS_FILE=../../.local/event.credentials.json \
|
||||
go run ./examples/event-protocol
|
||||
```
|
||||
|
||||
也可以传 ORBIT_TOKEN 与 ORBIT_QUEUE_ID,环境变量覆盖凭据文件对应值。每次运行新建随机订阅及任务身份,全部流程最多 15 秒。本例是短流程,两个连接均短于一次心跳周期;长期运行必须用自动消费器或主动 Ping。
|
||||
|
||||
## 输出与核对
|
||||
|
||||
```text
|
||||
event=<本次事件ID> task=<本次任务ID> state=CANCELLED
|
||||
ack_replayed=true same_receipt=true pending=0 subscription_enabled=false
|
||||
```
|
||||
|
||||
该输出验证的是事件快照和平台 ACK 去重,不表示业务副作用获得恰好一次保证。任何请求失败、收到非本次任务的事件或确认时间不一致,都非零退出;保留已生成的状态供核查。
|
||||
|
||||
需要独立示例队列,避免其他程序同时产生取消结果。正常结束暂停本次订阅,保留任务、交付和审计;不删除历史。每个命名空间最多 64 个订阅,重复演示应使用独立测试环境。`make integration` 在一次性 MySQL 和真实进程上实际执行本例。
|
||||
75
docs/reports/phase-3-event-consumer-sdk.md
Normal file
75
docs/reports/phase-3-event-consumer-sdk.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# P3 事件消费 SDK 与节点故障回归
|
||||
|
||||
验证日期:2026-09-17。已交付 [消费 SDK](../sdk/event-consumer.md) 和 [协议示例](../examples/event-protocol.md)。业务端持久化 Inbox 示例及消费者进程强杀恢复尚未完成,P3 和持续目标继续进行。
|
||||
|
||||
## 1. 实现范围
|
||||
|
||||
- [EventConnection](../../sdk/go/event_connection.go):严格事件协议、接收、请求关联、ACK、心跳和关闭;控制最多32个在途,接收缓冲为声明容量,超时关闭连接后保留原证明核对。
|
||||
- [EventConsumer](../../sdk/go/event_consumer.go):自动心跳、指数退避重连、本地1–32个在途事件、成功后确认、ACK不明时原证明核对、处理器失败不确认、取消与有界收尾。
|
||||
- [类型及秘密隐藏](../../sdk/go/event_types.go):ResultEvent不给业务处理器租约秘密;Grant/Proof仍可显式序列化用于受保护的存储,普通fmt隐藏敏感数据。
|
||||
- [严格解码](../../sdk/go/event_codec.go):大小写、重复键、方向、会话、规范uint64、证明编码和事件/交付/快照关联。大整数不经float64转换。协议类WebSocket关闭按永久错误处理。
|
||||
- [协议例子](../../sdk/go/examples/event-protocol/main.go):产生取消结果、读取、ACK、重连重报、验证原确认时间、暂停订阅。它只读取结果,不执行接收方业务写入。
|
||||
- SDK保持独立模块,无新增数据库驱动、服务端包或持久化模型。本阶段不增加迁移、外键或级联删除。
|
||||
|
||||
## 2. 验证环境与入口
|
||||
|
||||
macOS、Go1.27.1、Docker MySQL8.4.10、一次性数据库;普通SDK集成连接真实Orbit进程,故障网关使用本地TLS,进程回归使用真实子进程和SIGKILL。注入后用Wait确认进程已经退出,不能以发送了Kill调用代替终止证据。
|
||||
|
||||
```sh
|
||||
make fmt test vet build
|
||||
make integration
|
||||
cd sdk/go
|
||||
GOWORK=off go test -race ./...
|
||||
```
|
||||
|
||||
| 分组 | 结果 |
|
||||
| --- | --- |
|
||||
| 全量MySQL集成 | PASS,92.995秒 |
|
||||
| SDK Worker连接/运行器 | PASS,88.494秒 |
|
||||
| SDK计划、队列、订阅管理、应用配额 | PASS,5.704 / 4.466 / 33.386 / 1.363秒 |
|
||||
| 新消费SDK三项集成 | PASS,2.18 / 15.28 / 33.14秒,包总计51.843秒 |
|
||||
| 新节点交付租约恢复 | PASS,32.15秒 |
|
||||
| 新ACK提交/丢响应/节点强杀 | PASS,1.31秒 |
|
||||
| 九项真实进程故障(原七项加新两项) | PASS,包总计61.810秒 |
|
||||
| 完整SDK示例 | 14个全部通过,包括新的event-protocol |
|
||||
| fmt、race、vet、build | PASS |
|
||||
| GOWORK=off独立SDK模块 | PASS,编译及竞态测试不依赖服务端workspace |
|
||||
|
||||
完整SDK示例输出包含:
|
||||
|
||||
```text
|
||||
event=<本次事件ID> task=<本次任务ID> state=CANCELLED
|
||||
ack_replayed=true same_receipt=true pending=0 subscription_enabled=false
|
||||
```
|
||||
|
||||
## 3. 协议与运行器边界
|
||||
|
||||
[连接单元回归](../../sdk/go/event_connection_test.go) 在TLS服务器上混合投递与乱序控制响应,验证独立关联、精确数字、秘密隐藏;取消Next不关闭连接,已发ACK取消关闭连接并报告未知确认状态。32个控制槽满后拒绝新控制,交付缓冲满后关闭。拒绝重复键、大小写别名、非规范计数、null布尔/错误、缺失创建时间、错误事件关联、状态不匹配、额外字段及不匹配ACK;1008关闭不无限重连。
|
||||
|
||||
[运行器回归](../../sdk/go/event_consumer_test.go) 验证ACK回应丢失后原证明重连,不重复运行仍保留的已提交处理器;新代次替换后重新进入业务去重;error/panic/Goexit均不确认、不立即本地重试。取消后收集已提交结果,忽略取消的处理器在收尾期限到达后报告Running与Unconfirmed。永久401只连接一次,实例不能重复Run,容量2实际并行处理并限制并发。
|
||||
|
||||
处理器测试中的原子计数只用于确认调用次数,不能作为已实现持久化Inbox的证据。
|
||||
|
||||
## 4. 真实服务与MySQL
|
||||
|
||||
[三项SDK集成](../../sdk/go/event_integration_test.go):
|
||||
|
||||
1. 接收已取消任务的冻结快照,暂停后Ping返回enabled=false,已有Proof仍能ACK;关闭连接后重报返回同一确认时间。暂停时新增结果不投递,恢复后继续消费,最终pending_count=0。
|
||||
2. 处理器持续等待真实15秒自动心跳,故障网关收到上游心跳ACK才放行业务完成。随后上游提交事件ACK,网关丢弃回应并断开。消费器重连核对原证明,处理器调用数和交付次数均保持1。
|
||||
3. 首次处理器返回error,SDK不ACK。测试等待服务端真实30秒租约及退避,不修改数据库时间;同一交付以第二代次发送,处理成功后确认,最终attempts=2、HandlerFailures=1。
|
||||
|
||||
## 5. 真实节点强杀
|
||||
|
||||
[进程故障测试](../../sdk/go/event_process_integration_test.go):
|
||||
|
||||
- **领取提交后节点退出**:节点A成功投递后被SIGKILL并确认退出。节点B读取同一MySQL,在租约自然到期后恢复相同event_id/delivery_id,代次和次数增加到2。旧Proof返回DELIVERY_LEASE_LOST,新Proof成功ACK,订阅积压清零。
|
||||
- **ACK提交后响应丢失**:网关截获成功ACK回应并阻断后续重连;通过查询确认ACKED和已保存的确认时间,然后强杀节点A。启动节点B,切换网关并恢复连接;Consumer仍持有原Proof,返回原确认时间,不新建交付尝试,不重跑处理器。
|
||||
|
||||
这两项强杀的是Orbit服务端进程。第二项消费器保持运行,其内存证明未丢失;不能将此证据当作消费者进程被强杀后业务去重也已验证。
|
||||
|
||||
## 6. 保留边界
|
||||
|
||||
- 后续需要交付独立业务数据库的Inbox事务例子,验证提交后/ACK前消费者进程退出、并发重复投递以及接收方事务回滚/提交不明等窗口,再验收P3可靠订阅。
|
||||
- Consumer取消或传输断线不能撤销已经发出的业务效果;交付租约不是业务互斥锁。生产业务需要持久化唯一键和事务,外部副作用需要业务幂等/Outbox/核查。
|
||||
- 当前仍按启用订阅的领取活动恢复过期交付;独立清扫、历史保留与清理、凭据安全重绑和生产容量/RPO/RTO仍属后续阶段。
|
||||
- 结果证明只覆盖本报告的单数据库、进程/TLS网关拓扑;未将本机回归等同于百万租户容量或生产高可用验收。
|
||||
@@ -1,6 +1,6 @@
|
||||
# Go SDK 接入说明
|
||||
|
||||
已实现健康查询、任务提交/查询/分页/待执行取消、队列创建/查询、[自动 Worker](worker-runtime.md)、[Worker 底层协议 SDK](worker.md) 和 [执行历史查询](../api/execution-history.md)。[安全重试](../api/retries.md) 已提供有限策略、WorkerFailure.Retryable、历史查询及完整例子。[计划调度](../api/schedules.md) 已支持创建、查询、版本发布、暂停/恢复、触发历史与完整示例。[订阅管理](../api/subscriptions.md) 已支持创建、查询、暂停恢复、交付历史与人工重试;事件消费 SDK 继续开发。流程构建等后续接口按 [开发计划](../08-development-plan.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) 继续实现。
|
||||
|
||||
module:`git.yuntuo.hk/k2safe/orbit/sdk/go`。SDK 使用独立 go.mod,依赖 Go 标准库与 `github.com/coder/websocket v1.8.15`,不引用服务端模型或数据库实现。
|
||||
|
||||
@@ -108,6 +108,8 @@ fmt.Println(cancelled.Task.State)
|
||||
| CreateSubscription / GetSubscription / ListSubscriptions | 订阅受理、当前配置与队列分页 | [订阅](../api/subscriptions.md) |
|
||||
| ConfigureSubscription | 显式暂停/继续与版本、原因 | [订阅](../api/subscriptions.md) |
|
||||
| ListEventDeliveries / GetEventDelivery / RetryEventDelivery | 交付历史、冻结结果和耗尽后追加预算 | [订阅](../api/subscriptions.md) |
|
||||
| DialEvents / Next / Ack / Ping | 单连接交付、确认与心跳,可用原证明跨连接重报 | [消费 SDK](event-consumer.md) |
|
||||
| NewEventConsumer / Run / Events / Stats | 自动心跳、重连、受控处理器并发及有界收尾 | [消费 SDK](event-consumer.md) |
|
||||
| CreateSchedule / GetSchedule / ListSchedules | 计划受理、详情与队列分页 | [计划](../api/schedules.md) |
|
||||
| PublishSchedule / GetScheduleVersion | 不可变发布与历史版本 | [计划](../api/schedules.md) |
|
||||
| PauseSchedule / ResumeSchedule / ListScheduleOccurrences | 控制与触发记录 | [计划](../api/schedules.md) |
|
||||
@@ -186,4 +188,4 @@ P2 已提供底层协议、[自动 Worker 运行器及示例](worker-runtime.md)
|
||||
|
||||
## 订阅管理示例
|
||||
|
||||
[完整运行方法](../examples/subscriptions.md) 使用显式 --event-access 凭据,执行创建/同键核对/暂停/恢复/查询,再暂停本次订阅并保留审计。源码见 [examples/subscriptions](../../sdk/go/examples/subscriptions/main.go)。WSS 连接 SDK、自动消费与持久化 Inbox 示例尚未交付,不能将此管理示例当作完整可靠消费链路。
|
||||
[完整运行方法](../examples/subscriptions.md) 使用显式 --event-access 凭据,执行创建/同键核对/暂停/恢复/查询,再暂停本次订阅并保留审计。源码见 [examples/subscriptions](../../sdk/go/examples/subscriptions/main.go)。WSS 连接 SDK 与自动消费器见 [消费手册](event-consumer.md),可运行 [协议示例](../examples/event-protocol.md) 验证跨连接 ACK;持久化 Inbox 示例继续开发。
|
||||
|
||||
94
docs/sdk/event-consumer.md
Normal file
94
docs/sdk/event-consumer.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Go 结果消费 SDK
|
||||
|
||||
对应 [HTTP/WSS 契约](../api/subscriptions.md)。SDK 是独立模块,只依赖 HTTP/WebSocket,不导入服务端模型、存储或数据库驱动。订阅先用管理 API 创建,并固定在创建凭据与队列范围内。
|
||||
|
||||
已提供 [EventConnection](../../sdk/go/event_connection.go)、[EventConsumer](../../sdk/go/event_consumer.go)、[公开类型](../../sdk/go/event_types.go) 与 [可运行协议示例](../examples/event-protocol.md)。业务 MySQL Inbox 示例和消费者进程强杀验证尚在后续阶段,不能把 SDK 内存状态作为持久化去重。
|
||||
|
||||
## 1. 自动消费
|
||||
|
||||
下面是可复用的应用函数。`applyOnce` 由接收方提供,必须在同一数据库事务内保存事件去重记录和业务变更;事务明确提交或读取到已经提交的同一事件后才返回 nil。它不能仅向内存队列投递后返回。
|
||||
|
||||
```go
|
||||
func consumeResults(
|
||||
ctx context.Context,
|
||||
client *orbit.Client,
|
||||
subscriptionID string,
|
||||
applyOnce func(context.Context, orbit.ResultEvent) error,
|
||||
) error {
|
||||
consumer, err := orbit.NewEventConsumer(client, orbit.EventConsumerOptions{
|
||||
SubscriptionID: subscriptionID,
|
||||
Capacity: 4,
|
||||
Handle: applyOnce,
|
||||
ShutdownTimeout: 30 * time.Second,
|
||||
ReconnectMinDelay: 200 * time.Millisecond,
|
||||
ReconnectMaxDelay: 5 * time.Second,
|
||||
})
|
||||
if err != nil { return err }
|
||||
return consumer.Run(ctx)
|
||||
}
|
||||
```
|
||||
|
||||
所在文件需导入 `context`、`time` 和 `orbit "git.yuntuo.hk/k2safe/orbit/sdk/go"`。同一 Consumer 只能 Run 一次;多实例可共同绑定同一个订阅,按服务端交付租约竞争,不保证事件处理/完成顺序。
|
||||
|
||||
| 配置 | 默认 / 范围 |
|
||||
| --- | --- |
|
||||
| SubscriptionID | 必填;有效的 32 位小写十六进制 ID |
|
||||
| Handle | 必填;处理冻结的 ResultEvent,不接收交付秘密 |
|
||||
| Capacity | 默认 4;1–32 个本地在途事件及处理器 |
|
||||
| ShutdownTimeout | 默认 30 秒;1 毫秒至 5 分钟 |
|
||||
| ReconnectMinDelay | 默认 200 毫秒;至少 10 毫秒 |
|
||||
| ReconnectMaxDelay | 默认 5 秒;不小于起点且不超过 30 秒 |
|
||||
|
||||
每个连接另有 Capacity 条未读交付缓冲,满后关闭连接并按退避重连。处理器结果缓冲不超过 Capacity,本地观察缓冲 128;不会为积压无限创建 goroutine。自动消费器每条连接至多一个 ACK 在途,心跳独立并发,慢 ACK 不阻塞 WebSocket 读取。
|
||||
|
||||
## 2. 提交、补发和去重
|
||||
|
||||
推荐的接收方事务步骤:
|
||||
|
||||
1. 使用固定消费者用途与事件 ID 建立唯一键,例如 `(consumer_name, event_id)`。不要使用连接 ID、交付代次或消息 ID 作为业务去重键。
|
||||
2. 在一个业务事务中插入 Inbox 记录并更新业务数据。唯一键竞争必须等待对方事务提交或回滚;遇到重复键时核对已有记录,而不是忽略所有 SQL 错误。
|
||||
3. 业务事务提交成功后返回 nil,SDK 再发送 ACK。提交结果未知时返回 error,保留核查信息,下次投递先按唯一键核对。
|
||||
4. ACK 已提交但回应丢失,SDK 保留原证明,在新连接上重报;在该本地事件仍保留期间不会重新调用处理器。
|
||||
5. 原证明已被新代次替代时,SDK 记录 proof_lost,等待新投递。新投递会再次进入处理器,必须命中持久化 Inbox 后才能确认新证明。
|
||||
|
||||
处理器 error、panic 或 Goexit 都不会 ACK,也不会在 SDK 内存中立即重新执行。平台在原 30 秒租约自然到期后,按退避和累计预算重新投递;耗尽后保留 EXPIRED,占用订阅窗口,需要管理 API 追加预算。仅在启用订阅有领取活动时触发恢复,当前尚无独立后台交付清扫器。
|
||||
|
||||
事件交付租约不提供业务独占执行授权,Ping 不延长它。长于租约的处理、网络分区或多个接收进程可能造成同一事件并行进入业务侧;本地 Capacity 和事件合并不能代替跨进程的唯一约束及事务。外部 HTTP 副作用也不能和 Inbox SQL 自动组成原子事务,需要接收方 Outbox、业务幂等及结果核查设计。
|
||||
|
||||
`ResultEvent.Snapshot` 是原事件时刻的冻结快照,不能通过后来查询到的当前任务状态替换。SDK 保留 RawMessage 中大整数及业务 JSON;例如 `9007199254740993` 不经 float64 重编码。`task.needs_review` 表示需要核查,不能按业务成功或最终失败处理。
|
||||
|
||||
## 3. 重连与关闭
|
||||
|
||||
自动心跳间隔为当前 v1 固定的 15 秒。心跳也重新验证凭据和订阅权限,暂停的订阅仍可心跳及确认已有交付。
|
||||
|
||||
网络中断、超时、接收缓冲耗尽、UNAVAILABLE、HTTP 408/429/5xx 使用指数退避及半区间抖动重连;延迟上限为 ReconnectMaxDelay。确认成功后重置退避。认证、权限、资源不存在、历史缺口、协议错误及其他永久错误停止消费,返回支持 `errors.Is/As` 的 EventConsumerRunError。
|
||||
|
||||
取消 Run 的 context 后,SDK 停止启动新处理器并取消已有处理器 context。在 ShutdownTimeout 内收集处理器返回,已提交的结果仍尝试 ACK 或重连核对;正常收尾返回 nil。未成功处理的交付保留给平台按预算恢复。
|
||||
|
||||
超时返回 `ErrEventShutdownTimeout` 包装错误:`Unconfirmed` 是本进程已知且未确认的事件数,`Running` 是仍未退出的处理器数。Go 不能强杀忽略 context 的函数,不能在收到超时后声称这些函数的业务效果已经撤销。永久连接错误也会取消并等待在途函数,等待受相同收尾时限约束。
|
||||
|
||||
调用方主动停止后,暂停订阅是独立管理操作;关闭一个 Consumer 不会暂停共享订阅。进程被强杀时,SDK 内存中的结果和证明不保证保留,接收方 Inbox 必须独立持久化。
|
||||
|
||||
## 4. 底层连接
|
||||
|
||||
| 方法 | 语义 |
|
||||
| --- | --- |
|
||||
| Client.DialEvents(ctx, EventRegistration) | 使用 Authorization 与 orbit.events.v1 握手;ctx 只控制握手,容量必须显式传 1–32 |
|
||||
| Welcome() | 初始握手快照;不是实时订阅状态 |
|
||||
| Next(ctx) | 等待交付;等待取消不关闭连接 |
|
||||
| Ack(ctx, messageID, EventProof) | 确认原交付,可在同订阅的新连接中重报 |
|
||||
| Ping(ctx, messageID) | 查询 Enabled 并维持连接,不续交付租约 |
|
||||
| Done() / Err() | 等待读取循环退出及查询关闭原因 |
|
||||
| Close() | 关闭传输并等待读取循环退出,不取消服务端交付 |
|
||||
|
||||
EventConnection 不自动心跳、重连或执行处理器。应用必须每 15 秒 Ping,并在退出时 Close。最多 32 个在途控制请求;超出返回 ErrEventBusy。控制请求总时限最多 10 秒,包含写入排队;握手最多 8 秒;每条文本帧最多 512 KiB。控制超时关闭连接,防止无法关联的迟到回应被当作另一次操作成功。
|
||||
|
||||
`grant.Proof()` 包含订阅、事件、交付、代次和秘密;SDK 本地核对订阅与回执事件,线上 ACK 只发送交付 ID、代次和秘密。重连后使用原 Proof,可通过 NewEventMessageID 生成新的关联 ID。ACK 持久化去重不依赖消息 ID。
|
||||
|
||||
SDK 严格验证消息方向、会话、回复关联、规范十进制 uint64、秘密编码、必填字段、重复键、字段大小写和交付/事件/快照关联。无效响应返回 ErrEventProtocol 并关闭连接;不会将无效回执当作已确认。
|
||||
|
||||
## 5. 观察与敏感信息
|
||||
|
||||
`Stats()` 返回进程内 Running、Started、HandlerFailures、Acknowledged、Reconnects 和 DroppedEvents;重启清零。`Events()` 返回有界本地通知,不读取也不会阻塞执行,Run 返回时关闭。它们不能替代数据库状态和持久化审计;Acknowledged 包括对已提交 ACK 的重报确认。
|
||||
|
||||
EventGrant、EventProof、EventConnection、EventConsumer 和 EventError 的常见 fmt 输出隐藏秘密、业务 JSON 或远端错误正文。显式 JSON 序列化 Grant/Proof 仍包含租约秘密,不可放到普通日志;若业务确需保存 Proof,应加密并限制访问。调用方记录自己的 handler error 时也应脱敏,SDK 观察事件只给出 HANDLER_FAILED 分类。
|
||||
Reference in New Issue
Block a user