chore(outbox-cleanup): add 10s grace to defend against id-vs-commit race

PG sequences advance outside transactions, so a slow committer can hold
an id that gets surpassed by the watermark before its commit becomes
visible. Without the grace period, cleanup would delete such rows before
the snapshot poller ever sees them. 10s is a comfortable upper bound
on realistic enqueueSchedulerOutbox commit latency.
This commit is contained in:
shaw
2026-06-16 11:57:32 +08:00
parent cb14935e9a
commit 31dc8913ac
2 changed files with 6 additions and 0 deletions
@@ -108,11 +108,16 @@ func (r *schedulerOutboxRepository) DeleteConsumedUpTo(ctx context.Context, wate
if limit <= 0 {
limit = schedulerOutboxDefaultCleanSize
}
// created_at < NOW() - INTERVAL '10 seconds' 防御 PG 序列号在事务内提前分配但
// 提交延迟的竞争:若某 Tx 在 watermark 推进前持有 id=N(未提交),watermark
// 跨过 N 后该 Tx 才提交,此时 row N 已经"低于 watermark"但从未被 poll;10s
// 宽限期让此类慢事务有机会提交后被消费,再被 cleanup 删除。
result, err := r.db.ExecContext(ctx, `
WITH doomed AS (
SELECT id
FROM scheduler_outbox
WHERE id <= $1
AND created_at < NOW() - INTERVAL '10 seconds'
ORDER BY id ASC
LIMIT $2
)
@@ -20,6 +20,7 @@ func TestSchedulerOutboxRepositoryDeleteConsumedUpToUsesBoundedCTE(t *testing.T)
SELECT id
FROM scheduler_outbox
WHERE id <= $1
AND created_at < NOW() - INTERVAL '10 seconds'
ORDER BY id ASC
LIMIT $2
)