feat: add durable result journal and subscription delivery core

This commit is contained in:
2026-09-17 19:19:57 +08:00
parent 309ed75666
commit 1c3300cc5b
26 changed files with 2036 additions and 11 deletions

View File

@@ -1,6 +1,6 @@
# 数据模型与实现基线
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度、队列限速/背压和应用配额;优先级及节点公平领取已实现;订阅及流程编排继续开发。
更新:2026-09-17。数据库统一禁止外键。当前已实现任务执行、安全重试、运行中取消、计划调度、队列限速/背压和应用配额;优先级及节点公平领取已实现;可靠订阅持久化核心已实现,HTTP/WSS 与 SDK 接入及流程编排继续开发。
## 1. 模块与依赖
@@ -35,7 +35,7 @@
## 3. 实体清单
当前共 33 个实体:初始迁移包含 30 个,000003 增加进程会话头和迟到证据,000010 增加应用共享额度。迁移自身另有 `orbit_schema_migrations` 记录。
当前共 35 个实体:初始迁移包含 30 个,000003 增加进程会话头和迟到证据,000010 增加应用共享额度,000013 增加结果流与不可变结果快照。迁移自身另有 `orbit_schema_migrations` 记录。
| 模块 | Go 模型 | 数据表 |
| --- | --- | --- |
@@ -45,6 +45,8 @@
| platform | `Credential` | `orbit_credentials` |
| platform | `IdempotencyKey` | `orbit_idempotency_keys` |
| platform | `Event` | `orbit_events` |
| platform | `EventStream` | `orbit_event_streams` |
| platform | `ResultEvent` | `orbit_result_events` |
| platform | `Subscription` | `orbit_subscriptions` |
| platform | `Delivery` | `orbit_deliveries` |
| platform | `AuditEvent` | `orbit_audit_events` |
@@ -73,7 +75,7 @@
| weave | `InboxEvent` | `weave_inbox` |
| weave | `PendingAdvance` | `weave_pending_advances` |
设计文档中的 `task_events` / `result_events` 当前合并为 `orbit_events`,通过聚合类型、事件类型和状态版本区分;对外投递使用 `orbit_deliveries`,内部流程接续使用 `weave_inbox`。`weave_pending_advances` 保存暂停或分批推进后的续接检查点。
`orbit_events` 保存原始状态事件,`orbit_result_events` 使用相同事件 ID 保存队列、提交序号和冻结快照,`orbit_event_streams` 串行分配命名空间内的结果序号;对外投递使用 `orbit_deliveries`,内部流程接续预留 `weave_inbox`。`weave_pending_advances` 保存暂停或分批推进后的续接检查点。
## 4. 已落实的数据库约束
@@ -93,7 +95,7 @@
禁止“先查后另开事务写入”。需要防止并发状态改变的关联行在事务内持锁,统一锁顺序并保留必要索引。当前不提供硬删除;将来增加清理时,必须核验在途任务、引用、幂等恢复窗口与审计保留期,按有限批次处理,不能依赖数据库级联。直接绕过服务写 SQL 不受应用校验保护。
P1 已完成动作/队列授权、幂等摘要与原回执、受理事件及取消 CAS/审计事务;P3 扩展到运行中取消。流程及订阅目前只具备模型,后续实现也必须补齐事务内父资源归属验证,不能把模型存在当作已完成引擎。
P1 已完成动作/队列授权、幂等摘要与原回执、受理事件及取消 CAS/审计事务;P3 扩展到运行中取消。订阅已实现服务层及持久化核心,包含事务内队列、所有者和交付归属校验;HTTP/WSS 和 SDK 尚未接入。流程目前仍只具备模型,后续引擎也必须补齐事务内父资源归属验证。
P2 服务端已新增会话隔离、两步派发、租约、并发许可、结果回执/证据和恢复扫描,见 [执行协议](api/worker.md)。底层及自动 Worker SDK、尝试/证据查询、Schema 与进程故障恢复已通过 [P2 阶段验收](reports/phase-2.md)。
@@ -146,3 +148,7 @@ P2 服务端已新增会话隔离、两步派发、租约、并发许可、结
000012_dispatch_priority 开放既有 Priority,范围 0–9,并增加对应 CHECK。新增只读生成列 DispatchAt(AvailableAt 减 Priority 乘30秒)及 `(namespace_id,queue_id,state,dispatch_at,available_at,id)` 排序索引;历史默认优先级 0 的排序时间等于 AvailableAt。Generated 字段随退避和恢复自动重新计算,不允许独立写入。33 个实体、274 个直接声明的持久化字段全部保留中文注释,新增 SQL 字段和 Priority 的 COMMENT 同步更新,无外键。
派发先检查实际到期、截止、能力、门控和资源键,再按排序时间选择。节点公平领取池根据认证后的 ApplicationID 分组,再按命名空间/队列轮转;内存队列和短期空探测缓存可在重启后重建,不保存授权事实。详细约束、SDK 和升级步骤见 [优先级与公平派发](api/priority-and-fairness.md)。
## 13. 可靠结果订阅核心
000013_result_subscriptions 新增 EventStream、ResultEvent,扩展 Subscription 和 Delivery。289 个直接声明字段均有中文注释。生产事务在独立流行锁下分配序号,与任务、原事件和结果快照一起提交;消费事务不锁流行、任务、队列或应用额度。交付记录和扫描位置一起落库,每条交付单独持有租约和证明。重复确认不重复扣减积压,耗尽保留记录,人工追加预算不重置历史。详见 [订阅核心](09-result-subscription-core.md)。对外 HTTP/WSS 与 SDK 尚未实现,P3 继续推进。

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 场景已实现并通过全量回归;优先级及节点公平派发已实现并通过全量回归;可靠订阅待开发 |
| P3 Pulse 完整能力 | 安全重试、延时、Cron/固定间隔、漏跑策略、计划版本、运行中取消、并发许可、配额/背压、可靠结果订阅 | 多实例领取/触发竞争、重试上限、取消竞争、队列隔离、积压恢复、事件重复交付 | 进行中:安全重试、计划调度、运行中取消、队列控制/限速/积压背压、跨命名空间应用配额、显式资源键许可及 SDK 场景已实现并通过全量回归;优先级及节点公平派发已实现并通过全量回归;可靠订阅持久化核心已实现,HTTP/WSS、SDK 及断线故障回归继续开发 |
| P4 Weave 主流程 | SDK 定义构建与发布校验、实例、顺序/分支/结构化并行汇合、信号/定时等待、版本绑定 | 重启接续、早到/重复信号、并行竞争、内部事件重放、发布版本兼容;端到端通用流程 | 待开发 |
| P5 Weave 恢复与查询 | 有界查询、暂停继续、取消、人工核查恢复、控制门控与审计 | 轮次与尝试独立预算;暂停/开始/回执/超时竞争;恢复保留预算;未知结果不盲目重复 | 待开发 |
| P6 生产验证与运维 | 完整监控、凭据轮换、备份恢复、滚动升级、运行手册、可复现负载和故障工具 | 对照生产验收清单执行,报告环境/指标/遗留项;容量、RPO/RTO 按真实拓扑验证 | 待开发 |
@@ -48,6 +48,7 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
| 跨命名空间应用配额 | first 受理 → second 全局满额 → 释放 → 原键成功 → 恢复配置 | P3,已实现完整示例 |
| 跨队列共享资源 | 两队列同键 → 一个有效许可 → 递增序号 → 资源端拒绝旧写入 | P3,已实现完整示例 |
| 优先级与应用隔离 | 近同时到期按优先级 → 旧任务等待补偿 → 应用/队列轮转 | P3,已实现完整示例 |
| 可靠结果订阅 | 冻结结果 → 持久化交付 → Inbox 去重 → 确认/补发 → 耗尽后人工重试 | P3,已实现服务与存储核心;HTTP/WSS 和 SDK 待接入 |
| 并行生成结果 | 发布流程 → 并行执行 → 汇合 → 完成 | P4 |
| 审核与外部回执 | 提交流程 → 等待信号 → 授权信号注入 → 继续 | P4 |
| 外部作业轮询 | 提交一次 → 有界查询处理中状态 → 回执或最终结果 → 超限核查 | P5 |
@@ -74,6 +75,8 @@ SDK、文档与示例随 P1–P5 同步开发,P7 是全量核对,不是最
## 7. 文档入口
- [数据模型](07-data-model.md)
- [可靠结果订阅核心](09-result-subscription-core.md)
- [P3 订阅核心回归](reports/phase-3-result-subscription-core.md)
- [接口手册](api/README.md)
- [SDK 接入](sdk/README.md)
- [通用场景示例](examples/README.md)

