You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// "RoundTrip should not modify the request, except for consuming and closing the Request's Body."
37
-
ifreq.Body!=nil {
38
-
req.Body.Close()
39
-
}
40
-
req=req.Clone(ctx)
41
-
ifreq.GetBody!=nil {
42
-
varerrerror
43
-
req.Body, err=req.GetBody()
44
-
iferr!=nil {
45
-
returnnil, err
46
-
}
47
-
}
48
-
}
49
-
firstTry=false
50
-
51
40
// in theory, this RoundTripper we're invoking should close req.Body (per the RoundTripper contract), so we shouldn't have to 🤞
52
41
res, err:=d.roundTripper.RoundTrip(req)
53
42
iferr!=nil {
54
43
returnnil, err
55
44
}
56
45
57
-
// TODO 503 should probably result in at least one or two auto-retries (especially with the automatic retry delay this injects)
46
+
doRetry:=false
47
+
58
48
ifres.StatusCode==429 {
49
+
// just eat all available tokens and starve out the rate limiter (any 429 means we need to slow down, so our whole "bucket" is shot)
50
+
fori:=d.limiter.Tokens(); i>0; i-- {
51
+
_=d.limiter.Allow()
52
+
}
53
+
doRetry=true// TODO maximum number of retries? (perhaps a deadline instead? req.WithContext to inject a deadline? 👀)
54
+
}
55
+
56
+
// 503 should result in a few auto-retries (especially with the automatic retry delay this injects), but up to a limit so we don't contribute to the "thundering herd" too much in a serious outage
57
+
ifres.StatusCode==503&&maxTry503>1 {
58
+
maxTry503--
59
+
doRetry=true
60
+
// no need to eat up the rate limiter tokens as we do for 429 because this is not a rate limiting error (and we have the "requestRetryLimiter" that separately limits our retries of *this* request)
61
+
}
62
+
63
+
ifdoRetry {
59
64
// satisfy the big scary warnings on https://pkg.go.dev/net/http#RoundTripper and https://pkg.go.dev/net/http#Client.Do about the downsides of failing to Close the response body
60
65
iferr:=res.Body.Close(); err!=nil {
61
66
returnnil, err
62
67
}
63
68
64
-
// just eat all available tokens and starve out the rate limiter (any 429 means we need to slow down, so our whole "bucket" is shot)
65
-
fori:=d.limiter.Tokens(); i>0; i-- {
66
-
_=d.limiter.Allow()
69
+
// https://pkg.go.dev/net/http#RoundTripper
70
+
// "RoundTrip should not modify the request, except for consuming and closing the Request's Body."
71
+
ifreq.Body!=nil {
72
+
req.Body.Close()
73
+
}
74
+
req=req.Clone(ctx)
75
+
ifreq.GetBody!=nil {
76
+
varerrerror
77
+
req.Body, err=req.GetBody()
78
+
iferr!=nil {
79
+
returnnil, err
80
+
}
67
81
}
68
82
69
83
// TODO some way to notify upwards that we retried?
70
-
// TODO maximum number of retries? (perhaps a deadline instead? req.WithContext to inject a deadline? 👀)
71
-
// TODO implement more backoff logic than just one retry per second + docker hub rate limit?
84
+
// TODO implement more backoff logic than just one retry per second + docker hub rate limit (+ limited 503 retry)?
0 commit comments