Skip to content

Commit b0ca912

Browse files
authored
Adding custom backoff function for API's that _can_ be excessive (#354)
1 parent 23007d0 commit b0ca912

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

pkg/client/docker/docker.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import (
1212

1313
"github.com/sirupsen/logrus"
1414

15-
"github.com/hashicorp/go-retryablehttp"
15+
retryablehttp "github.com/hashicorp/go-retryablehttp"
1616
"github.com/jetstack/version-checker/pkg/api"
17+
"github.com/jetstack/version-checker/pkg/client/util"
1718
)
1819

1920
const (
@@ -41,8 +42,10 @@ func New(opts Options, log *logrus.Entry) (*Client, error) {
4142
}
4243
retryclient.HTTPClient.Timeout = 10 * time.Second
4344
retryclient.RetryMax = 10
44-
retryclient.RetryWaitMax = 2 * time.Minute
45+
retryclient.RetryWaitMax = 10 * time.Minute
4546
retryclient.RetryWaitMin = 1 * time.Second
47+
// This custom backoff will fail requests that have a max wait of the RetryWaitMax
48+
retryclient.Backoff = util.HTTPBackOff
4649
retryclient.Logger = log.WithField("client", "docker")
4750
client := retryclient.StandardClient()
4851

pkg/client/util/http_backoff.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package util
2+
3+
import (
4+
"net/http"
5+
"time"
6+
7+
"github.com/hashicorp/go-retryablehttp"
8+
)
9+
10+
// This is a custom Backoff that enforces the Max wait duration.
11+
// If the sleep is greater we refuse to sleep at all
12+
func HTTPBackOff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
13+
sleep := retryablehttp.DefaultBackoff(min, max, attemptNum, resp)
14+
if sleep.Abs() <= max {
15+
return sleep.Abs()
16+
}
17+
18+
return max.Abs()
19+
}

pkg/client/util/http_backoff_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package util
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
"time"
7+
8+
"github.com/hashicorp/go-retryablehttp"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestHTTPBackOff(t *testing.T) {
13+
tests := []struct {
14+
name string
15+
min time.Duration
16+
max time.Duration
17+
attemptNum int
18+
resp *http.Response
19+
expSleep time.Duration
20+
}{
21+
{
22+
name: "sleep within max duration",
23+
min: 100 * time.Millisecond,
24+
max: 1 * time.Second,
25+
attemptNum: 1,
26+
resp: nil,
27+
expSleep: retryablehttp.DefaultBackoff(100*time.Millisecond, 1*time.Second, 1, nil),
28+
},
29+
{
30+
name: "sleep exceeds max duration (too many requests)",
31+
min: 100 * time.Millisecond,
32+
max: 500 * time.Millisecond,
33+
attemptNum: 10,
34+
resp: &http.Response{
35+
StatusCode: http.StatusTooManyRequests,
36+
Header: http.Header{"Retry-After": []string{"3600"}}},
37+
expSleep: 500 * time.Millisecond,
38+
},
39+
{
40+
name: "zero max duration",
41+
min: 100 * time.Millisecond,
42+
max: 0,
43+
attemptNum: 1,
44+
resp: nil,
45+
expSleep: 0,
46+
},
47+
{
48+
name: "negative max duration",
49+
min: 100 * time.Millisecond,
50+
max: -1 * time.Second,
51+
attemptNum: 1,
52+
resp: nil,
53+
expSleep: time.Second,
54+
},
55+
}
56+
57+
for _, test := range tests {
58+
t.Run(test.name, func(t *testing.T) {
59+
gotSleep := HTTPBackOff(test.min, test.max, test.attemptNum, test.resp)
60+
assert.Equal(t, test.expSleep, gotSleep, "unexpected sleep duration")
61+
})
62+
}
63+
}

0 commit comments

Comments
 (0)