npm remote tarball: a coalescing 503 answers the sweep interval, not S13's Retry-After: 1
The gap
A coalescing failure on the npm remote tarball route answers Retry-After
derived from health_check.scheduled_interval. The two metadata routes answer
1 on the same condition.
docs/specs/S13-virtual-remote-foundation.md, #### Retry-After on a coalescing failure, fixes the value at 1 for ErrLeaderCanceled and
ErrSingleFlightTimeout, states it as the same value for Maven, npm and
Container, and forbids substituting the sweep interval for it. The tarball
route does not follow that rule.
Why the routes differ
The metadata routes reach writeRemoteProxyError
(internal/format/npm/remote_read_errors.go), whose two coalescing arms pass
remoteCoalescingRetryAfterSeconds.
The tarball route never gets there for those sentinels.
remoteTarballBackpressure (internal/format/npm/remote_tarball.go)
intercepts ErrSingleFlightTimeout, ErrLeaderCanceled and
ErrFillAbandoned ahead of the shared mapping and answers through
RemoteTarballHandler.writeUnavailable, which passes
remoteReadPolicy.retryAfterSeconds — the sweep interval.
The split is deliberate in the sense that it is pinned, not in the sense that
anything argues for it: cmd/artifact-registry/wire_npm_remote_boot_integration_test.go
asserts 1 for the dist-tags follower and the interval for the tarball
follower on the identical condition, with a comment recording that the tarball
route writes its own 503.
What it costs
A client coalesced onto a tarball fill that loses its leader backs off by the
sweep interval (300 at the 5m default) when the condition it hit clears as
soon as the next request elects a fresh leader. The same client on a packument
backs off by 1. Nothing about the condition is route-specific.
Decide which way to reconcile
Either is defensible and the choice is not obvious, which is why this is a separate item rather than a drive-by fix:
- Route the three sentinels through the shared mapping so the tarball answers
1, matching S13 and the metadata routes. Note that1invites an immediate retry against a repository whose fill capacity is by definition saturated, which is the conditionremoteTarballBackpressureexists to name. - Keep the interval and amend S13 to carve the tarball out, saying why a fill slot's contention is an interval-scale condition rather than a one-second one.
Documentation state
docs/specs/S15-npm-remote.md's Error Cases table scopes its coalescing row
to the metadata routes and points here for the tarball, so the spec and the
code agree today. Whichever way this is resolved, that row and the paragraph
under it change with it.
Related to #785 (closed) Related to #778