29 lines
5.8 KiB
Markdown
29 lines
5.8 KiB
Markdown
# 第三方任务发现事件游标分页 v0.3(项目内提案)
|
||
|
||
> 仅替换 [`v0.2`](v0.2.md) §2.5 的任务发现目标;其他 SaaS 项目内业务接口仍按 v0.1。设计依据为 [`plan-0926.md`](../plan-0926.md)。本文件、Schema 和 Mock **未经 SaaS/业务签收,不是现网接口或生产合同**;当前本地仅运行 v0.3;v0.2 仅作历史,真实 SaaS 尚未切换。
|
||
|
||
## 2.5 唯一任务发现路径
|
||
|
||
`GET /internal/v1/dispatcher/tasks?after=<cursor>` 由指定 Dispatcher 使用既有 `X-DISPATCHER-id` / `X-DISPATCHER-SECRET-KEY` 读取自己的任务。首次无本协议游标时发送 `after=0`;之后只发送已和任务状态一起持久化的 `next_cursor`。`after` 是**该 D 范围内单调、不可复用的事件 ID**,不是任务 ID、页号或任务配置版本。规范形式是无前导零的十进制 uint64 字符串;只允许起点为 `0`。D 身份不可更换来绕过游标。
|
||
|
||
所有 HTTP 200 均采用同一形状:`schema_version=task-discovery.v0.3-proposal`、`dispatcher_id`、`tasks[]` 和 `next_cursor`。`tasks[]` 中每项为 `task_id`、`tenant_id`、原值 `tenant_key`、`status`、`task_revision`;**没有** `changes`、`operation`、`snapshot`、`mode`、单项 `event_id`、SaaS 队列名或任务页 token。同一任务有更晚的事件时可再次出现。SaaS 对每个任务只需返回**最新状态**,不要求回放所有中间状态;已有任务的 `status` 变化更新 SaaS 状态;新 `paused`、`stopped`、`finished`、`removed` 可关闭或保持准入。**后续 `running`(即使更新且已持久化)只更新 SaaS 状态与游标,不直接解除已持久的 `paused`;必须收到 MQ `resume` 并由任务只读接口新鲜确认 `running` 才能重开原队列。**其他字段仍须校验身份、版本及归属。任务具体执行配置仍由独立只读接口取得,不由发现列表取代。
|
||
|
||
SaaS 在响应中按其事件序号升序选取尚未返回的**最新任务记录**。每页至多 256 项,活动任务总量仍受单 D 256 上限约束;撤销墓碑的累计数量不在这个活动上限之内。非空页的 `next_cursor` 必须是**最后一个实际返回任务**的事件 ID,并严格大于请求的 `after`,绝不能前进到尚未返回的更新之后。空页表示本轮追平,`tasks=[]` 且 `next_cursor=after`;D 可在首次追平后开放经核验的新执行,其后约 30 秒再次查询。不得依靠“不足 256 项”断定追平:无论每页实际数量,D 都必须继续读取直到空页。若页内同一任务重复出现、任务身份发生冲突或游标不前进,D 拒绝整页而不是挑一条使用。
|
||
|
||
**信任边界**:用户确认仅返回响应级 `next_cursor`,不返回每项 `event_id`。D 可以验证游标形式、单调前进、响应身份和任务数据,但**不能独立证明 SaaS 的页内顺序及没有漏项**;SaaS 必须保证选择完整、有序、无跳跃,真实 SaaS 签收及并发分页故障测试属于外部门禁。Mock 正例不代签这一保证。
|
||
|
||
### 状态和撤销
|
||
|
||
`status` 只允许 `running`、`paused`、`stopped`、`finished`、`removed`。`removed` 是该 D 归属撤销的**任务墓碑**,必须保留原任务/租户身份;某任务没有出现在本页不表示撤销。SaaS 先持久化 stop/撤销及其必要回执、协调自己拥有的任务队列退役,再发出墓碑;D 收到后停止新接纳,不清理执行恢复、已入队结果、幂等或未知占用,也不自动强挂在途通话。paused 保留原积压;MQ 控制即时执行,不等轮询。发现页无论新旧 `running` 均不解除已持久的 paused/stopped 屏障;stopped 同任务不可逆,removed 不撤销旧执行事实。
|
||
|
||
### 一致性、错误和恢复
|
||
|
||
- 任务页与游标同一 SQLite 事务提交;页提交失败不能推进游标。首次启动/恢复在读到**空页**并完成授权、归属和 SaaS 预建队列核验前不消费执行消息。中途失败、响应无效或网络不可用时保留本地任务、控制、执行事实与错误记录,拒绝新执行;MQ 即时控制仍继续。不能拿空列表推断未返回任务已经 removed。
|
||
- 游标**永不过期**:不提供 410 或 `cursor_expired`,也不因时间推移要求 D 自动从零重建。SaaS 必须长期维护每个任务可追溯的最新事件位置和撤销墓碑;对任意曾提交的 D 游标,不能返回遗漏后续状态的伪造空页。`after=0` 的首次分页同样有界而不一次返回全部。序号回退、持久记录丢失或无法证明连续时返回明确错误,D fail-closed 并等待处理,不暗中重置。
|
||
- HTTP 错误:400 `invalid_cursor`、401 `unauthorized`、403 `dispatcher_not_authorized`、503 `service_unavailable`;成功只允许 200。返回体按本版本严格 Schema,错误正文不含任务或凭据。具体身份、响应大小与实际 SaaS 系统行为仍待签收。
|
||
- 当前 v0.2 游标**不能**重解释为事件 ID;切换前必须核验旧任务、积压与在途执行安全收口。新版只运行一个任务发现消费者,不保留旧 `changes`/410/分页 token 兼容路径或 HTTP→MQ 回退。SaaS 独占建队、绑定和退役,D 只消费预建队列。
|
||
|
||
## 验收及容量阻断
|
||
|
||
本地 Mock 要覆盖初始多页、空页、同一任务再出现、撤销、跨页并发更新、重启原游标续读、重复页、事务回滚、发现页 `paused→running` 后准入仍暂停直到 MQ `resume`+新鲜任务核验、暂停/停止竞态和 queue-only 消费;所有关键边界 fail-closed,不重拨。实时控制与最终结果的其他 v0.1 业务合同不变。活动任务 256 上限**不约束永久墓碑**;历史撤销增长、离线追赶时间、SaaS 数据保留及灾备后序号连续性未签收,不能声称容量有界或真实联调/生产可用。
|