Files
next-tjwater-server/docs/workbench-event-refinement.md

35 lines
5.5 KiB
Markdown

# 工作台事件链路收敛(2026-10-08)
本次保留事务事件日志、任务/Run 分离、通知收件箱和低频对账,不增加表、字段、迁移、账号、消息中继或服务。数据库验证和新增事件排序保护只作用于物理数据库 `tjwater_v2`;未向其他 BizDB 应用变更。
## 修复与成本边界
- 自增序号不等于提交顺序。任务、告警、分析和外围工单写入在业务锁和写入之前取得同一个 PostgreSQL 事务 advisory lock;后续写者必须等前一个提交或回滚后再分配事件序号。锁范围是单个 BizDB,数据库自动在事务结束时释放,不需要新表。事务回滚仍可能留下序号空洞,这是正常情况,不能要求游标连续。
- 所有事件生产者必须遵守同一锁约定;旧进程和绕开约定的直接 SQL 写入不受保护。激活时必须一起更新 Server 和外围源服务;混合版本不能声称提交顺序已保证。分析计算、Agent 执行和外部 HTTP 调用不得放进持锁事务。确认派发的前置查询、派发前详情读取和回执核对读取均使用显式短事务,避免默认非 autocommit 连接的隐式事务使写阶段退化为 savepoint、锁一直跨 HTTP 存活。不改其他业务模块的连接池配置。
- 这个锁会串行化事件写事务,任务仓储的短命令事务(包括心跳)也使用它。这是当前低频业务规模下的明确取舍,不适用于高频遥测或长事务。扩大业务事件量前应实际测量写入等待时间,不把增加锁误称为写吞吐优化。
- 每个进程/项目仍只有一条专用 LISTEN 连接,不长期占用普通请求池。Hub 按唤醒代次共享同游标读取;最多保留 32 个游标批次,每批最多 200 条,慢客户端不会形成无界缓存。事件通知和每 30 秒一次的共享对账使缓存失效;每个浏览器的 15 秒 keepalive 不再触发独立 SQL 查询。无订阅者 60 秒后仍关闭 Hub。
- `reset_required` 允许权威快照把客户端游标从较大值重置为 0;普通快照仍禁止回退,过时并发响应仍不能覆盖新响应。重置失败会在重连时重试。只有 SSE 请求成功打开后才显示连接正常,HTTP 失败按原退避策略重连;离开或重置时释放流读取器。
- 工单源事件按 ID 定向同步。任务投影的 `operational.task.changed` 负责更新详情,其伴随的 `workbench.work_order.changed/operational_task` 不再重复拉取整份快照。告警和分析事件仍合并后刷新运行态;SCADA/流量采样通道不变。全量对账见 [外围工单主动同步](peripheral-work-order-events.md)。
- 项目查询按同一身份提供器共享,缓存 5 分钟。每个调用仍重新获取 token,各调用可独立取消;共享查询有 15 秒超时,失败后清除缓存。此缓存不授予权限,Server 每次请求和每次 SSE 重建仍鉴权。
- 事件接收游标与数据读取成功分开处理:增量详情、通知或事件触发的运行态快照失败时,只对失败读取按 1、2、4 秒递增至 30 秒重试,成功后才确认失效事件。被并发游标守卫拒绝的快照不能当作刷新成功;仅在更新的成功快照已覆盖失效游标时停止重试。正常读取成功后不安排定时请求;依赖变化、重置或离开页面会取消旧请求及重试定时器。这不是新增正常轮询通道,也不扩大初始化列表的裁剪/重试语义。
20 个相同游标订阅者的单元用例中,数据库读取从 20 次降为 1 次;这是查询次数验证,不是生产 CPU、连接预算或服务器费用压测结论。
## 保留但不扩展的兼容项
任务表的旧执行字段、旧 `status` 和 JSON/标量重复投影先保留;新执行流程以 Run 为准。空的 schedule 表/API 仍不接入前端 mock 定时任务。未经前向迁移、备份及调用方验收,不删除这些兼容结构。现有表的数量并不是本次优化目标;应减少重复计算、扫描和通信,而不是合并独立生命周期的业务对象。
## 验证与启用
```bash
conda run -n server python -m pytest tests/unit tests/auth -q
WORKBENCH_EVENT_ORDER_TEST=1 PERIPHERAL_SAMPLE_LIVE_TEST=1 conda run -n server python -m pytest tests/integration/test_workbench_event_order.py tests/integration/test_peripheral_work_order_events.py -q
conda run -n server python scripts/check_openapi.py
```
并发测试强制连接 `tjwater_v2`,观察后写者等待事务锁;所有随机项目事件均回滚。测试会消耗序号,但不会保留测试业务记录或事件,也不变更 schema。外围原子性用例验证源记录、历史及事件同事务回滚。前端浏览器用例验证从 41 重置到 0 后重连,以及源事件/投影事件只触发一次详情读取。
默认非 autocommit 连接的三条动作回归(确认派发、直接派发、核对回执)运行真实读取/事务锁和实际服务编排,以内存动作夹具替代业务写入。外部适配器替身检查调用时连接为 IDLE,并用第二条真实连接取得同一事件锁,确认没有跨调用持锁;这不是实际外围 HTTP 或设备执行联调。
代码提交不代表部署。本轮不重启现有进程,不运行全库迁移,不推送。启用新同步器需要先上线含新增查询参数的 Server,再按维护者授权的发布流程更新外围服务和同步器,最后更新前端。回退旧代码可恢复低频全量对账,但也会重新暴露旧游标排序问题,不能作为已解决并发问题的版本。