View File

@@ -0,0 +1,94 @@
# 可靠结果订阅:持久化核心
状态:P3 的存储与服务层已实现并通过本地全量回归,见 [验证记录](reports/phase-3-result-subscription-core.md)。HTTP 管理接口、事件 WSS、独立 SDK 和可运行订阅示例尚未接入;不能把本页的方法当作已经发布的 SDK。对外接口接通后再冻结消息 Schema 和完整调用手册。
## 1. 已实现范围
结果来源限定为 task.succeeded、task.failed、task.cancelled、task.needs_review。安全重试产生的 task.requeued 不表示最终结果,不进入这个结果流。needs_review 表示需要核查,可能以后再产生权威结果,接入方不能把它当作成功或最终失败。
来源任务状态、原状态事件、不可变结果快照、结果流序号、原执行回执/审计在同一数据库事务中提交。任何一项失败都会回滚,Worker 不会收到“结果确认成功但没有可靠通知记录”的回执。
`orbit_events` 继续保存状态事件;新增 `orbit_result_events` 使用相同事件 ID 保存队列、提交序号和快照。快照包含 schema_version=1、task_id、queue_id、state、revision、result、effect_uncertain 和可空 current_attempt_id。不包含任务输入和租约秘密。结果使用 JSON 文本保存,保留大整数的原始数值文本。来源任务之后发生改变时,不会重新生成已发布的快照。
## 2. 为什么不能按最大事件 ID 推进
自增 ID、随机 ID 和事件创建时间都不能证明事务提交顺序。如果较小 ID 的事务还没有提交,消费者先看到较大的 ID 并移动游标,就可能永久漏掉较小的事件。
`orbit_event_streams` 为每个命名空间保存 last_sequence。生产事务持有独立流行锁,分配下一个序号并一起提交。后来事务在前一个提交或回滚前,不能获得更大的可见序号。回滚不消耗序号。
消费者读取已经提交的流头,并且只扫描不超过这个位置的日志。读取流头不加锁;订阅创建需要锁流行,以确定“从现在开始”的准确边界。该方案存在命名空间内的短暂串行分配热点,其实际吞吐和锁等待仍须在 P6 验证。
锁顺序:
- 任务路径沿用认证/应用额度/队列/任务/尝试/许可的既定顺序,保存结果时最后取得流行锁;不访问订阅行。
- 创建订阅按认证 → 幂等身份 → 队列共享锁 → 流行锁 → 新订阅/审计执行。
- 消费、确认和管理按认证 → 必要的幂等身份 → 订阅 → 交付执行;不再取得任务、队列、应用额度或流行锁。
## 3. 订阅和授权
每个订阅绑定创建凭据和一个队列,事件类型集合在创建时冻结。名字限 1–128 个 ASCII 字符,以字母或数字开头,其余允许字母、数字、点、下划线和连字符;命名空间内唯一。首版每命名空间最多 64 个订阅,没有公开删除或更换所属凭据的能力。
| 字段 | 语义 |
| --- | --- |
| from_sequence | 订阅此序号之后的结果;省略时使用创建事务确定的最新提交序号;显式 0 表示从现存结果日志开始 |
| types | 四类结果的非空子集;省略或空数组使用全部四类,重复或其他类型被拒绝 |
| max_pending | 已物化但尚未确认的交付上限,省略或 0 使用 100,自定义范围 1–1000 |
| max_attempts | 每条新交付初始可发送次数,省略或 0 使用 20,自定义范围 1–100 |
| cursor_sequence | 已扫描并可靠建立交付记录的位置,**不是消费者已确认位置** |
| pending_count | PENDING、LEASED、EXPIRED 的总数;每次有效确认只扣减一次 |
| revision | 配置并发版本;物化和积压数量变化不增加该版本 |
新动作独立授权:events:manage、events:read、events:consume。每个操作同时检查凭据有效性、所属应用/命名空间启用、队列范围和订阅所有者。同一命名空间的另一张凭据,也不能读取、领取、确认或管理该订阅。管理权限不能隐含消费权限,任务读取权限不能隐含订阅权限。
bootstrap 新增显式 --event-access,用于签发包含这三项动作的开发凭据;不会给既有凭据自动扩权。轮换后的安全重新绑定列入 P6,当前不能将旧凭据所属订阅自动转移。
创建、暂停/继续、人工重试都有持久化幂等身份及原回执。身份作用域包含所属凭据与目标资源,复合身份使用 SHA-256 编码到 64 字符,避免跨凭据回执泄漏或数据库截断。
## 4. 从结果日志到交付记录
每次领取在订阅锁下最多扫描 100 条本队列已提交结果,再按冻结类型过滤。选中的事件先创建唯一的 `(namespace_id, subscription_id, event_id)` 交付,随后推进 cursor_sequence,两者同事务提交。
达到 max_pending 后停止物化。未物化的结果保留在来源日志;任务生产者不锁订阅、也不等待消费者腾出交付窗口。有效确认释放空间后,后续扫描继续追赶。每条交付独立确认,处理顺序可以不同,不靠单个最大 ACK 序号跳过中间事件。
这是有界交付窗口,不是“所有存储都已限制大小”。首版结果日志和已确认交付不自动删除;持久化积压仍占数据库空间。清理、水位推进、容量告警及运维治理需要后续 P6 完成。
## 5. 租约、确认与恢复
| 状态 | 含义与转换 |
| --- | --- |
| PENDING | 等待就绪;领取时持久化租约后转 LEASED |
| LEASED | 已生成一次发送授权;有效确认转 ACKED,超时后按预算转 PENDING 或 EXPIRED |
| ACKED | 已可靠确认;重复确认返回原确认时间,不再次扣减 pending_count,也不重复写确认审计 |
| EXPIRED | 已耗尽交付预算,仍保留事件和积压占用;允许带原因的人工追加预算,或接受尚未被替换的最近证明的迟到确认 |
租约固定 30 秒,每次领取增加 lease_epoch 和 attempts,并产生新的 32 字节随机秘密。数据库只保存摘要;确认时使用常量时间摘要比较。当前内部领取按服务端连接 ID 计算容量,单连接容量 1–32。连接 ID 由后续传输层生成,不能接受客户端自报来绕过容量。
下一次启用状态下的领取,会先回收该订阅的过期交付。未耗尽时使用 `min(2^(attempts-1), 30)` 秒退避;耗尽时转 EXPIRED,不无限自动发送。当前没有独立订阅清扫进程:无人消费或订阅暂停期间,租约状态可能保持 LEASED,直至下一次领取执行恢复。
ACK 表示消费者已可靠保存或处理结果,不授予新业务执行权。当前最后一次秘密和代次即使过期,只要没有被新的交付替换,仍可用于迟到确认;新交付产生后,旧证明统一返回 DELIVERY_LEASE_LOST。确认和重新交付竞争通过订阅锁串行化,只会有一方有效。
服务端不能保证消费者业务恰好执行一次。后续 SDK 示例需展示接收方以 event_id 建立唯一 Inbox,在本地事务中保存结果/业务变更,提交后才 ACK。断线和 ACK 响应丢失可能造成重复投递,调用方不得按“收到过一次”就直接丢弃尚未可靠保存的内容。
每次交付授权、首次有效确认、订阅管理和人工重试均记录审计。交付审计包含事件标识、代次、次数、连接和租约截止,不记录交付秘密或结果正文。
## 6. 管理与人工重试
暂停只停止新的物化和领取,不撤回已经发出的结果。暂停期间仍接受有效 ACK。恢复后继续处理原有积压,事件和交付 ID 不变。
人工重试仅适用于 EXPIRED,必须提供 expected_revision、非空 reason 和 additional_attempts(1–100)。追加后的累计预算最多 1000000;已使用次数、当前代次和原事件身份均不归零。新领取才产生新代次。相同幂等键重报返回原响应,不重复加预算。
当前服务层方法见 [platform/events/service.go](../platform/events/service.go),持久化实现见 [订阅](../internal/storage/mysql/event_subscriptions.go)、[交付](../internal/storage/mysql/event_deliveries.go) 和 [结果日志](../internal/storage/mysql/result_journal.go)。这些是服务端内部模块,独立 SDK 不应直接依赖它们。
## 7. 迁移与历史边界
[000013](../internal/storage/mysql/migrations/000013_result_subscriptions.up.sql) 新增两个实体,并扩展订阅和交付字段。当前共 35 个实体、289 个直接声明的持久化字段,全部中文注释;新增或调整的 SQL 列提供中文 COMMENT。没有新增外键或级联删除。
升级必须停止旧版本写入节点,应用迁移,再启动支持新结果日志的版本。旧的运行中写入代码不会补写结果日志,不能在启用订阅后继续混跑。迁移沿用显式版本、校验和、dirty 标记和就绪检查,不自动修改历史迁移。
迁移前的 orbit_events 原样保留,不补造无法证明正确的旧结果快照。from_sequence=0 只覆盖迁移启用后的结果日志,不代表系统建立以来全部任务。历史占位订阅缺少可靠队列绑定,升级后保留但禁用,不对外消费;历史交付保留原次数和代次,预算至少等于已用次数。
retained_from_sequence 初始且目前固定为 1。未来实现清理后,如果订阅游标落在保留边界以前,必须返回 EVENT_HISTORY_EXPIRED,不得静默跳到最新结果。请求未来序号也会被拒绝。
## 8. 下一步
接入 HTTP 创建/查询/分页、暂停/继续、交付与耗尽记录查询、人工重试;实现 orbit.events.v1 WSS 的认证、hello、交付、ACK、心跳、连接上限、背压和退出;补独立 Go SDK、Inbox 去重示例、严格消息 Schema 和完整接口手册。随后做断路、ACK 响应丢失、节点进程强杀及 SDK 端到端回归,再验收 P3 的可靠订阅能力。

