Files
orbit/docs/api/worker.md

147 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Worker 通信与执行契约 v1(开发中)
已实现服务端执行事务、WebSocket 接入、[独立 Go 底层协议 SDK](../sdk/worker.md) 和 [自动 Worker 运行器](../sdk/worker-runtime.md),通过真实 WSS、自动续租、ACK 丢失/持续断网恢复及有界退出回归。**P2 尚未完成**:尝试/证据查询、完整协议 Schema 与完整阶段验收继续推进。底层 WorkerConnection 不自动管理处理器;NewWorker / Run 提供自动管理。
## 1. 接入与身份
入口 `GET /v1/worker/connect`,子协议必须为 `pulse.worker.v1`。Authorization 使用 Bearer 凭据;禁止 URL 查询参数。握手先校验 workers:connect,消费控制操作再次校验 workers:consume、队列范围和凭据有效性。
初始化开发凭据可显式增加 Worker 权限:
```sh
go run ./cmd/orbit-admin bootstrap \
--application worker-demo --namespace default --environment development \
--queue objects --worker-access --output .local/worker.credentials.json
```
默认 bootstrap 仍只有 P1 的五项权限,现有凭据不会自动扩权。生产细粒度凭据管理在 P6 完成。
当前 orbit 命令监听 HTTP,开发可使用 ws;生产需要受控的 TLS 终止。WSS 回归使用真实 TLS 测试服务器及受信任测试证书,没有跳过证书校验。传输库锁定 github.com/coder/websocket v1.8.15,使用上下文、读写期限、子协议和默认 Origin 校验,关闭压缩。[上游版本记录](https://github.com/coder/websocket/releases/tag/v1.8.15)、[上游接口文档](https://pkg.go.dev/github.com/coder/websocket@v1.8.15)。
## 2. 帧格式
只接受 JSON 文本帧;单帧最多 512 KiB,业务结果最多 256 KiB。重复键、未知字段、无效 Unicode 和非法数值按 HTTP 相同规则拒绝。
```json
{
"protocol_version":"1",
"type":"task.start",
"message_id":"44444444444444444444444444444444",
"session_id":"33333333333333333333333333333333",
"body":{
"task_id":"11111111111111111111111111111111",
"attempt_id":"22222222222222222222222222222222",
"epoch":"1",
"lease_secret":"<执行秘密,仅通过受控连接传输>"
}
}
```
所有 ID 为 32 位小写十六进制;epoch、sequence、revision 用十进制字符串。message_id 标识一次操作,重连重报保持不变;session_id 必须换为当前连接身份。服务端响应以 reply_to 关联请求:
```json
{
"protocol_version":"1",
"type":"error",
"message_id":"55555555555555555555555555555555",
"reply_to":"44444444444444444444444444444444",
"session_id":"33333333333333333333333333333333",
"error":{"code":"LEASE_LOST","message":"attempt no longer grants execution authority"}
}
```
普通成功响应 type=ack,body 为下表对应结果。服务端主动派发 type=task.assign,无 reply_to。错误仅包含稳定分类和公开消息,不包含 SQL、业务参数或秘密。
## 3. 注册、恢复与容量
连接后 5 秒内必须发送 worker.hello,hello 不携带 session_id。
| hello 字段 | 含义 |
| --- | --- |
| instance_id | Worker 进程实例 ID;进程重启必须换新值 |
| instance_secret | 32 字节随机秘密的无填充 base64url 编码,共 43 字符;同进程重连保留 |
| queue_id | 当前进程绑定队列 |
| capabilities | 1–32 个 handler / handler_version / payload_version 精确版本组合,不可重复 |
| capacity | 总执行槽数量,1–256,不是可用槽的累加值 |
重连必须保持凭据、队列、处理器集合和进程秘密,处理器集合的排列顺序不影响身份。新连接递增会话代次并使旧会话失效。知道 instance_id 或 task_id 不能接管执行。
worker.welcome 返回 session_id、epoch、server_time、expires_at、lease_duration_ms、draining。当前窗口为会话 45 秒、执行租约最多 30 秒;注册及能力声明不等于开始授权。
**派发在收到第一次 worker.capacity 确认后才启用**,便于客户端先恢复旧尝试。
| 类型 | body | 返回 |
| --- | --- | --- |
| worker.resume | AttemptRef,字段见上方 task.start 示例 | 当前 LeaseStatus;成功迁移有效尝试到新会话,不创建尝试或延长期限 |
| worker.capacity | sequence 字符串、capacity 0–256、drain bool | 更新后的 welcome 字段 |
| task.assign | 服务端主动发送 | task 完整任务、attempt_id、epoch、lease_secret、lease_until、server_time |
capacity 使用单调递增序号和覆盖值。同序号同内容可重复,同序号不同内容或旧序号冲突;capacity=0 不释放已有任务的并发许可。drain=true 是进程级持久化排空,重连保留;后续必须保持 drain=true,尝试清除返回 STATE_CONFLICT。新进程使用新实例身份。
一个 Worker 进程当前绑定一个队列。可以运行多个 Worker 进程或后续 SDK 实例消费不同队列;不能通过重连静默切换队列。
## 4. 开始、续租与结果
| 类型 | body | 语义 |
| --- | --- | --- |
| task.start | AttemptRef | 领取后再次检查当前会话、尝试、租约、队列暂停及流程门控,保存 RUNNING 后确认 |
| task.renew | AttemptRef | 只续期当前 RUNNING;新消息可续租,重复原消息只取回原回执 |
| task.finish | ref: AttemptRef、outcome、result、error_code | 保存明确结果或未知效果;返回操作回执和当前授权 |
outcome 为 SUCCEEDED / FAILED / UNKNOWN。result 为非 null JSON 或证据引用。FAILED/UNKNOWN 必须带 1–64 字符的稳定 error_code;SUCCEEDED 不带错误分类。当前 FAILED 成为任务失败终态,UNKNOWN 转 NEEDS_REVIEW;策略化自动重试在 P3 实现。
开始、续租和结束返回 CommandResult:
```json
{
"receipt":{
"operation":"start",
"task_state":"RUNNING",
"attempt_state":"RUNNING",
"task_revision":"3",
"lease_until":"2026-09-17T08:00:30Z",
"disposition":"APPLIED"
},
"lease":{
"active":true,
"state":"RUNNING",
"lease_until":"2026-09-17T08:00:30Z",
"server_time":"2026-09-17T08:00:00Z"
},
"deduplicated":false
}
```
receipt 是原操作的不可变确认;lease 是本次重新核验的执行权,两者不能混用:
- 只有 lease.active=true、lease.state=RUNNING,且仍在保守的本地有效期内,才允许处理器推进。LEASED 的 active 只表示预占有效。
- 原 start 回执可能仍表示 RUNNING,但当前 lease.active=false;旧 ACK 不能让已过期、已结束或已被替换的尝试复活。
- 有效期不超过租约窗口、会话期限、任务总截止和从 authorized_at 起计算的单次尝试时限。
- 客户端需要扣除请求总耗时及安全余量,用本地单调时钟停止执行,不能假定本地墙上时钟与数据库一致。
- 同结果重报不重复释放许可或产生成功事件。同消息不同内容返回 IDEMPOTENCY_CONFLICT。
- 迟到、未开始或与已保存结果冲突的内容,经过身份和秘密验证后保存为 EVIDENCE_ONLY;不改变任务权威结果,不当作新的执行授权。
- 断线后原结果仍保留稳定消息 ID,可在新会话恢复/核对后重报。不会因重连创建第二次业务执行。
## 5. 领取、并发与回收
平台在同一事务中分配队列并发许可、创建尝试、递增任务执行代次和次数、保存 LEASED 与派发事件。领取本身消耗一次 max_attempts 预算,包括最终未获开始授权的尝试,预算不会因重连或过期恢复重置。
运行中的进程总容量跨其所有旧/新会话统计。声明容量减少、断开连接或重复结果,都不能重复释放额度。当前已实施队列 max_concurrency;rate_per_second、其他配额和调度策略在 P3 完成。
每个节点每 2 秒发起一批恢复扫描,每批最多 100 项:
- 未批准开始即到期:有剩余次数和截止时间时,延后 1 秒重派;否则 FAILED。
- 已批准开始后失联:NEEDS_REVIEW,尝试 UNKNOWN,不能因网络超时断定业务未生效。
- WAITING 超过总截止或最晚开始时间:FAILED。
- 回收再次锁定队列、任务、尝试并读取数据库时间,与续租/完成串行化;多个节点不能重复释放许可或重复终结事件。
此处的安全回收不是完整重试引擎;IDEMPOTENT 的自动重试和策略退避仍是 P3 工作。
## 6. 连接保护与当前边界
当前节点上限 128 条 Worker 连接;每连接入站队列 4 帧、出站队列 8 帧,派发保留 ACK 缓冲空间,每秒最多发起 4 次新派发尝试。读空闲期限 60 秒、写期限 5 秒、单个数据库控制操作期限 8 秒。慢消费者导致连接关闭时,已提交状态仍由稳定身份恢复。
这些是有界的开发基线,不代表经过容量压测的生产参数。当前同队列操作使用较粗的行锁协调,吞吐与公平性需要 P6 按负载验证。服务退出会显式关闭被 HTTP Upgrade 接管的连接;断开不会直接撤销外部已发出的请求。
运行中取消、完整 WS JSON Schema、凭据轮换和多节点进程级故障演练尚未全部完成。SDK 已实现自动重连/续租、非协作处理器的有界退出与状态报告;实际证据见 [P2 服务端阶段记录](../reports/phase-2-server.md) 和 [自动运行器回归](../reports/phase-2-worker-runtime.md)。