feat: expose result subscription HTTP and WebSocket APIs

This commit is contained in:
2026-09-17 19:57:55 +08:00
parent 1c3300cc5b
commit 8485a662b2
38 changed files with 3462 additions and 26 deletions

View File

@@ -163,7 +163,7 @@ SDK 可以在进程存活期间缓存未确认消息;不得宣称内存缓存
## 7. 可靠结果订阅
建议独立使用 WSS 入口 `/v1/events/connect` 与子协议 `orbit.events.v1`,区分 Worker 消费权和结果订阅权。具体路径待评审。
当前已实现独立 WSS 入口 `/v1/events/connect` 与子协议 `orbit.events.v1`,区分 Worker 消费权和结果订阅权。当前请求、响应、限制与机器可读 Schema 以 [订阅接口](api/subscriptions.md) 为准;下述涉及流程事件的描述仍为规划。
事件包含 `event_id`、资源类型、资源 ID、状态版本、事件类型及结果引用,可表达任务与流程事件。平台先持久化事件与交付状态,再发送 `event.deliver`;接收方完成自身的可靠处理后发送 `event.ack`。

View File

@@ -1,6 +1,6 @@
# 数据模型与实现基线
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度、队列限速/背压和应用配额;优先级及节点公平领取已实现;可靠订阅持久化核心已实现,HTTP/WSS 与 SDK 接入及流程编排继续开发。
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度、队列限速/背压和应用配额;优先级及节点公平领取已实现;可靠订阅核心、HTTP/WSS 与管理 SDK 已实现,消费 SDK 及流程编排继续开发。
## 1. 模块与依赖
@@ -95,7 +95,7 @@
禁止“先查后另开事务写入”。需要防止并发状态改变的关联行在事务内持锁,统一锁顺序并保留必要索引。当前不提供硬删除;将来增加清理时,必须核验在途任务、引用、幂等恢复窗口与审计保留期,按有限批次处理,不能依赖数据库级联。直接绕过服务写 SQL 不受应用校验保护。
P1 已完成动作/队列授权、幂等摘要与原回执、受理事件及取消 CAS/审计事务;P3 扩展到运行中取消。订阅已实现服务层及持久化核心,包含事务内队列、所有者和交付归属校验;HTTP/WSS 和 SDK 尚未接入。流程目前仍只具备模型,后续引擎也必须补齐事务内父资源归属验证。
P1 已完成动作/队列授权、幂等摘要与原回执、受理事件及取消 CAS/审计事务;P3 扩展到运行中取消。订阅已实现服务层及持久化核心,包含事务内队列、所有者和交付归属校验;HTTP/WSS 和管理 SDK 已接入,消费 SDK 待完成。流程目前仍只具备模型,后续引擎也必须补齐事务内父资源归属验证。
P2 服务端已新增会话隔离、两步派发、租约、并发许可、结果回执/证据和恢复扫描,见 [执行协议](api/worker.md)。底层及自动 Worker SDK、尝试/证据查询、Schema 与进程故障恢复已通过 [P2 阶段验收](reports/phase-2.md)。
@@ -151,4 +151,8 @@ P2 服务端已新增会话隔离、两步派发、租约、并发许可、结
## 13. 可靠结果订阅核心
000013_result_subscriptions 新增 EventStream、ResultEvent,扩展 Subscription 和 Delivery。289 个直接声明字段均有中文注释。生产事务在独立流行锁下分配序号,与任务、原事件和结果快照一起提交;消费事务不锁流行、任务、队列或应用额度。交付记录和扫描位置一起落库,每条交付单独持有租约和证明。重复确认不重复扣减积压,耗尽保留记录,人工追加预算不重置历史。详见 [订阅核心](09-result-subscription-core.md)。对外 HTTP/WSS 与 SDK 尚未实现,P3 继续推进。
000013_result_subscriptions 新增 EventStream、ResultEvent,扩展 Subscription 和 Delivery。289 个直接声明字段均有中文注释。生产事务在独立流行锁下分配序号,与任务、原事件和结果快照一起提交;消费事务不锁流行、任务、队列或应用额度。交付记录和扫描位置一起落库,每条交付单独持有租约和证明。重复确认不重复扣减积压,耗尽保留记录,人工追加预算不重置历史。详见 [订阅核心](09-result-subscription-core.md)。对外 HTTP/WSS 与管理 SDK 已实现,消费 SDK 与完整场景继续推进。
## 14. 交付历史查询索引
000014_delivery_history 增加按订阅及可选状态过滤的创建时间分页索引,领取、确认和重试不改变排序键。不增加模型字段或外键,仍为35个实体、289个中文注释字段。查询按凭据所有者、命名空间和订阅校验,游标绑定过滤条件;详情按来源事件ID读取不可变快照且不返回交付秘密。见 [订阅查询接口](api/subscriptions.md)。

View File

@@ -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 及断线故障回归继续开发 |
| 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 待接入 |
| 可靠结果订阅 | 冻结结果 → 持久化交付 → Inbox 去重 → 确认/补发 → 耗尽后人工重试 | P3,核心、HTTP/WSS、管理 SDK/示例已实现;消费 SDK 与 Inbox 场景待完成 |
| 并行生成结果 | 发布流程 → 并行执行 → 汇合 → 完成 | P4 |
| 审核与外部回执 | 提交流程 → 等待信号 → 授权信号注入 → 继续 | P4 |
| 外部作业轮询 | 提交一次 → 有界查询处理中状态 → 回执或最终结果 → 超限核查 | P5 |
@@ -77,6 +77,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
- [数据模型](07-data-model.md)
- [可靠结果订阅核心](09-result-subscription-core.md)
- [P3 订阅核心回归](reports/phase-3-result-subscription-core.md)
- [P3 订阅通信与管理 SDK 回归](reports/phase-3-result-subscription-transport.md)
- [接口手册](api/README.md)
- [SDK 接入](sdk/README.md)
- [通用场景示例](examples/README.md)

