现象
outbox 完全为空(零 pending)时,NotificationDispatcher 每一跳仍然:
runTick 遍历 partitionCount 个分区(默认 8,dispatcher.ts 构造函数 partitionCount ?? 8)
- 每个分区
runPartition 依次调用 outbox.claim() 与 outbox.claimDigest()
sql-outbox.ts 里这两个方法各自先跑一次 reap(UPDATE … WHERE status='in_flight' AND claimed_at < now - claimTtlMs,代码注释自称 environment-wide),再跑一次候选 find,没有候选即返回
⇒ 空闲时每跳 8 × (2 + 2) = 32 条语句。其中 reap 逻辑上每跳至多需要 1 次,现在跑 16 次。
intervalMs 默认 500。inflightTick 守卫会跳过重叠的跳,所以实际速率被单跳耗时封顶——远程 Turso 下每条语句一次 HTTP 往返,生产实测稳态约每秒 28 条,与 32 条 ÷ 约 1.1 秒/跳吻合。
后果(生产实测;租户信息已匿名)
在热表缺索引的租户库上(驱动侧另卡),这 32 条每条都是全表扫描。一个生产租户库在 sys_notification_delivery 约 5,700 行时,稳态约 15 万行读/秒,9 天 1,011.6 亿次读,占其 Turso 组织 99.9%。
即使驱动侧索引修好,空闲成本仍是每跳 32 次索引查找、永不停歇、每个温热 kernel 一份。
验收
- reap 与领取解耦:reap 每跳至多一次(或更慢的独立节奏,例如
claimTtlMs 量级),不再随 分区数 × 2 放大
- 空闲退避:连续若干跳零领取时逐步拉长间隔(上限可配);有投递入队时立即唤醒。若退避会让新通知的首次投递延迟变长,写明上界
- 计数测试防回归:对空 outbox 跑 N 跳,断言 engine 语句数的上限
- 语义不变:分区锁、至少一次投递、digest 折叠、claim TTL 回收——现有测试全绿
同形状,请评估(不强求同卡)
service-queue 的 DbQueueAdapter.claimBatch(默认 1s 轮询)按 priority, scheduled_for 排序,而声明索引是 ['queue','status','scheduled_for']——排序首键 priority 不在索引里;空闲时同样恒定轮询。你若判断应该另立卡,说出来。
驱动缺索引、未配置渠道写死信两张关联卡的编号见本卡评论。
现象
outbox 完全为空(零 pending)时,
NotificationDispatcher每一跳仍然:runTick遍历partitionCount个分区(默认 8,dispatcher.ts构造函数partitionCount ?? 8)runPartition依次调用outbox.claim()与outbox.claimDigest()sql-outbox.ts里这两个方法各自先跑一次 reap(UPDATE … WHERE status='in_flight' AND claimed_at < now - claimTtlMs,代码注释自称 environment-wide),再跑一次候选find,没有候选即返回⇒ 空闲时每跳 8 × (2 + 2) = 32 条语句。其中 reap 逻辑上每跳至多需要 1 次,现在跑 16 次。
intervalMs默认 500。inflightTick守卫会跳过重叠的跳,所以实际速率被单跳耗时封顶——远程 Turso 下每条语句一次 HTTP 往返,生产实测稳态约每秒 28 条,与 32 条 ÷ 约 1.1 秒/跳吻合。后果(生产实测;租户信息已匿名)
在热表缺索引的租户库上(驱动侧另卡),这 32 条每条都是全表扫描。一个生产租户库在
sys_notification_delivery约 5,700 行时,稳态约 15 万行读/秒,9 天 1,011.6 亿次读,占其 Turso 组织 99.9%。即使驱动侧索引修好,空闲成本仍是每跳 32 次索引查找、永不停歇、每个温热 kernel 一份。
验收
claimTtlMs量级),不再随 分区数 × 2 放大同形状,请评估(不强求同卡)
service-queue的DbQueueAdapter.claimBatch(默认 1s 轮询)按priority, scheduled_for排序,而声明索引是['queue','status','scheduled_for']——排序首键priority不在索引里;空闲时同样恒定轮询。你若判断应该另立卡,说出来。驱动缺索引、未配置渠道写死信两张关联卡的编号见本卡评论。