chore(influxv2plugin): Increase accepted retry-after header values. (#9619)

This commit is contained in:
Phil Bracikowski 2021-08-25 15:43:06 -07:00 committed by GitHub
parent 8e8074e47b
commit 8daba8aa19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 11 deletions

View File

@ -36,8 +36,9 @@ func (e APIError) Error() string {
} }
const ( const (
defaultRequestTimeout = time.Second * 5 defaultRequestTimeout = time.Second * 5
defaultMaxWait = 60 // seconds defaultMaxWaitSeconds = 60
defaultMaxWaitRetryAfterSeconds = 10 * 60
) )
type HTTPConfig struct { type HTTPConfig struct {
@ -306,8 +307,9 @@ func (c *httpClient) writeBatch(ctx context.Context, bucket string, metrics []te
// retryDuration takes the longer of the Retry-After header and our own back-off calculation // retryDuration takes the longer of the Retry-After header and our own back-off calculation
func (c *httpClient) getRetryDuration(headers http.Header) time.Duration { func (c *httpClient) getRetryDuration(headers http.Header) time.Duration {
// basic exponential backoff (x^2)/40 (denominator to widen the slope) // basic exponential backoff (x^2)/40 (denominator to widen the slope)
// at 40 denominator, it'll take 35 retries to hit the max defaultMaxWait of 30s // at 40 denominator, it'll take 49 retries to hit the max defaultMaxWait of 60s
backoff := math.Pow(float64(c.retryCount), 2) / 40 backoff := math.Pow(float64(c.retryCount), 2) / 40
backoff = math.Min(backoff, defaultMaxWaitSeconds)
// get any value from the header, if available // get any value from the header, if available
retryAfterHeader := float64(0) retryAfterHeader := float64(0)
@ -319,11 +321,12 @@ func (c *httpClient) getRetryDuration(headers http.Header) time.Duration {
// there was a value but we couldn't parse it? guess minimum 10 sec // there was a value but we couldn't parse it? guess minimum 10 sec
retryAfterHeader = 10 retryAfterHeader = 10
} }
// protect against excessively large retry-after
retryAfterHeader = math.Min(retryAfterHeader, defaultMaxWaitRetryAfterSeconds)
} }
// take the highest value from both, but not over the max wait. // take the highest value of backoff and retry-after.
retry := math.Max(backoff, retryAfterHeader) retry := math.Max(backoff, retryAfterHeader)
retry = math.Min(retry, defaultMaxWait) return time.Duration(retry*1000) * time.Millisecond
return time.Duration(retry) * time.Second
} }
func (c *httpClient) makeWriteRequest(url string, body io.Reader) (*http.Request, error) { func (c *httpClient) makeWriteRequest(url string, body io.Reader) (*http.Request, error) {

View File

@ -56,12 +56,12 @@ func TestExponentialBackoffCalculation(t *testing.T) {
expected time.Duration expected time.Duration
}{ }{
{retryCount: 0, expected: 0}, {retryCount: 0, expected: 0},
{retryCount: 1, expected: 0}, {retryCount: 1, expected: 25 * time.Millisecond},
{retryCount: 5, expected: 0}, {retryCount: 5, expected: 625 * time.Millisecond},
{retryCount: 10, expected: 2 * time.Second}, {retryCount: 10, expected: 2500 * time.Millisecond},
{retryCount: 30, expected: 22 * time.Second}, {retryCount: 30, expected: 22500 * time.Millisecond},
{retryCount: 40, expected: 40 * time.Second}, {retryCount: 40, expected: 40 * time.Second},
{retryCount: 50, expected: 60 * time.Second}, {retryCount: 50, expected: 60 * time.Second}, // max hit
{retryCount: 100, expected: 60 * time.Second}, {retryCount: 100, expected: 60 * time.Second},
{retryCount: 1000, expected: 60 * time.Second}, {retryCount: 1000, expected: 60 * time.Second},
} }
@ -72,3 +72,29 @@ func TestExponentialBackoffCalculation(t *testing.T) {
}) })
} }
} }
func TestExponentialBackoffCalculationWithRetryAfter(t *testing.T) {
c := &httpClient{}
tests := []struct {
retryCount int
retryAfter string
expected time.Duration
}{
{retryCount: 0, retryAfter: "0", expected: 0},
{retryCount: 0, retryAfter: "10", expected: 10 * time.Second},
{retryCount: 0, retryAfter: "60", expected: 60 * time.Second},
{retryCount: 0, retryAfter: "600", expected: 600 * time.Second},
{retryCount: 0, retryAfter: "601", expected: 600 * time.Second}, // max hit
{retryCount: 40, retryAfter: "39", expected: 40 * time.Second}, // retryCount wins
{retryCount: 40, retryAfter: "41", expected: 41 * time.Second}, // retryAfter wins
{retryCount: 100, retryAfter: "100", expected: 100 * time.Second},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%d_retries", test.retryCount), func(t *testing.T) {
c.retryCount = test.retryCount
hdr := http.Header{}
hdr.Add("Retry-After", test.retryAfter)
require.EqualValues(t, test.expected, c.getRetryDuration(hdr))
})
}
}