Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: cenkalti/backoff
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.0.2
Choose a base ref
...
head repository: cenkalti/backoff
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.1.0
Choose a head ref
  • 5 commits
  • 6 files changed
  • 3 contributors

Commits on Apr 14, 2020

  1. fix the links in README

    yashbhutwala authored and cenkalti committed Apr 14, 2020
    Copy the full SHA
    31cc31b View commit details

Commits on Jun 4, 2020

  1. Make Permanent(nil) return nil.

    bobg authored and cenkalti committed Jun 4, 2020
    Copy the full SHA
    382faa4 View commit details

Commits on Oct 8, 2020

  1. Copy the full SHA
    7175d05 View commit details
  2. Copy the full SHA
    cb87eb5 View commit details
  3. bump minimal go version to 1.13

    ptrus authored and cenkalti committed Oct 8, 2020
    Copy the full SHA
    c2975ff View commit details
Showing with 110 additions and 27 deletions.
  1. +3 −0 .gitignore
  2. +1 −1 .travis.yml
  3. +3 −4 README.md
  4. +1 −1 go.mod
  5. +15 −3 retry.go
  6. +87 −18 retry_test.go
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -20,3 +20,6 @@ _cgo_export.*
_testmain.go

*.exe

# IDEs
.idea/
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: go
go:
- 1.12
- 1.13
- 1.x
- tip
before_install:
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -11,16 +11,15 @@ The retries exponentially increase and stop increasing when a certain threshold

Import path is `github.com/cenkalti/backoff/v4`. Please note the version part at the end.

godoc.org does not support modules yet,
so you can use https://godoc.org/gopkg.in/cenkalti/backoff.v4 to view the documentation.
Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation.

## Contributing

* I would like to keep this library as small as possible.
* Please don't send a PR without opening an issue and discussing it first.
* If proposed change is not a common use case, I will probably not accept it.

[godoc]: https://godoc.org/github.com/cenkalti/backoff
[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v4
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
[travis]: https://travis-ci.org/cenkalti/backoff
[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master
@@ -30,4 +29,4 @@ so you can use https://godoc.org/gopkg.in/cenkalti/backoff.v4 to view the docume
[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java
[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff

[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_
[advanced example]: https://pkg.go.dev/github.com/cenkalti/backoff/v4?tab=doc#pkg-examples
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/cenkalti/backoff/v4

go 1.12
go 1.13
18 changes: 15 additions & 3 deletions retry.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package backoff

import "time"
import (
"errors"
"time"
)

// An Operation is executing by Retry() or RetryNotify().
// The operation will be retried using a backoff policy if it returns an error.
@@ -53,7 +56,8 @@ func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer
return nil
}

if permanent, ok := err.(*PermanentError); ok {
var permanent *PermanentError
if errors.As(err, &permanent) {
return permanent.Err
}

@@ -88,8 +92,16 @@ func (e *PermanentError) Unwrap() error {
return e.Err
}

func (e *PermanentError) Is(target error) bool {
_, ok := target.(*PermanentError)
return ok
}

// Permanent wraps the given err in a *PermanentError.
func Permanent(err error) *PermanentError {
func Permanent(err error) error {
if err == nil {
return nil
}
return &PermanentError{
Err: err,
}
105 changes: 87 additions & 18 deletions retry_test.go
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"io"
"log"
"testing"
"time"
@@ -88,29 +89,97 @@ func TestRetryContext(t *testing.T) {
}
}

func TestRetryPermenent(t *testing.T) {
const permanentOn = 3
var i = 0

// This function fails permanently after permanentOn tries
f := func() error {
i++
log.Printf("function is called %d. time\n", i)
func TestRetryPermanent(t *testing.T) {
ensureRetries := func(test string, shouldRetry bool, f func() error) {
numRetries := -1
maxRetries := 1

_ = RetryNotifyWithTimer(
func() error {
numRetries++
if numRetries >= maxRetries {
return Permanent(errors.New("forced"))
}
return f()
},
NewExponentialBackOff(),
nil,
&testTimer{},
)

if shouldRetry && numRetries == 0 {
t.Errorf("Test: '%s', backoff should have retried", test)
}

if i == permanentOn {
log.Println("permanent error")
return Permanent(errors.New("permanent error"))
if !shouldRetry && numRetries > 0 {
t.Errorf("Test: '%s', backoff should not have retried", test)
}
}

log.Println("error")
return errors.New("error")
for _, testCase := range []struct {
name string
f func() error
shouldRetry bool
}{
{
"nil test",
func() error {
return nil
},
false,
},
{
"io.EOF",
func() error {
return io.EOF
},
true,
},
{
"Permanent(io.EOF)",
func() error {
return Permanent(io.EOF)
},
false,
},
{
"Wrapped: Permanent(io.EOF)",
func() error {
return fmt.Errorf("Wrapped error: %w", Permanent(io.EOF))
},
false,
},
} {
ensureRetries(testCase.name, testCase.shouldRetry, testCase.f)
}
}

err := RetryNotifyWithTimer(f, NewExponentialBackOff(), nil, &testTimer{})
if err == nil || err.Error() != "permanent error" {
t.Errorf("unexpected error: %s", err)
func TestPermanent(t *testing.T) {
want := errors.New("foo")
other := errors.New("bar")
var err error = Permanent(want)

got := errors.Unwrap(err)
if got != want {
t.Errorf("got %v, want %v", got, want)
}
if i != permanentOn {
t.Errorf("invalid number of retries: %d", i)

if is := errors.Is(err, want); !is {
t.Errorf("err: %v is not %v", err, want)
}

if is := errors.Is(err, other); is {
t.Errorf("err: %v is %v", err, other)
}

wrapped := fmt.Errorf("wrapped: %w", err)
var permanent *PermanentError
if !errors.As(wrapped, &permanent) {
t.Errorf("errors.As(%v, %v)", wrapped, permanent)
}

err = Permanent(nil)
if err != nil {
t.Errorf("got %v, want nil", err)
}
}