s3: short-circuit filer failover on ErrNotFound (#9748)

withFilerClientFailover treated a filer's ErrNotFound like a transport
failure: it kept the result, re-queried every other filer, and finally
wrapped the answer as "all filers failed, last error: ... no entry is
found in filer store".

For workloads with many legitimate misses (e.g. GET object?versionId=X
for a version that was deleted or expired), this turned each 404 into N
filer round-trips and produced a misleading error string.

A reachable filer that answers ErrNotFound has given an authoritative
answer; failover exists to route around unreachable or unhealthy filers,
not to look harder for an entry the store reports as absent. Return
ErrNotFound directly instead of fanning out. Callers that need
read-after-write retries already handle that at the S3 semantic layer
(e.g. getLatestObjectVersion).
This commit is contained in:
Chris Lu
2026-05-30 15:07:27 -07:00
committed by GitHub
parent 34be9170f0
commit 3441a2a7f1
+11 -9
View File
@@ -47,14 +47,14 @@ func (s3a *S3ApiServer) withFilerClientFailover(streamingMode bool, fn func(file
return nil
}
// ErrNotFound is a valid application-level response (entry doesn't exist on this filer),
// not a filer health issue. Only record true failures (transport errors, timeouts, etc.)
// in the health tracker to avoid poisoning the circuit breaker with normal "not found"
// responses in multi-filer setups.
if !errors.Is(err, filer_pb.ErrNotFound) {
s3a.filerClient.RecordFilerFailure(currentFiler)
// A reachable filer answering ErrNotFound is authoritative; failover is for
// unreachable/unhealthy filers, not for re-asking about an absent entry.
if errors.Is(err, filer_pb.ErrNotFound) {
return err
}
s3a.filerClient.RecordFilerFailure(currentFiler)
// Current filer failed - try all other filers with health-aware selection
filers := s3a.filerClient.GetAllFilers()
var lastErr error = err
@@ -83,10 +83,12 @@ func (s3a *S3ApiServer) withFilerClientFailover(streamingMode bool, fn func(file
return nil
}
// Only record real failures, not ErrNotFound
if !errors.Is(err, filer_pb.ErrNotFound) {
s3a.filerClient.RecordFilerFailure(filer)
// Authoritative not-found - stop failing over.
if errors.Is(err, filer_pb.ErrNotFound) {
return err
}
s3a.filerClient.RecordFilerFailure(filer)
glog.V(2).Infof("WithFilerClient: failover to %s failed: %v", filer, err)
lastErr = err
}