View File

@@ -20,7 +20,7 @@
| 任务冻结策略与 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` | 计划 P3,尚未实现 | [协议草案](../03-http-websocket-protocol.md) |
| `/v1/events/connect` 及订阅管理 | 持久化核心已实现;HTTP/WSS 和 SDK 尚未接入 | [订阅核心及边界](../09-result-subscription-core.md) |
| `/v1/workflows` 与流程管理 | 计划 P4–P5,尚未实现 | [流程接口规划](workflows.md) |
未实现路由返回 404。健康检查就绪不表示任务执行、定时调度或流程引擎已经可用。

View File

@@ -57,3 +57,5 @@ P3 增加 schedules:read 和 schedules:manage。bootstrap 显式传 --schedule-a
P2 增加 workers:connect 和 workers:consume。bootstrap 只有显式提供 --worker-access 时才同时授予这两项开发权限;现有凭据不自动变化。Worker 还需独立的进程恢复秘密和尝试秘密,见 [Worker 契约](worker.md)。
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)。

View File

@@ -0,0 +1,80 @@
# P3 可靠结果订阅持久化核心回归
验证日期:2026-09-17。本阶段完成结果日志、订阅和交付的服务/存储核心;HTTP/WSS、独立 SDK、Inbox 示例和订阅进程故障用例尚未交付。P3 与持续开发目标继续进行,不视为完整订阅能力验收。
## 1. 实现内容
[核心设计与边界](../09-result-subscription-core.md) 对应以下实现:
- [迁移 000013](../../internal/storage/mysql/migrations/000013_result_subscriptions.up.sql) 增加命名空间结果流和不可变结果快照,扩展订阅窗口、游标、交付预算及证明摘要。最终 Schema 无外键,模型和 SQL 字段注释同步。
- [结果日志](../../internal/storage/mysql/result_journal.go) 接入原状态事件事务;成功、已知失败、取消和需要核查四类结果同事务追加快照。每个命名空间独立串行分配序号,不按随机 ID 或最大自增 ID 推断提交顺序。
- [订阅服务](../../platform/events/service.go) 和 [订阅存储](../../internal/storage/mysql/event_subscriptions.go) 实现范围权限、凭据所有者、单队列冻结过滤、历史边界、幂等创建、暂停/继续、64 个订阅上限。
- [交付存储](../../internal/storage/mysql/event_deliveries.go) 实现有限批次物化、未确认窗口、30 秒租约、连接容量、到期退避、次数耗尽保留、最新证明隔离、重复/迟到确认和人工追加预算。每次授权及管理确认有审计,秘密只保存摘要。
- 数据模型目前共 35 个实体、289 个直接声明的持久化字段;逐字段中文注释检查无遗漏。SQL 模型映射由真实数据库回归校验。
## 2. 环境与命令
macOS 本机、Go 1.27.1、Docker MySQL 8.4.10、一次性数据库、回环 HTTP/WSS。使用随机测试凭据,测试清理自己的容器;不连接既有业务数据库。
```sh
make fmt test vet build
make integration
cd sdk/go
GOWORK=off go test -race ./...
```
上述检查均通过。模型、接口、SDK、示例与故障检查分别执行;没有数据库环境的普通 go test 会跳过集成,不能替代本次真实数据库验证。
| 分组 | 本次结果 |
| --- | --- |
| 全部 MySQL 集成 | PASS,85.486 秒 |
| 新增订阅核心 15 组场景 | PASS,3.49 秒 |
| 新增提交顺序/创建边界 3 组 | PASS,0.63 秒 |
| v12 订阅迁移升级 | PASS,3.79 秒 |
| SDK Worker 协议及运行器 | PASS,89.166 秒 |
| SDK 计划生命周期 | PASS,5.626 秒 |
| SDK 队列 / 应用资源 | PASS,4.501 / 1.386 秒 |
| 既有七项真实进程故障 | PASS,28.528 秒 |
| 既有十二个完整 SDK 示例 | 全部通过 |
| fmt / race / vet / build / 独立 SDK 模块 | 全部通过 |
耗时仅记录本地回归,不代表生产吞吐、SLO、容量或高可用结论。
## 3. 新增正确性和竞争证据
[订阅数据库用例](../../internal/storage/mysql/event_subscriptions_integration_test.go) 验证:
1. 来源重复回报不会重复追加结果;省略游标从创建边界之后开始,显式 0 可回放现存结果日志。类型过滤会安全推进扫描位置。
2. 结果 9007199254740993 保留原数值文本,`<&>` 不被无谓扩大;后续改动任务结果不会改变已保存快照。数据库拒绝 UPDATE 结果日志。
3. 不同命名空间、同命名空间另一凭据、移除队列范围、缺少独立动作及凭据撤销均被阻止。同一个幂等键不会跨所有者泄漏原回执。
4. 未确认窗口为 1 时,仍可提交后续 3 条结果;确认后按日志逐条追赶,无跳过或重复扣减。持有订阅行锁期间,独立任务取消仍在限定时间内提交结果。
5. 12 个消费者争抢同一条交付,只有一个获得有效租约。12 次并发确认中只有一次首次生效,所有响应保留相同确认时间,只有一条确认审计。
6. 丢弃存储服务对象后,重新创建服务仍从数据库恢复相同交付和事件身份。租约到期后退避,新的发送增加次数和代次并更换秘密;旧证明失效。
7. 次数耗尽后保留 EXPIRED 及积压占用。最新证明未被新代次替换时允许迟到确认,重复确认返回原回执。
8. 人工重试增加预算而不重置次数、代次或事件身份;相同管理身份重报不重复追加。错误状态或版本被拒绝。
9. 暂停阻止新发送,但允许在途 ACK;恢复后继续接收后续结果。单连接容量在数据库事务内执行,确认后释放容量。
10. 12 次迟到 ACK 与新领取竞争,最终只存在一个有效确认,旧证明不能确认新代次,也不会泄漏窗口额度。
11. 注入交付 INSERT 失败,交付记录、扫描位置和 pending_count 一起回滚;注入审计 INSERT 失败,ACK、确认时间和计数一起回滚。
12. 注入结果日志 INSERT 失败,任务状态、原状态事件、流序号及应用额度一起回滚;原请求身份可以重报成功。
13. 安全重试不产生最终结果通知;最终失败、UNKNOWN 及执行失联恢复的核查结果进入结果日志。
14. 未来游标、缺失历史和订阅数量超限分别返回明确错误。秘密原文不进入确认审计。
提交顺序用例显式保持第一个生产事务未提交,同时启动另一个事务。消费者看不到越过未提交事务的较大序号,且不等待生产者流锁。首事务提交时依次获得 1、2;首事务回滚时后一个获得 1,无非事务性空洞。创建“从现在开始”的订阅必须等前一个生产事务完成,并正确捕获最终提交边界。
回归修正了首次物化后的时钟边界及复合幂等作用域超过既有 64 字符的问题,最终完整套件重新通过。
## 4. 升级和无外键证据
[v12 专项升级](../../internal/storage/mysql/result_subscription_upgrade_integration_test.go) 重建真实旧 Schema,保留已有任务事件及数值精度;旧占位订阅禁用且 queue_id 为空,不自动取得消费权限。旧交付已使用 25 次、代次 25,升级后原值不变,预算至少覆盖已用次数。重复迁移安全,未凭空生成历史结果快照。
[v7 无外键升级](../../internal/storage/mysql/no_foreign_keys_integration_test.go) 继续验证原有 70 个外键移除后为 0,325 项原索引列定义保留、任务数据不变,手工引入外键时启动检查拒绝。新结果表和交付扫描索引作为新增内容单独排除于“原索引不变”比较;没有跳过原索引的校验。
v4 安全重试及 v9 应用配额回填和在途释放回归仍通过。当前迁移新增结果日志,需要停止旧写入节点后升级;不支持旧二进制持续写入但遗漏新日志的混跑部署。
## 5. 后续验收边界
- 尚未开放订阅 HTTP/WSS 路由,未声称新订阅能由独立 SDK 调用。本次十二个示例及七项强杀故障覆盖的是既有能力。
- 本次“恢复服务对象”只验证交付权威状态不在内存,不能替代订阅 WSS 断路、ACK 响应丢失或真实节点进程强杀;这些将在传输层和 SDK 接入后验证。
- 首版按领取触发该订阅的租约恢复,没有独立订阅后台清扫。暂停或没有消费者时,过期 LEASED 可能延迟转为 EXPIRED。
- 未确认交付窗口有限,来源日志和已确认历史目前无自动清理,积压仍消耗存储。生产容量告警、保留/清理、监控及凭据轮换属于 P6。
- 命名空间流锁热点、结果大小和数据库写放大,以及 MySQL 故障切换、跨故障域部署、RPO/RTO 仍需真实目标环境测试。