diff --git a/config/consul_test.go b/config/consul_test.go index fb7cf8e02..bdc782a46 100644 --- a/config/consul_test.go +++ b/config/consul_test.go @@ -249,9 +249,10 @@ func TestConsulConfig_Finalize(t *testing.T) { Password: String(""), }, Retry: &RetryConfig{ - Backoff: TimeDuration(DefaultRetryBackoff), - Enabled: Bool(true), - Attempts: Int(DefaultRetryAttempts), + Backoff: TimeDuration(DefaultRetryBackoff), + MaxBackoff: TimeDuration(DefaultRetryMaxBackoff), + Enabled: Bool(true), + Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ CaCert: String(""), diff --git a/config/retry.go b/config/retry.go index 57c39f57d..7a8191ee8 100644 --- a/config/retry.go +++ b/config/retry.go @@ -13,6 +13,9 @@ const ( // DefaultRetryBackoff is the default base for the exponential backoff // algorithm. DefaultRetryBackoff = 250 * time.Millisecond + + // DefaultRetryMaxBackoff is the default maximum of backoff time + DefaultRetryMaxBackoff = 0 * time.Millisecond ) // RetryFunc is the signature of a function that supports retries. @@ -23,12 +26,17 @@ type RetryFunc func(int) (bool, time.Duration) type RetryConfig struct { // Attempts is the total number of maximum attempts to retry before letting // the error fall through. + // 0 means unlimited. Attempts *int // Backoff is the base of the exponentialbackoff. This number will be // multipled by the next power of 2 on each iteration. Backoff *time.Duration + // MaxBackoff is an upper limit the result of the sleep time between retries + // A MaxBackoff of zero means there is no limit to the exponential growth of the backoff. + MaxBackoff *time.Duration + // Enabled signals if this retry is enabled. Enabled *bool } @@ -51,6 +59,8 @@ func (c *RetryConfig) Copy() *RetryConfig { o.Backoff = c.Backoff + o.MaxBackoff = c.MaxBackoff + o.Enabled = c.Enabled return &o @@ -82,6 +92,10 @@ func (c *RetryConfig) Merge(o *RetryConfig) *RetryConfig { r.Backoff = o.Backoff } + if o.MaxBackoff != nil { + r.MaxBackoff = o.MaxBackoff + } + if o.Enabled != nil { r.Enabled = o.Enabled } @@ -103,6 +117,11 @@ func (c *RetryConfig) RetryFunc() RetryFunc { base := math.Pow(2, float64(retry)) sleep := time.Duration(base) * TimeDurationVal(c.Backoff) + maxSleep := TimeDurationVal(c.MaxBackoff) + if maxSleep > 0 && maxSleep < sleep { + return true, maxSleep + } + return true, sleep } } @@ -117,6 +136,10 @@ func (c *RetryConfig) Finalize() { c.Backoff = TimeDuration(DefaultRetryBackoff) } + if c.MaxBackoff == nil { + c.MaxBackoff = TimeDuration(DefaultRetryMaxBackoff) + } + if c.Enabled == nil { c.Enabled = Bool(true) } @@ -131,10 +154,12 @@ func (c *RetryConfig) GoString() string { return fmt.Sprintf("&RetryConfig{"+ "Attempts:%s, "+ "Backoff:%s, "+ + "MaxBackoff:%s, "+ "Enabled:%s"+ "}", IntGoString(c.Attempts), TimeDurationGoString(c.Backoff), + TimeDurationGoString(c.MaxBackoff), BoolGoString(c.Enabled), ) } diff --git a/config/retry_test.go b/config/retry_test.go index 1112dac30..41dc445ab 100644 --- a/config/retry_test.go +++ b/config/retry_test.go @@ -28,6 +28,15 @@ func TestRetryConfig_Copy(t *testing.T) { Enabled: Bool(true), }, }, + { + "max_backoff", + &RetryConfig{ + Attempts: Int(0), + Backoff: TimeDuration(20 * time.Second), + MaxBackoff: TimeDuration(100 * time.Second), + Enabled: Bool(true), + }, + }, } for i, tc := range cases { @@ -120,6 +129,32 @@ func TestRetryConfig_Merge(t *testing.T) { &RetryConfig{Backoff: TimeDuration(10 * time.Second)}, &RetryConfig{Backoff: TimeDuration(10 * time.Second)}, }, + + { + "maxbackoff_overrides", + &RetryConfig{MaxBackoff: TimeDuration(10 * time.Second)}, + &RetryConfig{MaxBackoff: TimeDuration(20 * time.Second)}, + &RetryConfig{MaxBackoff: TimeDuration(20 * time.Second)}, + }, + { + "maxbackoff_empty_one", + &RetryConfig{MaxBackoff: TimeDuration(10 * time.Second)}, + &RetryConfig{}, + &RetryConfig{MaxBackoff: TimeDuration(10 * time.Second)}, + }, + { + "maxbackoff_empty_two", + &RetryConfig{}, + &RetryConfig{MaxBackoff: TimeDuration(10 * time.Second)}, + &RetryConfig{MaxBackoff: TimeDuration(10 * time.Second)}, + }, + { + "maxbackoff_same", + &RetryConfig{MaxBackoff: TimeDuration(10 * time.Second)}, + &RetryConfig{MaxBackoff: TimeDuration(10 * time.Second)}, + &RetryConfig{MaxBackoff: TimeDuration(10 * time.Second)}, + }, + { "enabled_overrides", &RetryConfig{Enabled: Bool(true)}, @@ -166,9 +201,10 @@ func TestRetryConfig_Finalize(t *testing.T) { "empty", &RetryConfig{}, &RetryConfig{ - Attempts: Int(DefaultRetryAttempts), - Backoff: TimeDuration(DefaultRetryBackoff), - Enabled: Bool(true), + Attempts: Int(DefaultRetryAttempts), + Backoff: TimeDuration(DefaultRetryBackoff), + MaxBackoff: TimeDuration(DefaultRetryMaxBackoff), + Enabled: Bool(true), }, }, } diff --git a/config/vault_test.go b/config/vault_test.go index ab95a52df..38d1012fc 100644 --- a/config/vault_test.go +++ b/config/vault_test.go @@ -298,9 +298,10 @@ func TestVaultConfig_Finalize(t *testing.T) { Enabled: Bool(false), RenewToken: Bool(DefaultVaultRenewToken), Retry: &RetryConfig{ - Backoff: TimeDuration(DefaultRetryBackoff), - Enabled: Bool(true), - Attempts: Int(DefaultRetryAttempts), + Backoff: TimeDuration(DefaultRetryBackoff), + MaxBackoff: TimeDuration(DefaultRetryMaxBackoff), + Enabled: Bool(true), + Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ CaCert: String(""), @@ -334,9 +335,10 @@ func TestVaultConfig_Finalize(t *testing.T) { Enabled: Bool(true), RenewToken: Bool(DefaultVaultRenewToken), Retry: &RetryConfig{ - Backoff: TimeDuration(DefaultRetryBackoff), - Enabled: Bool(true), - Attempts: Int(DefaultRetryAttempts), + Backoff: TimeDuration(DefaultRetryBackoff), + MaxBackoff: TimeDuration(DefaultRetryMaxBackoff), + Enabled: Bool(true), + Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ CaCert: String(""), @@ -370,9 +372,10 @@ func TestVaultConfig_Finalize(t *testing.T) { Enabled: Bool(true), RenewToken: Bool(DefaultVaultRenewToken), Retry: &RetryConfig{ - Backoff: TimeDuration(DefaultRetryBackoff), - Enabled: Bool(true), - Attempts: Int(DefaultRetryAttempts), + Backoff: TimeDuration(DefaultRetryBackoff), + MaxBackoff: TimeDuration(DefaultRetryMaxBackoff), + Enabled: Bool(true), + Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ CaCert: String(""),