Ctrl K
ComunidadGuías…

docs/NOTIFICATION_DURABILITY.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 通知持久化与补发

[English](NOTIFICATION_DURABILITY.en.md)

通知持久化从 Core v0.9.4 提供;历史 Core v0.9.3 制品仍只有正常关闭排空和 067 死信重投。能力属于 Core 及其自带控制台,不修改 ACC 契约或独立消费者。

## 保存和恢复的范围

内置 MySQL 状态库将任务终态和对应通知意图写入同一个事务;审批创建和审批提示写入审批所在配置库的同一个事务。两种库可以不同,不依赖跨库事务。JSONL 状态库将任务与通知写入同一条日志并同步落盘,随后才更新内存;这是单写者实现,不提供多个进程共享同一 JSONL 文件的协调。

覆盖任务 callback、配置的结果送达、审批提示,以及失败通知子任务的死信留存。只有新增的状态转换创建意图,启动时不会扫描历史已完成任务、旧待审批单并批量补发。业务父任务再次合法运行完成会产生新的 callback 事件;显式送达路由的 `deliver_<parent>` 和审批提示的 `approval_<id>` 子任务仍沿用已有身份。原生企微回流按终态事件区分,审批补续后的真实最终结果仍会通知;同一事件补发不会重新创建子任务或重跑父任务。普通监控告警仍是尽力发送。

终态与通知事件冻结业务快照、路由目的地和正文使用的品牌名,重试复用原事件 ID。签名密钥仍从原凭据入口读取,不复制到 outbox。快照可能含业务正文和目的地址,应按任务数据的敏感等级备份和保护;管理列表和指标不返回这些内容。

## 发送语义

- 后台在启动和已有维护周期恢复待处理通知,单轮、并发和重试都有界。正常关闭停止接受新的扫描,并等待已接受的处理;留存的待处理意图由后续实例恢复。
- 每次领取具有独立租约 token,确认、失败和续租都校验当前 token 与有效期。过期 worker 不能确认后继 worker 的任务。
- HTTP 发送等待真实响应。稳定事件 ID 同时放入 `X-Bailing-Event-Id` 请求头及签名正文的 `notification_event_id` 字段。网络/429/5xx 可退避重试,普通 4xx 停止;每个自动处理周期最多六次发送尝试。接收方应保存此 ID 去重。
- 接收端已处理而本地确认丢失时,Webhook 可能再次送达。租约和事件 ID 不等于外部系统“恰好执行一次”。
- 原生渠道可能已部分送达、回执未知或处理期间租约失效时,保留为 `uncertain`,不会自动完整重发。操作者核对接收端后才可再次处理。
- `completed` 表示本通知步骤已完成,也可能只是已交给持久通知执行器,不能当作收件人已阅读。失败执行器子任务继续使用 067 原子重投标记;关联原生通知事件的死信统一从 outbox 补发,避免两个入口各发一遍。

状态包括 `pending`、`processing`、`completed`、`skipped`、`failed`、`uncertain`。人工重试沿用事件 ID,重置当前周期计数,保留累计处理次数。失败记录不会静默丢弃;本次未添加自动清除或历史归档策略。

## 查看和处置

控制台「任务 → 通知送达」按通知类型、状态查看摘要。只读角色可查看,具有 `runs:write` 权限的角色可以重新处理失败通知;未知送达必须先确认重复风险。操作不会重跑业务父任务。

管理 API 为 `GET /admin/api/notification-outbox` 和 `POST /admin/api/notification-outbox/{event_id}/retry`。重试需要 `kind` 与观察到的 `expected_status`;`uncertain` 还需 `allow_uncertain: true`。状态已改变返回 409,避免旧页面重复操作。缺少持久化端口的第三方仓储保留旧尽力发送兼容路径,API 返回能力不可用,不宣称完整持久化。

[运维指标](OPERATIONS.md)新增固定来源 `jobs` / `approvals` 的 outbox 可用性、各状态数量、最老待处理年龄和过期租约数。不同来源按种类分区,配置库和状态库共库时不重复统计。指标不含事件 ID、租户、收件人或正文标签。

## 迁移、回退和验证

新增 [068_notification_outbox.sql](../sql/068_notification_outbox.sql),不修改 067 及以前的迁移。升级前备份代码、配置、任务/审批数据库和 JSONL;迁移同时覆盖实际使用的状态与配置数据库。迁移只建表和增加可空关联列,不把旧终态或旧审批转换成新通知。

回退前先停止新接单和通知 worker,保存 outbox、死信和已有外部回执。旧版本不会处理新 outbox;恢复备份也不能撤销已送出的消息。必须以一致的代码/配置/数据库集合恢复,并核对已发送事件,不能通过删除通知记录或直接切旧代码宣称无损回退。发行和合成验证不代表生产升级或回滚演练,具体步骤见 [v0.9.4 升级指南](UPGRADE_v0.9.4.md)。

公开验证入口:

- [存储回归](../src/infrastructure/state/notification-outbox-storage.test.ts):JSONL 记录恢复、MySQL 同事务/回执丢失、租约隔离、审批和迁移保留。
- [通知运行回归](../src/app/notification-outbox.test.ts):本地接收端、强杀后同事件补发、已决审批跳过、原生未知态、子任务身份和旧 worker 确认隔离。
- [生命周期回归](../src/app/runtime-evidence-maintenance.test.ts):维护启动、停止与未完成工作排空。
- [管理 API 回归](../src/routes/admin-notifications.test.ts)和[旧死信入口回归](../src/routes/admin-runtime.test.ts):权限、摘要脱敏、并发状态改变和单一补发入口。

这些测试使用合成数据和本地接收端,不证明真实渠道可用性或生产收件。JSONL 同步落盘与 MySQL 提交的保障仍依赖底层存储;不覆盖磁盘损坏、任意旧扩展实现或未经验证的外部副作用。