Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(backoff): Add functional options for ExponentialBackOff #137

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
}
}