View File

@@ -1,6 +1,6 @@
# 可靠结果订阅:持久化核心
状态:P3 的存储与服务层已实现并通过本地全量回归,见 [验证记录](reports/phase-3-result-subscription-core.md)。HTTP 管理接口、事件 WSS、独立 SDK 和可运行订阅示例尚未接入;不能把本页的方法当作已经发布的 SDK。对外接口接通后再冻结消息 Schema 和完整调用手册。
状态:P3 的存储与服务层已实现并通过本地全量回归,见 [验证记录](reports/phase-3-result-subscription-core.md)。[HTTP/WSS、管理 SDK 与管理示例](api/subscriptions.md) 已接入。事件连接 SDK、自动消费和持久化 Inbox 示例继续开发;本页的存储方法属于服务端内部模块。
## 1. 已实现范围
@@ -61,7 +61,7 @@ bootstrap 新增显式 --event-access,用于签发包含这三项动作的开
| ACKED | 已可靠确认;重复确认返回原确认时间,不再次扣减 pending_count,也不重复写确认审计 |
| EXPIRED | 已耗尽交付预算,仍保留事件和积压占用;允许带原因的人工追加预算,或接受尚未被替换的最近证明的迟到确认 |
租约固定 30 秒,每次领取增加 lease_epoch 和 attempts,并产生新的 32 字节随机秘密。数据库只保存摘要;确认时使用常量时间摘要比较。当前内部领取按服务端连接 ID 计算容量,单连接容量 1–32。连接 ID 由后续传输层生成,不能接受客户端自报来绕过容量。
租约固定 30 秒,每次领取增加 lease_epoch 和 attempts,并产生新的 32 字节随机秘密。数据库只保存摘要;确认时使用常量时间摘要比较。当前内部领取按服务端连接 ID 计算容量,单连接容量 1–32。连接 ID 由传输层生成,不能接受客户端自报来绕过容量。
下一次启用状态下的领取,会先回收该订阅的过期交付。未耗尽时使用 `min(2^(attempts-1), 30)` 秒退避;耗尽时转 EXPIRED,不无限自动发送。当前没有独立订阅清扫进程:无人消费或订阅暂停期间,租约状态可能保持 LEASED,直至下一次领取执行恢复。
@@ -91,4 +91,4 @@ retained_from_sequence 初始且目前固定为 1。未来实现清理后,如
## 8. 下一步
接入 HTTP 创建/查询/分页、暂停/继续、交付与耗尽记录查询、人工重试;实现 orbit.events.v1 WSS 的认证、hello、交付、ACK、心跳、连接上限、背压和退出;补独立 Go SDK、Inbox 去重示例、严格消息 Schema 和完整接口手册。随后做断路、ACK 响应丢失、节点进程强杀及 SDK 端到端回归,再验收 P3 的可靠订阅能力。
HTTP 创建/查询/分页、暂停/继续、交付与耗尽记录查询、人工重试,以及 orbit.events.v1 WSS、消息 Schema、管理 SDK 和管理示例已实现,见 [接口手册](api/subscriptions.md)。接下来完成事件连接 SDK、自动消费及持久化 Inbox 去重示例,并补 SDK 断线/ACK 丢失和节点进程强杀回归,再验收 P3 的可靠订阅能力。

View File

@@ -20,7 +20,8 @@
| 任务冻结策略与 Worker `retryable` | P3 安全重试已实现 | [安全重试](retries.md) |
| `/v1/tasks/{task_id}:cancel` 与 `task.cancel` 通知 | P3 运行中取消已实现 | [取消及 SDK 场景](cancellation.md) |
| `/v1/schedules`、不可变版本、暂停/恢复及触发查询 | P3 已实现 | [计划接口与 SDK 场景](schedules.md) |
| `/v1/events/connect` 及订阅管理 | 持久化核心已实现;HTTP/WSS 和 SDK 尚未接入 | [订阅核心及边界](../09-result-subscription-core.md) |
| `/v1/subscriptions` 及管理、交付查询与重试 | 已实现 HTTP、管理 SDK 和完整管理示例 | [订阅手册](subscriptions.md) |
| `/v1/events/connect` | 已实现服务端 WSS 与消息 Schema;消费 SDK 待交付 | [消息协议](subscriptions.md#5-websocket-建连与边界) |
| `/v1/workflows` 与流程管理 | 计划 P4–P5,尚未实现 | [流程接口规划](workflows.md) |
未实现路由返回 404。健康检查就绪不表示任务执行、定时调度或流程引擎已经可用。

View File

@@ -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 尚未接入,详见 [订阅核心边界](../09-result-subscription-core.md)。
P3 结果订阅核心新增 `events:read`、`events:manage` 和 `events:consume`,bootstrap 仅显式 `--event-access` 时授予。订阅绑定创建凭据与单个队列,不能用同命名空间的另一张凭据消费或管理。当前 HTTP/WSS 和管理 SDK 已接入;消费 SDK、凭据重绑继续开发,详见 [订阅接口](subscriptions.md)。

327
docs/api/subscriptions.md Normal file
View File

@@ -0,0 +1,327 @@
# 可靠结果订阅 HTTP 与 WebSocket
状态:已实现服务端 HTTP/WSS、管理 SDK 和管理示例。事件连接 SDK、自动重连消费器、持久化 Inbox 示例及真实进程强杀验证继续开发,完整 P3 订阅验收尚未完成。[存储语义与升级边界](../09-result-subscription-core.md),[本阶段验证](../reports/phase-3-result-subscription-transport.md)。
## 1. 授权与共同规则
所有 HTTP 请求使用 Authorization: Bearer 请求头,管理写请求还需要稳定的 Idempotency-Key。生产通过 HTTPS/WSS 接入;本地示例可使用回环 HTTP。凭据、交付秘密不能放在 URL。请求不接受自报 namespace_id、application_id 或 credential_id。
订阅固定绑定创建凭据和一个队列。除动作权限外,每次操作都验证队列范围和订阅所有者。另一张凭据即使属于相同命名空间也不能操作此订阅;凭据重新绑定尚未开放。bootstrap 使用显式 --event-access 才授予三个事件动作。
| 方法与路径 | 权限 | SDK 方法 |
| --- | --- | --- |
| POST /v1/subscriptions | events:manage | CreateSubscription |
| GET /v1/subscriptions?queue_id=... | events:read | ListSubscriptions |
| GET /v1/subscriptions/{id} | events:read | GetSubscription |
| POST /v1/subscriptions/{id}:configure | events:manage | ConfigureSubscription |
| GET /v1/subscriptions/{id}/deliveries | events:read | ListEventDeliveries |
| GET /v1/subscriptions/{id}/deliveries/{delivery_id} | events:read | GetEventDelivery |
| POST /v1/subscriptions/{id}/deliveries/{delivery_id}:retry | events:manage | RetryEventDelivery |
| GET /v1/events/connect(WebSocket Upgrade) | events:consume | 连接 SDK 待交付,可按下文协议接入 |
HTTP 使用统一 request_id + data/error 信封,响应带 X-Request-ID 和 Cache-Control: no-store。管理请求 8 秒服务端超时、JSON 最大 512 KiB。未知字段、大小写别名、重复 JSON 键、未知或重复查询参数均被拒绝。计数/版本以规范十进制字符串传输,不能带正号、前导零或小数。
除两个列表接口外,不接受查询参数。没有公开硬删除、修改过滤、修改历史起点或替换所属凭据的接口。
## 2. 创建订阅
POST /v1/subscriptions,Content-Type: application/json,Idempotency-Key: 调用方持久化的操作身份。
| 字段 | 要求 |
| --- | --- |
| name | 必填,1–128 个 ASCII 字符,首字符字母/数字,其余允许字母/数字/点/下划线/连字符;命名空间内唯一 |
| queue_id | 必填,32 位小写十六进制 ID;固定为该队列 |
| types | 可选数组,省略或 [] 表示四类全部;否则必须为四类的非重复子集 |
| from_sequence | 可选 uint64 十进制字符串;省略或 null 从创建时已提交流头之后开始;"0" 从现存结果日志开始 |
| max_pending | 可选整数,省略或 0 使用 100,自定义 1–1000 |
| max_attempts | 可选整数,省略或 0 使用 20,自定义 1–100 |
四类结果为 task.succeeded、task.failed、task.cancelled、task.needs_review。安全重试的 task.requeued 不在此集合。needs_review 不是成功或确定失败;同一个任务以后可能产生另一条权威结果,必须按 event_id 去重,不能只按 task_id 去重。
```json
{
"name": "object-results",
"queue_id": "11111111111111111111111111111111",
"types": ["task.succeeded", "task.failed", "task.cancelled", "task.needs_review"],
"max_pending": 100,
"max_attempts": 20
}
```
首次成功 HTTP 201;相同身份/内容重报 HTTP 200,deduplicated=true,返回原快照。请求尚未确认时必须保留原键、原请求和原 from_sequence,不能换键重新追到最新位置。
```json
{
"request_id": "77777777777777777777777777777777",
"data": {
"subscription": {
"id": "22222222222222222222222222222222",
"queue_id": "11111111111111111111111111111111",
"credential_id": "88888888888888888888888888888888",
"name": "object-results",
"revision": "1",
"enabled": true,
"types": ["task.cancelled", "task.failed", "task.needs_review", "task.succeeded"],
"cursor_sequence": "12",
"pending_count": 0,
"max_pending": 100,
"max_attempts": 20,
"created_at": "2026-09-17T10:00:00Z",
"updated_at": "2026-09-17T10:00:00Z"
},
"deduplicated": false
}
}
```
类型集合按固定顺序规范化。cursor_sequence 表示已经扫描并建立交付记录的位置,**不是确认水位**。pending_count 包含等待、租约中和次数耗尽记录。配置 revision 不随物化/ACK 用量变化。
每命名空间最多 64 个订阅。from_sequence 不能超过已提交流头,也不能早于已清理的保留边界。现存日志从迁移 000013 启用,不补造此前的任务结果。
## 3. 查询和控制订阅
GET /v1/subscriptions/{id} 返回 data 为上节 subscription 对象,HTTP 200。幂等写回执是原状态;查询接口才读取当前配置和用量。
GET /v1/subscriptions 必须带 queue_id;可选 limit=1–100(省略 20)和 cursor。返回 data.items 为 subscription 数组,另有可选 next_cursor。列表只包含当前凭据在这个队列中的订阅,按 created_at DESC、id DESC 排序。游标不能换队列、凭据或命名空间使用。
POST /v1/subscriptions/{id}:configure 只改变 enabled,要求以下三个字段全部显式提供:
```json
{
"expected_revision": "1",
"enabled": false,
"reason": "receiver maintenance"
}
```
expected_revision 必须匹配当前配置版本,reason 为非空字符串、最多 1024 个 Unicode 字符。成功 HTTP 200,data 为 SubscriptionResult,即 `{subscription, deduplicated}`。恢复时设置 enabled=true,并使用新的操作身份与查询取得的版本。重复原命令必须保留第一次的版本,不能把响应的新版本放回原键。
暂停停止新物化/领取,保留已有结果、交付和租约;仍允许消费者确认已可靠保存的结果。暂停不意味着已发消息被撤回。相同布尔值的新命令仍作为新的带审计配置操作增加版本。
## 4. 交付历史、详情和人工重试
GET /v1/subscriptions/{id}/deliveries 支持 limit、cursor,以及可选 state:PENDING、LEASED、ACKED、EXPIRED。不带 state 返回全部已物化交付。返回 data 为 `{items: [...], next_cursor?: "..."}`,按创建时间及 ID 倒序分页。游标绑定订阅、凭据、命名空间与状态过滤,不能混用。
列表项 EventDelivery 的字段如下:
| 字段 | 类型与含义 |
| --- | --- |
| id / subscription_id / event_id | 32 位小写十六进制身份;同一事件向同一订阅的交付 ID 不因补发变化 |
| sequence | 来源结果序号,uint64 字符串 |
| revision | 交付并发版本,uint64 字符串;领取、恢复、确认和人工重试时变化 |
| state | 四种交付状态 |
| created_at / available_at | UTC 创建时间和最早可重发时间 |
| lease_epoch | uint64 字符串,从未发送时为 "0",每次领取递增 |
| lease_until | 可选 UTC 租约截止;仅 LEASED 存在 |
| attempts / attempt_budget | 已用次数和累计预算,整数;人工重试不重置 attempts |
| acknowledged_at | 可选 UTC 首次有效确认时间,仅 ACKED 存在 |
GET /v1/subscriptions/{id}/deliveries/{delivery_id} 返回 data 为 `{delivery: EventDelivery, event: ResultEvent}`。事件字段见下一节的 event.deliver 示例。该接口不返回 lease_secret 或 token_digest,也不允许跨订阅查询同一个交付 ID。
分页是运维查询,不是一致性快照或可靠消费协议。并发状态变化可能改变过滤结果,不能通过列表遍历代替交付确认。未物化结果尚无交付记录,不会出现在该列表。
POST /v1/subscriptions/{id}/deliveries/{delivery_id}:retry 只允许 EXPIRED,带稳定 Idempotency-Key:
```json
{
"expected_revision": "3",
"reason": "receiver persistence failure repaired",
"additional_attempts": 2
}
```
成功 HTTP 200,data 为 `{delivery: EventDelivery, deduplicated: false}`;交付变为 PENDING,追加 1–100 次预算,累计预算最多 1000000。已有次数、代次、事件身份和积压占用不归零。重报返回原回执,不再追加。
当前租约恢复由启用订阅的后续领取触发;没有消费者或订阅暂停时,租约可能在截止后仍显示 LEASED。查询本身不修改状态,人工重试也不能绕过状态要求。独立后台清扫、历史清理和凭据安全重绑仍待后续完成。
## 5. WebSocket 建连与边界
入口 /v1/events/connect,Sec-WebSocket-Protocol: orbit.events.v1,Authorization 使用请求头。拒绝 URL 参数及跨来源 Origin;不使用 Cookie。升级前先验证 events:consume,随后 hello 再校验具体订阅所有者和队列范围。读权限不能替代消费权限,消费权限也不隐含读取所有历史的权限。
每帧为一个文本 JSON,最大 512 KiB,禁用压缩。固定 protocol_version="1",message_id 是调用方生成的 32 位随机十六进制标识。服务端响应使用新的 message_id,以 reply_to 关联请求。message_id 只用于相关性;ACK 的持久化去重依据是交付 ID、代次和秘密。
连接后 5 秒内发送 hello,不能带 session_id、reply_to 或 error:
```json
{
"protocol_version": "1",
"type": "events.hello",
"message_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"body": {"subscription_id": "22222222222222222222222222222222", "capacity": 1}
}
```
capacity 必填整数 1–32,表示该连接允许持有的交付数量,在数据库事务内校验。首版连接期间不可修改,调整容量需要关闭并重新连接。订阅仍可以由同一凭据的多个连接共同消费,各连接竞争持久化交付。
```json
{
"protocol_version": "1",
"type": "events.welcome",
"message_id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"reply_to": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"session_id": "44444444444444444444444444444444",
"body": {
"session_id": "44444444444444444444444444444444",
"subscription_id": "22222222222222222222222222222222",
"capacity": 1,
"lease_duration_ms": 30000,
"heartbeat_interval_ms": 15000,
"server_time": "2026-09-17T10:00:00Z",
"enabled": true
}
}
```
session_id 由服务端生成,仅标识当前连接。后续请求必须带这个值。断线后不恢复旧连接身份,也不删除租约;新的连接使用新 session_id,但可以用上一连接保留的最近证明核对 ACK。
节点最多 128 条事件连接。领取池全局最多 4 个在途、每应用最多 1 个;等待加在途最多 128,按认证应用及订阅轮转。每连接读队列 4、写队列 8,每个扫描轮次最多排入 4 次领取。慢 SQL 领取和控制收发分开;领取权及容量仍由 MySQL 决定。
这些是当前节点保护参数,不是生产吞吐承诺,也没有跨节点全局公平或应用连接席位预留。Worker 连接使用独立入口、协议与连接池。
## 6. 交付与可靠确认
服务端先持久化交付租约,再发送 event.deliver。以下 lease_secret 是全零字节编码的文档占位值,实际由服务端生成随机秘密:
```json
{
"protocol_version": "1",
"type": "event.deliver",
"message_id": "cccccccccccccccccccccccccccccccc",
"session_id": "44444444444444444444444444444444",
"body": {
"delivery": {
"created_at": "2026-09-17T10:00:00Z",
"id": "33333333333333333333333333333333",
"subscription_id": "22222222222222222222222222222222",
"event_id": "55555555555555555555555555555555",
"sequence": "13",
"revision": "2",
"state": "LEASED",
"available_at": "2026-09-17T10:00:00Z",
"lease_epoch": "1",
"lease_until": "2026-09-17T10:00:30Z",
"attempts": 1,
"attempt_budget": 20
},
"event": {
"id": "55555555555555555555555555555555",
"sequence": "13",
"type": "task.cancelled",
"aggregate_kind": "task",
"aggregate_id": "66666666666666666666666666666666",
"aggregate_revision": "2",
"occurred_at": "2026-09-17T10:00:00Z",
"details": {"state": "CANCELLED", "reason": "CANCEL_REQUESTED"},
"snapshot": {
"schema_version": 1,
"task_id": "66666666666666666666666666666666",
"queue_id": "11111111111111111111111111111111",
"state": "CANCELLED",
"revision": "2",
"effect_uncertain": false
}
},
"lease_secret": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}
}
```
snapshot 还可包含业务 result 和 current_attempt_id;result 用原始 JSON 文本保留数值精度。details 是来源事件的元信息,按事件类型演进;不含任务输入或交付秘密。结果快照不可变。
接收方应在本地事务中使用 event.id 作为 Inbox 唯一键,完成可靠保存或业务事务后才确认。不得先 ACK 再把消息仅放到内存队列。耗时业务可先可靠写入本地 Outbox,再由业务侧继续处理。平台首版不提供交付续租;30 秒租约用于尽快持久化接收结果。
```json
{
"protocol_version": "1",
"type": "event.ack",
"message_id": "dddddddddddddddddddddddddddddddd",
"session_id": "44444444444444444444444444444444",
"body": {
"delivery_id": "33333333333333333333333333333333",
"epoch": "1",
"lease_secret": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}
}
```
```json
{
"protocol_version": "1",
"type": "ack",
"message_id": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"reply_to": "dddddddddddddddddddddddddddddddd",
"session_id": "44444444444444444444444444444444",
"body": {
"delivery_id": "33333333333333333333333333333333",
"event_id": "55555555555555555555555555555555",
"epoch": "1",
"acknowledged_at": "2026-09-17T10:00:01Z",
"deduplicated": false
}
}
```
确认响应丢失时,保留原 delivery_id、epoch 和 lease_secret 重报;可以在新连接中使用新的 session_id 重报。已确认时返回原 acknowledged_at 和 deduplicated=true,不再次扣减积压或写重复确认审计。
租约到期后,最新证明若尚未被新代次替换,仍可迟到确认。新的发送产生后旧证明返回 DELIVERY_LEASE_LOST;接收方已保存的 Inbox 仍保留,下一次收到同一事件时检查已有记录,再用新交付证明 ACK。不能把旧证明替换为猜测的新代次。
交付是至少一次语义;连接关闭、写超时、ACK 响应丢失都可能导致重复。不得依靠本地内存“见过”集合宣称业务恰好一次。
## 7. 心跳、错误和关闭
客户端建议每 15 秒发送一次 events.ping,body 为 {};服务端以 ack 回复 `{server_time, enabled}`,同时重新校验凭据与订阅范围。收到结果也不能替代客户端心跳。连续 60 秒没有有效读取到客户端帧会关闭连接;写入超时为 5 秒。暂停中的订阅仍能连接、心跳和确认已有交付。
```json
{
"protocol_version": "1",
"type": "events.ping",
"message_id": "ffffffffffffffffffffffffffffffff",
"session_id": "44444444444444444444444444444444",
"body": {}
}
```
错误帧使用 type="error" 与 error={code,message},无 body。请求错误带 reply_to;后台领取发现授权失效或其他错误时可以没有 reply_to。hello 绑定失败时没有 session_id,绑定后的错误带当前 session_id。非法信封、二进制消息或错误会话直接按 WebSocket 1008 关闭,超限消息由 WebSocket 关闭。
| 错误码 | HTTP 状态 / WSS 行为 |
| --- | --- |
| INVALID_ARGUMENT | 400;命令体无效时可返回 WSS error,非法信封直接关闭 |
| UNAUTHENTICATED | 401;已连接时发送错误并关闭 |
| PERMISSION_DENIED | 403;已连接时发送错误并关闭 |
| NOT_FOUND | 404;订阅/交付不在凭据与父资源范围;连接内发生时关闭 |
| STATE_CONFLICT | 409;版本、状态或累计预算不允许操作 |
| IDEMPOTENCY_CONFLICT | 409;同键内容不同 |
| SUBSCRIPTION_LIMIT | 409;命名空间订阅数达到 64 |
| EVENT_HISTORY_EXPIRED | 410;WSS 领取发现历史缺口则报错并关闭,不静默跳到最新位置 |
| DELIVERY_LEASE_LOST | WSS ACK 证明已替换或无效;连接保留,等待新交付或核对历史 |
| PROTOCOL_REQUIRED | 升级前 426,必须指定 orbit.events.v1 |
| CAPACITY_EXCEEDED | 升级前 429,Retry-After: 1 |
| UNAVAILABLE | 503;WSS 中数据库等临时失败会报错并关闭,重连仍保留原交付/确认身份 |
断线/节点关闭不清除持久化交付。领取恢复会在预算内补发,或保留 EXPIRED。客户端关闭时应先完成自己的本地持久化、保存未核对 ACK 的证明,再关闭连接;平台不能保证物理撤回已经写入网络的消息。
## 8. 管理 SDK 与完整可运行示例
已实现的 Go 类型和方法:[类型](../../sdk/go/subscription_types.go)、[管理方法](../../sdk/go/subscriptions.go)。下面是应用函数内片段,client 已由 NewClient 创建,key 应先持久化:
```go
created, err := client.CreateSubscription(ctx, key, orbit.CreateSubscriptionRequest{
Name: "object-results",
QueueID: queueID,
MaxPending: 100,
MaxAttempts: 20,
})
if err != nil { return err }
current, err := client.GetSubscription(ctx, created.Subscription.ID)
if err != nil { return err }
_, err = client.ConfigureSubscription(ctx, current.ID, pauseKey, orbit.ConfigureSubscriptionRequest{
ExpectedRevision: current.Revision,
Enabled: false,
Reason: "receiver maintenance",
})
return err
```
[完整管理示例](../../sdk/go/examples/subscriptions/main.go) 的启动、输入输出、失败和保留规则见 [场景文档](../examples/subscriptions.md)。它演示管理与幂等,不启动结果消费者。WSS 连接 SDK、自动重连和持久化 Inbox 示例仍在下一阶段开发,不能把测试中的原始 WebSocket 辅助代码当成正式 SDK。
机器可读消息契约见 [events.schema.json](../../protocol/eventsv1/events.schema.json),使用 Draft 2020-12,clientFrame/serverFrame 分方向约束。运行时额外拒绝重复 JSON 键、超范围 uint64 及非规范 base64url。Schema 不代替授权、状态机、租约及事务校验。

View File

@@ -106,3 +106,12 @@
| 暂停与恢复 | 暂停 → 接收在途结果 → 核查 → 继续 | 历史与预算保留,已成功工作不重复 | 暂停/开始竞争、恢复时已过期 | P5 待开发 |
每个待开发场景最终都需提供独立目录、完整源码、配置模板、启动与验证命令,不能只留下概念流程图。配套回归会先使用可控的模拟处理器或外部服务,不对真实系统产生副作用。
## 已可运行:订阅管理与幂等回执
- 源码:[订阅管理示例](../../sdk/go/examples/subscriptions/main.go),[启动、输入输出和异常说明](subscriptions.md)。
- 前置:当前迁移及服务,具备 events:manage/read/consume 的专用开发凭据;初始化显式 --event-access。
- 步骤:创建 → 同键核对 → 暂停 → 恢复 → 查询与分页 → 最终暂停。
- 预期:duplicate=true、resumed_revision=3、final_enabled=false、revision=4、pending=0。
- 保留:每次运行留下一个已暂停订阅及审计;没有硬删除,受64个订阅上限约束。
- 范围:本例只演示管理SDK;自动消费、断线重连与持久化Inbox例子继续开发。

View File

@@ -0,0 +1,49 @@
# 订阅管理与幂等回执示例
状态:已提供 [完整可运行源码](../../sdk/go/examples/subscriptions/main.go)。演示 HTTP 管理 SDK;完整结果消费、重连及持久化 Inbox 示例继续开发。协议与字段见 [订阅接口](../api/subscriptions.md)。
## 前置和运行
需要 Go 1.27.1、已迁移到当前版本的 MySQL 8.4+ 和 Orbit 服务。使用新建专用开发命名空间,按环境注入 ORBIT_MYSQL_DSN;初始化显式开启事件权限:
```sh
go run ./cmd/orbit-migrate up
mkdir -p .local
chmod 700 .local
go run ./cmd/orbit-admin bootstrap \
--application subscription-example --namespace default --environment development \
--queue results --event-access --output .local/subscriptions.credentials.json
go run ./cmd/orbit
```
另一个终端从仓库根目录执行:
```sh
cd sdk/go
ORBIT_URL=http://127.0.0.1:8080 \
ORBIT_CREDENTIALS_FILE=../../.local/subscriptions.credentials.json \
go run ./examples/subscriptions
```
也可设置 ORBIT_TOKEN 和 ORBIT_QUEUE_ID,环境值优先于凭据文件。生产接入使用 HTTPS;不要提交凭据文件或在命令行打印秘密。
## 调用链和输出
创建四类结果订阅 → 同键同内容再次创建 → 查询并按版本暂停 → 恢复 → 检查当前配置及队列订阅列表 → 最后暂停本次订阅。
```text
subscription=<本次订阅ID> duplicate=true resumed_revision=3
final_enabled=false revision=4 pending=0
```
示例不会创建或执行业务任务,也没有消费者领取,因此不物化交付记录。结果日志仍由正常任务事务生成;此例验证管理调用、显式布尔控制和原幂等回执。
## 异常、重报与保留
- 缺少事件动作返回 PERMISSION_DENIED;已有普通凭据不会因新版本自动取得权限,应由有授权的管理流程签发所需权限。
- 名称/幂等内容冲突、预期版本过期、订阅数达到 64 均返回明确错误,SDK 不自动换键或覆盖版本。
- 网络超时不能证明没有提交。实际应用必须先持久化操作身份及原请求;按相同键和原版本核对。示例中的随机身份用于隔离每次演示,不是生产操作日志实现。
- 每次运行留下一个已暂停的独立订阅及审计;没有硬删除。重复运行会占用命名空间的订阅数量,应在专用可重建演示环境执行,不手工删除生产记录来绕过上限。
- 示例输出不包含凭据或交付秘密;异常非零退出。
make integration 会在一次性数据库、真实 Orbit 进程中执行这个例子。管理 SDK 集成还验证实际 30 秒交付租约到期后查询 EXPIRED、带版本追加预算、重新交付和确认;其中消息收发是测试辅助代码,正式消费 SDK 另行交付。

View File

@@ -0,0 +1,82 @@
# P3 结果订阅通信与管理 SDK 回归
验证日期:2026-09-17。已接通 [订阅 HTTP/WSS](../api/subscriptions.md)、独立管理 SDK 和 [管理示例](../examples/subscriptions.md)。消费连接 SDK、自动重连、持久化 Inbox 场景以及订阅真实进程强杀仍未完成,P3 和持续目标继续进行。
## 1. 本阶段交付
- [HTTP 路由](../../internal/transport/httpserver/events.go):订阅创建、队列范围分页、当前状态、带版本启停;交付分页、冻结结果详情、EXPIRED 追加预算。
- [查询存储](../../internal/storage/mysql/event_queries.go):按认证所有者、命名空间和父资源范围读取,分页游标绑定类别、过滤和凭据。交付详情不暴露证明原文或摘要。
- [迁移 000014](../../internal/storage/mysql/migrations/000014_delivery_history.up.sql):为交付创建时间及可选状态过滤添加索引,不改业务字段、已有迁移或外键规则。模型仍为 35 个实体、289 个带中文注释的持久化字段。
- [事件 Hub](../../internal/transport/httpserver/event_hub.go):独立 orbit.events.v1 入口,凭据、订阅和队列授权;hello、冻结快照交付、ACK、心跳、会话校验、断线恢复和有界关闭。
- 每节点 128 条事件连接;异步领取池 4 个全局在途、同应用 1 个在途、等待加在途总量 128,按认证应用及订阅轮转。控制收发不被同连接的慢领取阻塞。连接容量仍由持久化租约校验。
- [严格解码](../../internal/transport/httpserver/event_codec.go) 与 [公开 Schema](../../protocol/eventsv1/events.schema.json):方向、必填、字段精确大小写、规范 uint64、证明格式和消息大小;JSON 重复键、伪造响应字段或错误会话被拒绝。
- [管理 SDK](../../sdk/go/subscriptions.go) 与 [独立 DTO](../../sdk/go/subscription_types.go):七个管理/查询方法及类型,保留大整数和显式布尔控制,不依赖服务端或数据库包。
- [完整管理例子](../../sdk/go/examples/subscriptions/main.go) 已纳入统一隔离环境脚本。原始 WSS 测试辅助代码不作为正式消费 SDK 对外提供。
## 2. 验证环境与结果
macOS、Go 1.27.1、Docker MySQL 8.4.10、一次性数据库、两个 httptest TLS 节点和真实 Orbit 进程。回归随机凭据不打印,脚本结束清理自己的容器。
```sh
make fmt test vet build
make integration
cd sdk/go
GOWORK=off go test -race ./...
```
| 分组 | 本次结果 |
| --- | --- |
| 全量 MySQL 集成 | PASS,90.688 秒 |
| 新 HTTP/WSS 七组场景 | PASS,8.42 秒 |
| 原订阅核心 / 提交顺序 | PASS,3.41 / 0.59 秒 |
| SDK Worker 协议和运行器 | PASS,87.434 秒 |
| SDK 计划生命周期 | PASS,5.645 秒 |
| SDK 队列 / 应用额度 | PASS,4.526 / 1.403 秒 |
| 新订阅管理 SDK | PASS,32.16 秒,包总计 33.391 秒;包含实际 30 秒租约 |
| 既有七组真实进程故障 | PASS,27.534 秒 |
| 完整 SDK 示例 | 13 个全部通过 |
| fmt / race / vet / build / 独立 SDK 模块 | 全部通过 |
完整套件后补充交付正向翻页、跨订阅/命名空间详情拒绝及 Schema uint64 上界一致性检查;对应专项数据库与传输回归再次通过,28.651 秒。文档六个完整 WSS 消息也通过公开 Schema 校验。普通不带外部环境的 go test 会跳过集成,不能替代这些证据。
## 3. HTTP 与 WSS 实际验证
[真实数据库与 TLS 测试](../../internal/storage/mysql/event_transport_integration_test.go) 覆盖:
1. HTTP 创建/原键回执、显式 enabled、暂停继续、规范计数字符串、非法/重复查询参数;订阅与交付的稳定分页,游标不能换过滤范围。
2. 交付详情只能从正确的命名空间、所属凭据与订阅读取。JSON 不包含 lease_secret 或 token_digest;人工追加预算保留原身份、次数与幂等回执。
3. 握手拒绝缺失凭据、仅有 Worker 权限、错误子协议、URL 参数与跨来源 Origin。事件协议与 Worker 协议隔离。
4. WSS 交付接近 240 KiB 的结果,包含 9007199254740993 和大量 `<&>`,接收内容保持精确,帧仍在 512 KiB 以内。所有真实收发帧都通过方向对应的 Schema。
5. 容量 1 时即使存在第二条结果也只有一个有效交付租约;心跳正常,确认释放容量后继续派发。重复 ACK 使用原确认时间、无重复扣减。
6. 节点 A 断线后节点 B 恢复同一交付及事件,产生更大的代次和新秘密,拒绝旧证明。这里通过测试数据库提前设置到期验证路径;自然到期由独立 SDK 测试覆盖。
7. 消费者发送 ACK 后,查询数据库确认已提交,再丢弃未读取的响应并关闭连接。新节点使用原证明重报返回 deduplicated=true、原时间和唯一确认审计。
8. 已建立连接中的凭据撤销生效;错误 session_id 按 1008 关闭。关闭 Hub 保留已落库租约,拒绝新连接,另一节点仍能核对原证明。
[节点保护测试](../../internal/transport/httpserver/event_hub_test.go) 人为挂起领取直到 context 取消。在领取仍阻塞时,控制心跳在有界时间内回复;连接满额返回 429 和 Retry-After,断开后释放槽位,关闭时取消在途领取并清空槽位。测试将连接容量缩为 2 以减少资源,另外断言默认值为 128。
[解码与文档契约测试](../../internal/transport/httpserver/event_codec_test.go) 验证请求不能携带 reply_to/error/错误会话字段,拒绝缺失显式控制意图、重复键和别名。Schema 与实际解码都接受 uint64 最大值并拒绝溢出、零执行代次和前导零;HTTP 起始游标另外允许 "0"、null 或省略。
## 4. 独立 SDK 与示例证据
[SDK 管理集成](../../sdk/go/subscriptions_integration_test.go) 在真实进程上创建独立队列及订阅,执行 Create/Get/List/Configure;产生取消结果后通过测试 WSS 辅助连接领取一次并断开。另一个连接保持可领取状态,测试等待真实 30 秒租约到期,没有改数据库时间。
随后 SDK 通过 ListEventDeliveries 查到 EXPIRED,GetEventDelivery 读到冻结结果和已用次数;RetryEventDelivery 带版本追加一次预算,同键重报不再追加。下一次 WSS 交付保留事件和交付 ID、代次为 2,确认后 SDK 查到 ACKED、pending_count=0,配置 revision 未被消费计数污染。
[SDK 线格式测试](../../sdk/go/subscriptions_test.go) 核对显式 from_sequence=0 编码为字符串而不丢失,enabled=false 不被省略,9007199254740993 版本往返精确,非法路径和超限分页不发起网络请求。
管理示例实际输出(ID 每次不同):
```text
subscription=<本次订阅ID> duplicate=true resumed_revision=3
final_enabled=false revision=4 pending=0
```
13 个完整示例为 health、tasks、worker-protocol、worker、history、retry、schedules、cancellation、resources、keyed-concurrency、priority、subscriptions、application-resources。原七项进程故障覆盖的是任务/Worker/计划/额度/优先级等既有能力,不能当作新订阅已完成进程强杀验收。
## 5. 升级与尚未验证事项
- 真实 v7 升级继续保留 325 项原索引列定义,70 个历史外键最终为 0,错误手工外键仍被就绪检查拒绝。新交付分页索引作为新增项处理;v4、v9、v12 升级与模型映射回归通过。
- 完整事件连接 SDK、自动心跳/重连、持久化 Inbox 示例与真实节点进程强杀,仍是 P3 剩余工作。此阶段不把管理 SDK 或测试 WSS 辅助代码算作已经交付消费 SDK。
- 当前按启用订阅领取触发租约恢复;独立后台清扫、历史保留/清理、容量告警和安全凭据重新绑定未交付。
- 消费至少一次;业务效果需要接收方以事件 ID 去重并事务保存,再 ACK。连接关闭不能物理撤回已发送内容,也不提供业务恰好一次保证。
- 当前连接/领取上限是节点保护值,没有证明百万接入规模的吞吐、跨节点公平、全套请求治理或生产高可用。MySQL 故障切换、备份恢复、RPO/RTO 与目标容量仍需 P6 实测。

View File

@@ -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) 已支持创建、查询、版本发布、暂停/恢复、触发历史与完整示例。流程构建等后续接口按 [开发计划](../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 继续开发。流程构建等后续接口按 [开发计划](../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,9 @@ fmt.Println(cancelled.Task.State)
| 方法 | 请求 / 返回 | 文档 |
| --- | --- | --- |
| CreateSubscription / GetSubscription / ListSubscriptions | 订阅受理、当前配置与队列分页 | [订阅](../api/subscriptions.md) |
| ConfigureSubscription | 显式暂停/继续与版本、原因 | [订阅](../api/subscriptions.md) |
| ListEventDeliveries / GetEventDelivery / RetryEventDelivery | 交付历史、冻结结果和耗尽后追加预算 | [订阅](../api/subscriptions.md) |
| CreateSchedule / GetSchedule / ListSchedules | 计划受理、详情与队列分页 | [计划](../api/schedules.md) |
| PublishSchedule / GetScheduleVersion | 不可变发布与历史版本 | [计划](../api/schedules.md) |
| PauseSchedule / ResumeSchedule / ListScheduleOccurrences | 控制与触发记录 | [计划](../api/schedules.md) |
@@ -180,3 +183,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 提供流程构建发布、信号、等待、轮询与人工恢复。每项随实现同步交付可运行示例及异常回归。
## 订阅管理示例
[完整运行方法](../examples/subscriptions.md) 使用显式 --event-access 凭据,执行创建/同键核对/暂停/恢复/查询,再暂停本次订阅并保留审计。源码见 [examples/subscriptions](../../sdk/go/examples/subscriptions/main.go)。WSS 连接 SDK、自动消费与持久化 Inbox 示例尚未交付,不能将此管理示例当作完整可靠消费链路。