fix(depot): retry transient probe responses like curl --retry
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewBackend returns the backend for name ("local"|"cdn"|"bunny").
|
||||
@@ -85,9 +86,26 @@ func (b *httpBackend) cdnURL(sha string) string {
|
||||
return fmt.Sprintf("%s/%s", b.cfg.CDNBase, BlobKey(sha))
|
||||
}
|
||||
|
||||
// probeRetrySleep is the backoff sleeper for transient probe retries;
|
||||
// tests stub it (same pattern as sweep.go's confirmSleep).
|
||||
var probeRetrySleep = time.Sleep
|
||||
|
||||
// rangeProbe issues GET <url> with Range: bytes=0-0 and classifies the
|
||||
// response. Never HEAD (banned by spec).
|
||||
// response. Never HEAD (banned by spec). Transient outcomes (transport
|
||||
// error, or a status that is neither 2xx nor 404/410) retry up to 2 more
|
||||
// times with linear backoff (1s, 2s) — the CDN edge throttles sustained
|
||||
// sweeps; the bash this ports absorbed that with curl --retry 2.
|
||||
func (b *httpBackend) rangeProbe(ctx context.Context, url string, headers map[string]string) (ProbeState, bool, error) {
|
||||
for attempt := 0; ; attempt++ {
|
||||
state, transient, err := b.rangeProbeOnce(ctx, url, headers)
|
||||
if !transient || attempt >= 2 {
|
||||
return state, transient, err
|
||||
}
|
||||
probeRetrySleep(time.Duration(attempt+1) * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *httpBackend) rangeProbeOnce(ctx context.Context, url string, headers map[string]string) (ProbeState, bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return Unconfirmed, true, err
|
||||
@@ -212,6 +230,7 @@ func (b *httpBackend) fetch(ctx context.Context, sha string) (*http.Response, er
|
||||
if resp, err := b.get(ctx, b.cdnURL(sha), nil); err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||
return resp, nil
|
||||
} else if err == nil {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}
|
||||
resp, err := b.get(ctx, b.storageURL(sha), map[string]string{"AccessKey": b.cfg.ReadKey})
|
||||
@@ -219,6 +238,7 @@ func (b *httpBackend) fetch(ctx context.Context, sha string) (*http.Response, er
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("bunny get %s: unexpected status %d", sha, resp.StatusCode)
|
||||
}
|
||||
@@ -230,6 +250,7 @@ func (b *httpBackend) fetch(ctx context.Context, sha string) (*http.Response, er
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("cdn get %s: unexpected status %d", sha, resp.StatusCode)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user