fix(depot): retry transient probe responses like curl --retry
build-binaries / build-binaries (pull_request) Successful in 2m24s
test-image / build-image (pull_request) Successful in 44s
test / test (pull_request) Failing after 1m33s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 00:40:15 +02:00
co-authored by Claude Fable 5
parent f2847d6bc9
commit 446d10925c
3 changed files with 99 additions and 1 deletions
+76
View File
@@ -120,6 +120,81 @@ func hostPort(srv *httptest.Server) string {
return srv.URL
}
// stubProbeSleep replaces probeRetrySleep for the test, recording durations.
func stubProbeSleep(t *testing.T) *[]time.Duration {
t.Helper()
var slept []time.Duration
old := probeRetrySleep
probeRetrySleep = func(d time.Duration) { slept = append(slept, d) }
t.Cleanup(func() { probeRetrySleep = old })
return &slept
}
func TestProbeRetriesTransientThenPresent(t *testing.T) {
slept := stubProbeSleep(t)
sha := shaOf("throttled-blob")
var calls int
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
if calls == 1 {
w.WriteHeader(428)
return
}
w.WriteHeader(206)
}))
defer srv.Close()
cfg := testCfg(hostPort(srv), "readkey", "writekey")
b, err := NewBackend("bunny", "", cfg)
if err != nil {
t.Fatalf("NewBackend: %v", err)
}
state, transient, err := b.Probe(context.Background(), sha)
if err != nil {
t.Fatalf("Probe: %v", err)
}
if state != Present || transient {
t.Fatalf("expected Present/non-transient after retry, got %v/%v", state, transient)
}
if calls != 2 {
t.Fatalf("expected 2 requests (428 then 206), got %d", calls)
}
if len(*slept) != 1 || (*slept)[0] != time.Second {
t.Fatalf("expected one 1s backoff, got %v", *slept)
}
}
func TestProbe404NoRetry(t *testing.T) {
slept := stubProbeSleep(t)
sha := shaOf("gone-blob")
f := newFakeBunny()
srv := httptest.NewServer(f.handler())
defer srv.Close()
cfg := testCfg(hostPort(srv), "readkey", "writekey")
b, err := NewBackend("bunny", "", cfg)
if err != nil {
t.Fatalf("NewBackend: %v", err)
}
state, transient, err := b.Probe(context.Background(), sha)
if err != nil {
t.Fatalf("Probe: %v", err)
}
if state != Absent || transient {
t.Fatalf("expected Absent/non-transient, got %v/%v", state, transient)
}
if got := len(f.requestsSnapshot()); got != 1 {
t.Fatalf("expected exactly 1 request for 404, got %d", got)
}
if len(*slept) != 0 {
t.Fatalf("expected zero backoffs for 404, got %v", *slept)
}
}
func TestProbeUsesRangeGetNotHead(t *testing.T) {
sha := shaOf("present-blob")
f := newFakeBunny()
@@ -157,6 +232,7 @@ func TestProbeUsesRangeGetNotHead(t *testing.T) {
}
func TestProbeStates(t *testing.T) {
stubProbeSleep(t)
cases := []struct {
status int
wantState ProbeState