Accumulate investigation paths and preserve failed attempts across rounds. Keep graph annotations separate from execution state, synchronize the API contract, and consolidate documentation.
35 lines
5.5 KiB
Markdown
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-sample.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,再按维护者授权的发布流程更新外围服务和同步器,最后更新前端。回退旧代码可恢复低频全量对账,但也会重新暴露旧游标排序问题,不能作为已解决并发问题的版本。
|