Skip to content

Commit

Permalink
feat(backoff): Add functional options for ExponentialBackOff Closes #136
Browse files Browse the repository at this point in the history
  • Loading branch information
Sufiyan-Mirza authored and cenkalti committed Sep 15, 2023
1 parent a04a6fe commit a83af7f
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 1 deletion.
57 changes: 56 additions & 1 deletion exponential.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ type Clock interface {
Now() time.Time
}

// ExponentialBackOffOpts is a function type used to configure ExponentialBackOff options.
type ExponentialBackOffOpts func(*ExponentialBackOff)

// Default values for ExponentialBackOff.
const (
DefaultInitialInterval = 500 * time.Millisecond
Expand All @@ -81,7 +84,7 @@ const (
)

// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff() *ExponentialBackOff {
func NewExponentialBackOff(opts ...ExponentialBackOffOpts) *ExponentialBackOff {
b := &ExponentialBackOff{
InitialInterval: DefaultInitialInterval,
RandomizationFactor: DefaultRandomizationFactor,
Expand All @@ -91,10 +94,62 @@ func NewExponentialBackOff() *ExponentialBackOff {
Stop: Stop,
Clock: SystemClock,
}
for _, fn := range opts {
fn(b)
}
b.Reset()
return b
}

// WithInitialInterval sets the initial interval between retries.
func WithInitialInterval(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.InitialInterval = duration
}
}

// WithRandomizationFactor sets the randomization factor to add jitter to intervals.
func WithRandomizationFactor(randomizationFactor float64) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.RandomizationFactor = randomizationFactor
}
}

// WithMultiplier sets the multiplier for increasing the interval after each retry.
func WithMultiplier(multiplier float64) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Multiplier = multiplier
}
}

// WithMaxInterval sets the maximum interval between retries.
func WithMaxInterval(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.MaxInterval = duration
}
}

// WithMaxElapsedTime sets the maximum total time for retries.
func WithMaxElapsedTime(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.MaxElapsedTime = duration
}
}

// WithRetryStopDuration sets the duration after which retries should stop.
func WithRetryStopDuration(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Stop = duration
}
}

// WithClockProvider sets the clock used to measure time.
func WithClockProvider(clock Clock) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Clock = clock
}
}

type systemClock struct{}

func (t systemClock) Now() time.Time {
Expand Down
42 changes: 42 additions & 0 deletions exponential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,45 @@ func assertEquals(t *testing.T, expected, value time.Duration) {
t.Errorf("got: %d, expected: %d", value, expected)
}
}

func TestNewExponentialBackOff(t *testing.T) {
// Create a new ExponentialBackOff with custom options
backOff := NewExponentialBackOff(
WithInitialInterval(1*time.Second),
WithMultiplier(2.0),
WithMaxInterval(10*time.Second),
WithMaxElapsedTime(30*time.Second),
WithRetryStopDuration(0),
WithClockProvider(SystemClock),
)

// Check that the backOff object is not nil
if backOff == nil {
t.Error("Expected a non-nil ExponentialBackOff object, got nil")
}

// Check that the custom options were applied correctly
if backOff.InitialInterval != 1*time.Second {
t.Errorf("Expected InitialInterval to be 1 second, got %v", backOff.InitialInterval)
}

if backOff.Multiplier != 2.0 {
t.Errorf("Expected Multiplier to be 2.0, got %v", backOff.Multiplier)
}

if backOff.MaxInterval != 10*time.Second {
t.Errorf("Expected MaxInterval to be 10 seconds, got %v", backOff.MaxInterval)
}

if backOff.MaxElapsedTime != 30*time.Second {
t.Errorf("Expected MaxElapsedTime to be 30 seconds, got %v", backOff.MaxElapsedTime)
}

if backOff.Stop != 0 {
t.Errorf("Expected Stop to be 0 (no stop), got %v", backOff.Stop)
}

if backOff.Clock != SystemClock {
t.Errorf("Expected Clock to be SystemClock, got %v", backOff.Clock)
}
}

0 comments on commit a83af7f

Please sign in to comment.