diff --git a/backoff.go b/backoff.go index d0113ec9a..1a42e1a6f 100644 --- a/backoff.go +++ b/backoff.go @@ -46,6 +46,15 @@ type BackoffConfig struct { jitter float64 } +func (bc *BackoffConfig) setDefaults() { + md := bc.MaxDelay + *bc = *DefaultBackoffConfig + + if md > 0 { + bc.MaxDelay = md + } +} + func (bc *BackoffConfig) backoff(retries int) (t time.Duration) { if retries == 0 { return bc.baseDelay diff --git a/backoff_test.go b/backoff_test.go new file mode 100644 index 000000000..b3227b873 --- /dev/null +++ b/backoff_test.go @@ -0,0 +1,11 @@ +package grpc + +import "testing" + +func TestBackoffConfigDefaults(t *testing.T) { + b := BackoffConfig{} + b.setDefaults() + if b != *DefaultBackoffConfig { + t.Fatalf("expected BackoffConfig to pickup default parameters: %v != %v", b, *DefaultBackoffConfig) + } +} diff --git a/clientconn.go b/clientconn.go index 1562c0f92..ac56ea135 100644 --- a/clientconn.go +++ b/clientconn.go @@ -115,9 +115,21 @@ func WithPicker(p Picker) DialOption { } } +// WithBackoffMaxDelay configures the dialer to use the provided maximum delay +// when backing off after failed connection attempts. +func WithBackoffMaxDelay(md time.Duration) DialOption { + return WithBackoffConfig(&BackoffConfig{MaxDelay: md}) +} + // WithBackoffConfig configures the dialer to use the provided backoff // parameters after connection failures. +// +// Use WithBackoffMaxDelay until more parameters on BackoffConfig are opened up +// for use. func WithBackoffConfig(b *BackoffConfig) DialOption { + // Set defaults to ensure that provided BackoffConfig is valid and + // unexported fields get default values. + b.setDefaults() return withBackoff(b) } diff --git a/clientconn_test.go b/clientconn_test.go index 8eb1a225d..88c3bb401 100644 --- a/clientconn_test.go +++ b/clientconn_test.go @@ -80,3 +80,42 @@ func TestCredentialsMisuse(t *testing.T) { t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, ErrCredentialsMisuse) } } + +func TestWithBackoffConfigDefault(t *testing.T) { + testBackoffConfigSet(t, DefaultBackoffConfig) +} + +func TestWithBackoffConfig(t *testing.T) { + b := BackoffConfig{MaxDelay: DefaultBackoffConfig.MaxDelay / 2} + expected := b + expected.setDefaults() // defaults should be set + testBackoffConfigSet(t, &expected, WithBackoffConfig(&b)) +} + +func TestWithBackoffMaxDelay(t *testing.T) { + md := DefaultBackoffConfig.MaxDelay / 2 + expected := BackoffConfig{MaxDelay: md} + expected.setDefaults() + testBackoffConfigSet(t, &expected, WithBackoffMaxDelay(md)) +} + +func testBackoffConfigSet(t *testing.T, expected *BackoffConfig, opts ...DialOption) { + opts = append(opts, WithInsecure()) + conn, err := Dial("foo:80", opts...) + if err != nil { + t.Fatalf("unexpected error dialing connection: %v", err) + } + + if conn.dopts.bs == nil { + t.Fatalf("backoff config not set") + } + + actual, ok := conn.dopts.bs.(*BackoffConfig) + if !ok { + t.Fatalf("unexpected type of backoff config: %v", conn.dopts.bs) + } + + if *actual != *expected { + t.Fatalf("unexpected backoff config on connection: %v, want %v", actual, expected) + } +}