fix(queue): enforce max 5 retries and make queues healthy#2765
Conversation
Cap every queue and webhook delivery path at 5 retries, raise org_stats throughput, ignore delayed (vt>now) webhook messages in /queue_health, and purge already-stuck read_ct>5 rows on migrate so monitoring can go green. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 31 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThe PR standardizes queue and webhook retries at a hard maximum of five, updates queue-health calculations and messaging, purges over-budget PGMQ messages during migration, and documents the resulting retry and cleanup behavior. ChangesRetry budget enforcement
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant QueueConsumer
participant PGMQ
participant QueueHealth
participant CleanupMigration
QueueConsumer->>PGMQ: read messages with max read count 5
PGMQ-->>QueueConsumer: message and read count
QueueHealth->>PGMQ: evaluate visible and stuck messages
CleanupMigration->>PGMQ: delete messages with read_ct > 5
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
Merging this PR will not alter performance
Comparing Footnotes
|
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_195508c1-2806-46dd-9c0d-f563a9aa71a0) |
There was a problem hiding this comment.
Stale comment
Risk: high. Cursor Bugbot did not complete (skipped due to usage limit), so the configured automated review signal is unavailable. This PR changes production queue retry budgets, webhook delivery limits, and includes a migration that deletes stuck queue messages — human review is required.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot completed with status
skipping(usage limit reached) and did not produce a clean review signal. Human review is needed for this queue retry-budget and migration change. Assigned WcaleNieWolny and victorleduc as reviewers.Sent by Cursor Approval Agent: Pull Request Approver
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@supabase/functions/_backend/public/queue_health.ts`:
- Around line 376-389: Restore never_read_count to count all rows with read_ct =
0, preserving its existing public API meaning. Keep the vt <= now() visibility
filter on never_read_stale_count, and if the visible-only metric is required
elsewhere, expose it through a distinct field such as never_read_visible_count
and update only those consumers to use it.
In `@supabase/functions/_backend/utils/webhook.ts`:
- Around line 99-110: The retry schedule and cap in getWebhookRetryDelaySeconds
are inconsistent: the fifth delay exceeds WEBHOOK_MAX_RETRY_AFTER_SECONDS.
Reduce the final value in WEBHOOK_RETRY_DELAYS_SECONDS so every retry remains
within the existing 2-hour cap, preserving the current cap logic and attempt
ordering.
In `@supabase/migrations/20260726134739_queue_health_retry_budget_5.sql`:
- Around line 26-76: Add production-scale support for the stuck-row purge loop
in the migration’s anonymous DO block: ensure the DELETE subquery over each pgmq
queue table can efficiently locate rows matching read_ct > 5, preferably by
creating or reusing an appropriate partial index before batching, or validate
the approach with production-like EXPLAIN (ANALYZE, BUFFERS) results and adjust
it if it exceeds the migration time budget. Preserve the existing per-queue
batch and total-work bounds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 024dacb6-33d5-4200-a86c-5d00c84724ab
📒 Files selected for processing (10)
AGENTS.mdsupabase/functions/_backend/public/queue_health.tssupabase/functions/_backend/triggers/on_version_update.tssupabase/functions/_backend/triggers/queue_consumer.tssupabase/functions/_backend/triggers/webhook_delivery.tssupabase/functions/_backend/utils/webhook.tssupabase/migrations/20260726134739_queue_health_retry_budget_5.sqltests/queue-consumer-message-shape.unit.test.tstests/queue-health.unit.test.tstests/webhook-delivery-security.unit.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
There was a problem hiding this comment.
All reported issues were addressed across 10 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Restore never_read_count semantics, trim webhook delays within the 2h cap, bound the stuck-message purge with a global batch/runtime budget, and tighten queue_health docs assertions. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_17460284-86fe-41f1-8eb6-1452585cca7a) |
There was a problem hiding this comment.
Risk: high. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review signal. Human review is required for this queue retry-budget and migration change; Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
Risk: medium. Not approving: Cursor Bugbot completed with status skipping (usage limit reached) and did not produce a clean review signal. Human review is still needed for this queue retry-budget and migration change; Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver
|





Summary (AI generated)
MAX_QUEUE_READS = 5(removedVERSION_QUEUE_MAX_READS = 30)WEBHOOK_MAX_ATTEMPTS = 5(DB default + handler clamp) and shorten the retry delay ladderorg_stats_queueto batch 100 on every 10s tick socron_stat_orgcan drain/queue_healthignores delayed messages (vt > now()) for never-read stale checksread_ct > 5AGENTS.md(Production Scale Guardrails + dedicated Queue Retry Budget section)Motivation (AI generated)
Production
/queue_healthwas unhealthy for three distinct reasons:cron_stat_org—org_stats_queuewas batch 10 / every 5 minutes (~100 msgs/5m) and could not keep up with ingresson_version_update— special-case 30 retries kept poison/partial cleanup messages looping and masked them from health (stuck > 5while consumer still retried)webhook_delivery— delayed retries (pgmq.sendwith futurevt) looked like 12h never-read backlog; app-level retries also went up to 10 / 24hRetries above 5 are not an acceptable progress mechanism. Leftover work must be re-enqueued by sweepers/crons.
Business Impact (AI generated)
Restores a trustworthy queue monitoring signal, stops unbounded retry storms on version cleanup, drains org plan-stat backlog faster, and fails bad webhook endpoints sooner instead of parking delayed messages for up to a day.
Test Plan (AI generated)
bunx vitest run tests/queue-consumer-message-shape.unit.test.ts tests/queue-health.unit.test.ts tests/webhook-delivery-security.unit.test.ts tests/webhook-delivery-handler-security.unit.test.tsqueue_health_retry_budget_5in prod (also purgesread_ct > 5)GET /queue_healthreturnsstatus: okafter org_stats backlog drains (a few minutes post-deploy)max_read_ct > 5and Discord alerts fire at the shared budgetGenerated with AI
Summary by CodeRabbit
Enhancements
Maintenance