diff --git a/algorithms/retry.go b/algorithms/retry.go new file mode 100644 index 0000000..741a90f --- /dev/null +++ b/algorithms/retry.go @@ -0,0 +1,38 @@ +package algorithms + +import ( + "time" + + "github.com/cenkalti/backoff" +) + +func Retry(op func() error, maxTries uint64) (err error) { + b := backoff.NewExponentialBackOff() + br := withMaxRetries(b, maxTries) + return backoff.Retry(op, br) +} + +func withMaxRetries(b backoff.BackOff, max uint64) backoff.BackOff { + return &backOffTries{delegate: b, maxTries: max} +} + +type backOffTries struct { + delegate backoff.BackOff + maxTries uint64 + numTries uint64 +} + +func (b *backOffTries) NextBackOff() time.Duration { + if b.maxTries > 0 { + b.numTries++ + if b.maxTries <= b.numTries { + return backoff.Stop + } + } + return b.delegate.NextBackOff() +} + +func (b *backOffTries) Reset() { + b.numTries = 0 + b.delegate.Reset() +} diff --git a/algorithms/retry_test.go b/algorithms/retry_test.go new file mode 100644 index 0000000..911e467 --- /dev/null +++ b/algorithms/retry_test.go @@ -0,0 +1,19 @@ +package algorithms + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestRetryAlwaysFails(t *testing.T) { + var counter int + err := Retry(func() error { + counter++ + return fmt.Errorf("some error") + }, 2) + + assert.Error(t, err, "expected error") + assert.Equal(t, "some error", err.Error()) + assert.Equal(t, counter, 2) +} diff --git a/go.mod b/go.mod index d7d806f..80d4f73 100644 --- a/go.mod +++ b/go.mod @@ -2,12 +2,15 @@ module github.com/pluralsh/polly go 1.19 -require github.com/stretchr/testify v1.8.1 +require ( + github.com/cenkalti/backoff v2.2.1+incompatible + github.com/samber/lo v1.33.0 + github.com/stretchr/testify v1.8.1 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/samber/lo v1.33.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ab194f4..9b1f24a 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,10 @@ +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/samber/lo v1.33.0 h1:2aKucr+rQV6gHpY3bpeZu69uYoQOzVhGT3J22Op6Cjk= @@ -12,9 +16,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=