现象
service-messaging 里的 HttpDispatcher / SqlHttpOutbox 与 #17610 修复前的 NotificationDispatcher 完全同形状:
- 固定 500ms 间隔,无空闲退避
- 每跳遍历 8 个分区;每个分区的领取先跑一次 reap
UPDATE(谓词不含分区),再跑一次候选 SELECT
- ⇒ outbox 为空时每跳 16 条语句(8 reap + 8 select)
来源:#17610 开发席位用一次性探针实测(ObjectQL + SqlDriver,8 分区,表 sys_http_delivery),见 PR #17622 报告的范围外发现。
为什么要紧
修法(复用 #17610 的模式,不另起设计)
PR #17622 已为 NotificationDispatcher 落地:
- outbox 契约加可选
reap(),领取加可选 skipReap——reap 每跳一次、分区锁之外
- 空闲指数退避到上限(通知侧默认 30s,插件可调),
setTimeout 链,stop() 清除
- 入队时唤醒(通知侧经
setOutbox(outbox, { onEnqueued });HTTP 侧对应 enqueueHttp)
注意:PR #17622 的退避实现在 NotificationDispatcher 内部,未抽成共用 helper。实现本卡时判断是否值得抽出,二者择一并说明。
验收
- 空 outbox 上每跳语句数有上限的计数测试(每跳 ≤ 1 + 分区数)
- 空闲退避 + 入队即唤醒,各有测试
- 未实现
reap() 的旧 outbox 行为不变;分区锁、至少一次投递、claim TTL 回收语义不变
顺序
等 PR #17622 合并后再开工:两者会同时改 messaging-service-plugin.ts 与 outbox 契约,并行会产生同文件冲突。
现象
service-messaging里的HttpDispatcher/SqlHttpOutbox与 #17610 修复前的NotificationDispatcher完全同形状:UPDATE(谓词不含分区),再跑一次候选SELECT来源:#17610 开发席位用一次性探针实测(
ObjectQL+SqlDriver,8 分区,表sys_http_delivery),见 PR #17622 报告的范围外发现。为什么要紧
indexes— every declared secondary index is absent on production Turso tenant databases, so hot polling queries full-scan #17609(远程库缺声明索引)合并并被消费前,每条都是全表扫描;合并后仍是每跳 16 次查找修法(复用 #17610 的模式,不另起设计)
PR #17622 已为
NotificationDispatcher落地:reap(),领取加可选skipReap——reap 每跳一次、分区锁之外setTimeout链,stop()清除setOutbox(outbox, { onEnqueued });HTTP 侧对应enqueueHttp)注意:PR #17622 的退避实现在
NotificationDispatcher内部,未抽成共用 helper。实现本卡时判断是否值得抽出,二者择一并说明。验收
reap()的旧 outbox 行为不变;分区锁、至少一次投递、claim TTL 回收语义不变顺序
等 PR #17622 合并后再开工:两者会同时改
messaging-service-plugin.ts与outbox契约,并行会产生同文件冲突。