l1: default-route unresolvable S3 backend selectors instead of rejecting - #39
Draft
shreyas-blacksmith wants to merge 1 commit into
Draft
shreyas-blacksmith wants to merge 1 commit into
shreyas-blacksmith wants to merge 1 commit into
Conversation
The backend-selector trust interceptor enforced the forwarded
(x-blacksmith-s3-endpoint, x-blacksmith-s3-bucket) pair fail-closed:
anything not in the node's backends map got an InvalidArgument trust
rejection. That made every config race build-visible, and let one stale
host-level env value (the FA boot probe presented Doppler's
MINIO_ENDPOINT as its identity) knock whole hosts off the L1 ring into
direct-S3 fallback — dialing that same stale value. 2026-09-03: a
legacy us-west MinIO teardown left 134 hosts in s3_fallback with no
working cache path and 450k+ selector rejections in 24h.
Resolution now never rejects, mirroring the actions cache's shard
router: a missing/duplicate/unknown selector routes to the map's
designated default backend, and a bucket unusable for the resolved
entry uses that entry's default bucket. The L1 owns which backends
exist; upstream needs no endpoint knowledge of its own to be served.
Tenant isolation is unchanged — the storage-prefix interceptor stays
fail-closed; the selector only ever picked the shard.
Every defaulted resolution is metered
(bazel_remote_s3_backend_selector_defaulted_total{reason}) and
rate-limit logged: nonzero during a rollout = FA/L1 map drift;
sustained nonzero = a pin serving from a shard it wasn't allocated on.
RejectionReasonS3BackendSelector stays defined: L1s predating this
change still mint it and clients must keep degrading it to a miss.
Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The backend-selector trust interceptor enforced the forwarded
(x-blacksmith-s3-endpoint, x-blacksmith-s3-bucket)pair fail-closed: anything not in the node's backends map got anInvalidArgumenttrust rejection. Two problems, both proven in production on 2026-09-03 (us-west legacy MinIO teardown):MINIO_ENDPOINTas its routing identity; when that value left the map, probes failed and 134 hosts went boot-stickys3_fallback, dialing the same dead endpoint directly — no working cache path at all.The check never protected tenant isolation — that is the storage-prefix interceptor's job (unchanged, still fail-closed). The selector only ever picked which MinIO shard serves the request.
What
Resolution now never rejects, mirroring the actions cache's shard router (
clientFor): placement values are hints, anything unresolvable falls back to a default.The L1 owns which backends exist (its config is the map, and the fork's per-backend circuit breakers already own backend health); the upstream needs no endpoint knowledge of its own to be served. This is what lets the FA agent stop presenting
MINIO_ENDPOINTas probe identity (companion FA PR).Observability
bazel_remote_s3_backend_selector_defaulted_total{reason}(reasons:missing/duplicate/unknown,bucket_missing/bucket_duplicate/bucket_unknown) plus a rate-limited journald line. Nonzero during a map rollout = FA/L1 drift; sustained nonzero = a namespace pin serving from the default shard it wasn't allocated on (cold reads until the pin and map are reconciled) — worth an alert.bazel_remote_s3_backend_selector_rejected_totalis gone (nothing rejects anymore). Dashboards/alerts referencing it need to move to the new counter.Compatibility
RejectionReasonS3BackendSelectorstays defined: L1s predating this change still mint it, and upstream grpcproxy clients must keep degrading it to a metered miss. Client-side degradation tests unchanged and passing.default: trueentry) now also enforced at interceptor wiring viaRoutingBackends(), same ass3proxy.NewMulti.Testing
go build ./...and fullgo test ./...green, including rewrittenserver/grpc_s3_backend_test.go(every former rejection case now asserts which backend/bucket the request routes to) and the updatedconfigrouting-table test.Made with Cursor
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled. (Staging)Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.