Skip to content

fix(admin): bound queue-storage available counts and keep tombstone probes correlated - #720

Merged
hardbyte merged 3 commits into
release/0.6.6from
brian/admin-available-count-plan-0.6
Nov 11, 2026
Merged

hardbyte merged 3 commits into
release/0.6.6from
brian/admin-available-count-plan-0.6

Conversation

@hardbyte

@hardbyte hardbyte commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

The admin queue views time out (30 s statement timeout) on a database with a large available backlog and many ready tombstones. Two costs combine:

  • The correlated tombstone NOT EXISTS in the admin lane walk can be flattened into a merge anti join that seq-scans and sorts the whole ready_tombstones table, then re-walks it for every lane. With ~430k tombstones that was 300–400 ms per lane.
  • Even with per-row probes, an exact available count costs ~13 µs per unclaimed row, so a backlog of millions exceeds the timeout regardless of plan.

Change

  • Every tombstone probe in the admin SQL (ready_tombstones and receipt_completion_tombstones) carries OFFSET 0, so Postgres keeps it a correlated index probe and cost follows the rows checked. This mirrors the claim-path fix in fix(storage): keep claim tombstone checks correlated #713.
  • The per-queue available count walks at most AVAILABLE_LANE_EXACT_COUNT_CAP (5,000) unclaimed rows per lane. Within the cap the count is exact. A deeper lane contributes its head-sequence depth (next_seq - claim_seq, what the worker queue-depth gauge publishes) and the queue is flagged available_is_estimate on QueueOverview, Python QueueStat and awa queue stats (~N). The UI shows ≈N with a tooltip.
  • state_counts returns StateCounts { counts, available_is_estimate }, and /api/stats returns { "counts": {...}, "available_is_estimate": bool } instead of a bare state map, so the dashboard's available tile marks an estimate from the same pass that produced it. This is a breaking change for direct consumers of state_counts or /api/stats.
  • The state timeseries reads each lane's available rows from its newest end with the same cap, since rows created in the window sit at the tail.

Kind counts and list_jobs get the probe fix only. No schema change.

Validation

Measured against a production-sized database (one ready slot with ~55M rows, ~430k tombstones, one queue with >1M available across 32 lanes whose segments hold ~1 row each):

Query Before Probe fix only This PR
Per-queue available count >90 s (cancelled) 9.7 s 0.7–1.0 s
Timeseries available leg — — 1.0 s

The capped walk plans as Incremental Sort → Limit and stops at the cap.

Tests, on PostgreSQL 17 and 18:

  • available_count_probes_tombstones_per_row asserts no sequential scan reads tombstone rows; it fails with OFFSET 0 removed.
  • admin_available_count_test: exact at the cap with tombstones, estimate past it, mixed lanes and neighbouring queues, state_counts consistency, and recent rows counted behind an older backlog larger than the cap.
  • admin_api_test checks the new /api/stats shape and flag.
  • Existing admin, overview, timeseries, read-equivalence, copy and integration suites pass. cargo fmt, clippy (-D warnings) and tsc --noEmit are clean.
  • admin_read_cancel_test has two timeouts (cancelled_job_list_begin_…, cancelled_kind_overviews_begin_…) that fail identically on the base branch.

The history-backed legs (running from open receipt claims, completed and failed totals) still scale with retained ring contents; moving them onto the counter tables is separate work.

…robes correlated

The admin queue views counted every unclaimed ready row and checked each
against ready_tombstones through a correlated NOT EXISTS that Postgres could
flatten into a merge anti join over the whole tombstone table. On a large
backlog with many tombstones both costs exceeded the statement timeout.

Fence every ready and receipt-completion tombstone probe in the admin SQL with
OFFSET 0 so cost follows the rows checked. Walk at most
AVAILABLE_LANE_EXACT_COUNT_CAP (5,000) unclaimed rows per lane for the
per-queue available count: a lane within the cap is exact, a deeper lane
contributes next_seq - claim_seq, as the worker queue-depth gauge does, and
the queue is flagged with available_is_estimate on QueueOverview, the Python
QueueStat, the CLI and the admin UI. The state timeseries reads each lane from
its newest rows with the same cap.
@coderabbitai

coderabbitai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1f548bf1-99e7-4f30-a3d9-8d53e8dbda8d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
Pre-merge checks: Passed 1 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Title check Pending This assessment has not completed. The check will be retried on the next pre-merge check run.
Passed Passed checks: 1 passed

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-11T06:02:26.950199Z 12ab961 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 12ab961156

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread awa-model/src/admin.rs Resolved
Comment thread awa-model/src/admin.rs Resolved
state_counts now returns StateCounts { counts, available_is_estimate } and
/api/stats serves the same shape, so the dashboard's available tile marks an
estimated count from the same read instead of inferring it from the per-queue
overviews. The canonical-engine path always reports an exact count.
@hardbyte hardbyte added the full-ci Run the full CI matrix (Python build+test, E2E) on this PR label Nov 11, 2026 — with Claude
While a queue's available count is an estimate its lag is a lower bound, and the state timeseries undercounts a lane that gained more available jobs than the cap inside the window. Neither was stated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

full-ci Run the full CI matrix (Python build+test, E2E) on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant