Skip to content

feat(dispatch): count capacity refusals and expose the count - #408

Closed
EnRaiha wants to merge 3 commits into
NodeDB-Lab:mainfrom
EnRaiha:pr/374-371-dispatch-capacity-counter
Closed

EnRaiha wants to merge 3 commits into
NodeDB-Lab:mainfrom
EnRaiha:pr/374-371-dispatch-capacity-counter

Conversation

@EnRaiha

@EnRaiha EnRaiha commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

What

A dispatch refused by a capacity limit already reaches the client as the retryable 57P03 class. Nothing counted the refusals, so an operator could not see dispatch pressure without reading logs.

Every refusal path now goes through one helper that counts the refusal before it hands the request back, so the counter and the client-visible class cannot drift apart. The count is process-wide because the condition is the same one whichever core, database, or tenant hit its limit.

Rendered as nodedb_dispatch_capacity_busy_total on /metrics and as the dispatch_capacity_busy_total row of SHOW STATS.

Notes

  • The count lives at the refusal helper, not at each call site, because a second call site added later would otherwise count on one path and not the other.
  • The typed DispatchCapacity variant and its 57P03 class already exist. This adds the counter only; it does not change the class or the error shape.

Evidence

The tests fail on main without this change. Proof: the test was copied onto a clean origin/main (bd8da7dc2) worktree and run there first.

  • cargo nextest run -p nodedb --test wire -E 'test(~pgwire_show_dispatch)' on a clean main: FAIL, 2 tests (show_stats_carries_the_dispatch_capacity_counter reports the row missing from the SHOW STATS output; metrics_exposes_the_dispatch_capacity_counter reports the sample missing from /metrics).
  • On this branch: pgwire_show_dispatch 20/20 pass.
  • Unit coverage at the refusal helper: capacity_refusals_are_counted_once_each drives a full WFQ and a per-tenant in-flight cap and asserts at least two counts. The counter is process-wide, so the assertion is a lower bound.
  • cargo check -p nodedb, cargo fmt --all -- --check, and a lib clippy run are clean.
  • Mutation arm: with the two counting calls removed from the refusal helper, capacity_refusals_are_counted_once_each fails; restored, it passes.

What CI does not cover locally

  • 32-bit and ASan fuzz targets, and the full nightly matrix.

Closes #371

Copilot AI balanced review requested due to automatic review settings October 1, 2026 22:04

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

A dispatch refused by a capacity limit already reaches the client as the
retryable `57P03` class, but nothing counted the refusals, so an operator
could not see dispatch pressure without reading logs.

Every refusal path now goes through one helper that counts the refusal
before it hands the request back, so the counter and the client-visible
class cannot drift apart. The count is process-wide because the condition
is the same one whichever core, database, or tenant hit its limit.

Rendered as `nodedb_dispatch_capacity_busy_total` on `/metrics` and as the
`dispatch_capacity_busy_total` row of `SHOW STATS`; the architecture doc
names the class, the SQLSTATE, and the counter.
A refusal is only useful if it is observable. Assert the counter that
`nodedb_dispatch_capacity_busy_total` reports reaches an operator from SQL
(`SHOW STATS`) and from a Prometheus scrape, so neither surface can drift
from the refusals the dispatcher actually counted.
`dispatch_to_core` refuses on a full weighted-fair queue with a flat
`Error::DispatchCapacity`, so it could not route through the shared refusal
helper that records the count: that helper returns a boxed refusal carrying
the request back. The refusal was reported to the client and left out of
`nodedb_dispatch_capacity_busy_total`, so an operator reading the counter
saw fewer refusals than clients received.

Record the count on this path directly. The refusal keeps its type and the
request is still dropped rather than handed back, because this signature
has nowhere to hand it.
@EnRaiha
EnRaiha force-pushed the pr/374-371-dispatch-capacity-counter branch from c7b3442 to 2d0c6c2 Compare October 2, 2026 07:24
@farhan-syah farhan-syah closed this Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cluster: a saturated dispatch queue has no documented retryable class — clients cannot back off correctly; add one and a counter

3 participants