github.com/blend/go-sdk@v1.20220411.3/grpcutil/backoff.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package grpcutil 9 10 import ( 11 "context" 12 "math/rand" 13 "time" 14 ) 15 16 // BackoffFunc denotes a family of functions that control the backoff duration between call retries. 17 // 18 // They are called with an identifier of the attempt, and should return a time the system client should 19 // hold off for. If the time returned is longer than the `context.Context.Deadline` of the request 20 // the deadline of the request takes precedence and the wait will be interrupted before proceeding 21 // with the next iteration. 22 type BackoffFunc func(attempt uint) time.Duration 23 24 // BackoffFuncContext denotes a family of functions that control the backoff duration between call retries. 25 // 26 // They are called with an identifier of the attempt, and should return a time the system client should 27 // hold off for. If the time returned is longer than the `context.Context.Deadline` of the request 28 // the deadline of the request takes precedence and the wait will be interrupted before proceeding 29 // with the next iteration. The context can be used to extract request scoped metadata and context values. 30 type BackoffFuncContext func(ctx context.Context, attempt uint) time.Duration 31 32 // BackoffLinear is very simple: it waits for a fixed period of time between calls. 33 func BackoffLinear(waitBetween time.Duration) BackoffFunc { 34 return func(attempt uint) time.Duration { 35 return waitBetween 36 } 37 } 38 39 // BackoffLinearWithJitter waits a set period of time, allowing for jitter (fractional adjustment). 40 // 41 // For example waitBetween=1s and jitter=0.10 can generate waits between 900ms and 1100ms. 42 func BackoffLinearWithJitter(waitBetween time.Duration, jitterFraction float64) BackoffFunc { 43 return func(attempt uint) time.Duration { 44 return JitterUp(waitBetween, jitterFraction) 45 } 46 } 47 48 // BackoffExponential produces increasing intervals for each attempt. 49 // 50 // The scalar is multiplied times 2 raised to the current attempt. So the first 51 // retry with a scalar of 100ms is 100ms, while the 5th attempt would be 1.6s. 52 func BackoffExponential(scalar time.Duration) BackoffFunc { 53 return func(attempt uint) time.Duration { 54 return scalar * time.Duration(ExponentBase2(attempt)) 55 } 56 } 57 58 // BackoffExponentialWithJitter creates an exponential backoff like 59 // BackoffExponential does, but adds jitter. 60 func BackoffExponentialWithJitter(scalar time.Duration, jitterFraction float64) BackoffFunc { 61 return func(attempt uint) time.Duration { 62 return JitterUp(scalar*time.Duration(ExponentBase2(attempt)), jitterFraction) 63 } 64 } 65 66 // JitterUp adds random jitter to the duration. 67 // 68 // This adds or subtracts time from the duration within a given jitter fraction. 69 // For example for 10s and jitter 0.1, it will return a time within [9s, 11s]) 70 func JitterUp(duration time.Duration, jitter float64) time.Duration { 71 multiplier := jitter * (rand.Float64()*2 - 1) 72 return time.Duration(float64(duration) * (1 + multiplier)) 73 } 74 75 // ExponentBase2 computes 2^(a-1) where a >= 1. If a is 0, the result is 0. 76 func ExponentBase2(a uint) uint { 77 return (1 << a) >> 1 78 }