Skip to content

Commit

Permalink
Implement generic retry (#15)
Browse files Browse the repository at this point in the history
Simple, pluggable retry system.  Supports constant and exponential retries for now
  • Loading branch information
michaeljguarino authored Apr 4, 2023
1 parent 9b8314b commit f5b8532
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 0 deletions.
13 changes: 13 additions & 0 deletions algorithms/math.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package algorithms

type Numeric interface {
int | int8 | int16 | int32 | int64 | float64 | float32
}

func Max[T Numeric](a, b T) T {
if a > b {
return a
}

return b
}
47 changes: 47 additions & 0 deletions retry/algorithms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package retry

import (
"time"

"github.com/pluralsh/polly/algorithms"
)

type BackoffAlgorithm interface {
Backoff(iter int) time.Duration
Continue() bool
}

type Exponential struct {
mult float64
max float64
start float64
}

func (exp *Exponential) Backoff(iter int) time.Duration {
dur := algorithms.Max(exp.start*exp.mult, exp.max)
exp.start = dur
return time.Duration(exp.start)
}

func (exp *Exponential) Continue() bool {
if exp.max <= exp.start {
return false
}

return true
}

type Constant struct {
max int
dur int
count int
}

func (con *Constant) Backoff(iter int) time.Duration {
con.count++
return time.Duration(con.dur)
}

func (con *Constant) Continue() bool {
return con.count < con.max
}
24 changes: 24 additions & 0 deletions retry/retry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package retry

import "time"

func NewExponential(start, max int64, mult float64) *Exponential {
return &Exponential{start: float64(start), max: float64(max), mult: mult}
}

func NewConstant(duration, count int) *Constant {
return &Constant{dur: duration, max: count}
}

func Retry[T any](algo BackoffAlgorithm, fun func() (T, error)) (res T, err error) {
iter := 0
for {
res, err = fun()
if err != nil || !algo.Continue() {
return
}

iter++
time.Sleep(algo.Backoff(iter))
}
}
34 changes: 34 additions & 0 deletions retry/retry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package retry

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestConstantRetry(t *testing.T) {
algo := NewConstant(2, 5)
failer := func() (int, error) { return 0, fmt.Errorf("whoops") }

_, err := Retry(algo, failer)
assert.Error(t, err)

success := func() (int, error) { return 2, nil }
res, err := Retry(algo, success)
assert.NoError(t, err)
assert.Equal(t, res, 2)
}

func TestExponentialRetry(t *testing.T) {
algo := NewExponential(2, 5, 1.2)
failer := func() (int, error) { return 0, fmt.Errorf("whoops") }

_, err := Retry(algo, failer)
assert.Error(t, err)

success := func() (int, error) { return 2, nil }
res, err := Retry(algo, success)
assert.NoError(t, err)
assert.Equal(t, res, 2)
}

0 comments on commit f5b8532

Please sign in to comment.