Skip to content

feat(http): add an opt-in retry policy - #54

Merged
AayushMainali-Github merged 1 commit into
mainfrom
feat/35-retry-policy
Jul 25, 2026
Merged

feat(http): add an opt-in retry policy#54
AayushMainali-Github merged 1 commit into
mainfrom
feat/35-retry-policy

Conversation

@AayushMainali-Github

Copy link
Copy Markdown
Owner

Closes #35. Adds ADR-0009, which amends ADR-0006 rather than quietly contradicting it.

The decision the issue asked to settle first

timeoutMs stays the budget for a single attempt. Each attempt gets the full amount, and retry.maxElapsedMs is there for callers who want a ceiling on the whole sequence.

Reinterpreting it as an overall deadline was rejected for two reasons. It would silently redefine an existing option, so identical code would behave differently after an upgrade and nobody would find out until the day a request was slow. And it would shrink each attempt's budget as attempts accumulate, so the final attempt, made under the worst conditions, would get the least time to succeed. A per-attempt budget also matches what a caller means when they write timeoutMs: a single request must not hang for more than this.

What is retried

ping, query, and queryTable. Writes only with retryWrites, which is the explicit acceptance of duplication risk that ADR-0006 required. execute is never retried, whatever the configuration.

ADR-0006 rejected "retry reads, never writes" because POST /api/v1/sql carries both SELECT and INSERT and the client cannot tell them apart without parsing SQL. That is still true and this does not solve it by parsing. It uses the method the caller chose as the declaration: query and queryTable return rows and are treated as reads, execute exists for statements whose point is their effect. A caller who sends an INSERT through query has miscategorised it, and the TSDoc on both methods and the README say so plainly. That is a documented judgement about intent rather than a guarantee, and the ADR records it as a cost rather than pretending otherwise.

Which failures

Timeouts, network failures, HTTP 429, and 5xx other than 501. Rejected credentials, malformed SQL, an oversized payload, and a caller abort are final, because the server will decide them the same way next time. 501 is excluded because a server that will never implement an endpoint will not implement it in 200 ms.

Backoff and cancellation

Exponential from initialMs, capped at maxMs, full jitter on by default so a fleet that failed together does not return in lockstep. A Retry-After header wins over the computed delay, since the server knows when it will be ready, but is still capped by maxMs so a mistaken or hostile value cannot park the caller. Retry-After is carried out of band in a WeakMap rather than bolted onto the error classes, so those stay a description of what went wrong instead of a channel for transport bookkeeping.

An AbortSignal ends the sequence immediately, including mid-backoff, and the abort reason propagates rather than being swallowed by a sleeping client. Backoff timers are unrefed, so a pending retry never keeps a process alive.

Tests

56 unit tests across the policy itself, the transport loop, and the client wiring: default-off, per-method retryability, the write opt-in, attempt exhaustion, the elapsed ceiling, abort during backoff, Retry-After handling including nonsense values, and the fact that the last failure propagates rather than the first.

Three integration tests run against a live server through a fetch that fails the first N calls the way a dropped connection does and then delegates to the real one, so a recovered request is proved to return real rows rather than a mock's idea of them.

@github-actions github-actions Bot added area: client Area: client area: testing Area: testing area: documentation Area: documentation area: release Area: release labels Jul 25, 2026
@AayushMainali-Github
AayushMainali-Github merged commit 94cd075 into main Jul 25, 2026
14 checks passed
@AayushMainali-Github
AayushMainali-Github deleted the feat/35-retry-policy branch July 25, 2026 04:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: client Area: client area: documentation Area: documentation area: release Area: release area: testing Area: testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RFC]: Opt-in retry policy

1 participant