package main
import (
"context"
"fmt"
"time"
"github.com/kashifkhan0771/utils/retry"
)
func main () {
opts := retry.Options {
MaxAttempts : 3 ,
TotalTimeout : 10 * time .Second ,
Backoff : retry .FixedBackoff (500 * time .Millisecond ),
ShouldRetry : func (err error ) bool { return true },
}
result , err := retry .Do (context .Background (), opts , func (ctx context.Context ) (string , error ) {
return callAPI (ctx )
})
fmt .Println (result , err )
}
Aborting on non-retryable errors
package main
import (
"context"
"errors"
"fmt"
"time"
"github.com/kashifkhan0771/utils/retry"
)
var ErrNotFound = errors .New ("not found" )
func main () {
opts := retry.Options {
MaxAttempts : 5 ,
TotalTimeout : 10 * time .Second ,
Backoff : retry .FixedBackoff (500 * time .Millisecond ),
ShouldRetry : func (err error ) bool {
return ! errors .Is (err , ErrNotFound )
},
}
_ , err := retry .Do (context .Background (), opts , func (ctx context.Context ) (string , error ) {
return "" , ErrNotFound
})
fmt .Println (err )
}
Using DoVoid for side-effecting operations
package main
import (
"context"
"fmt"
"time"
"github.com/kashifkhan0771/utils/retry"
)
func main () {
opts := retry.Options {
MaxAttempts : 3 ,
TotalTimeout : 10 * time .Second ,
Backoff : retry .FixedBackoff (200 * time .Millisecond ),
ShouldRetry : func (err error ) bool { return true },
}
err := retry .DoVoid (context .Background (), opts , func (ctx context.Context ) error {
return sendNotification (ctx )
})
fmt .Println (err )
}
Backoff strategies compared
package main
import (
"fmt"
"time"
"github.com/kashifkhan0771/utils/retry"
)
func main () {
fixed := retry .FixedBackoff (1 * time .Second )
linear := retry .LinearBackoff (1 * time .Second )
exponential := retry .ExponentialBackoff (1 * time .Second )
for attempt := range 4 {
fmt .Printf ("attempt %d fixed=%-6s linear=%-6s exponential=%s\n " ,
attempt ,
fixed (attempt ),
linear (attempt ),
exponential (attempt ),
)
}
}
attempt 0 fixed=1s linear=0s exponential=1s
attempt 1 fixed=1s linear=1s exponential=2s
attempt 2 fixed=1s linear=2s exponential=4s
attempt 3 fixed=1s linear=3s exponential=8s
Using optional fields (nil Backoff and ShouldRetry)
package main
import (
"context"
"fmt"
"time"
"github.com/kashifkhan0771/utils/retry"
)
func main () {
// Backoff and ShouldRetry are optional.
// nil Backoff = no delay between attempts.
// nil ShouldRetry = always retry.
opts := retry.Options {
MaxAttempts : 3 ,
TotalTimeout : 5 * time .Second ,
}
result , err := retry .Do (context .Background (), opts , func (ctx context.Context ) (string , error ) {
return callAPI (ctx )
})
fmt .Println (result , err )
}
Inspecting the cause after max attempts exhausted
package main
import (
"context"
"errors"
"fmt"
"time"
"github.com/kashifkhan0771/utils/retry"
)
var ErrServiceUnavailable = errors .New ("service unavailable" )
func main () {
opts := retry.Options {
MaxAttempts : 3 ,
TotalTimeout : 5 * time .Second ,
Backoff : retry .FixedBackoff (100 * time .Millisecond ),
ShouldRetry : func (err error ) bool { return true },
}
_ , err := retry .Do (context .Background (), opts , func (ctx context.Context ) (string , error ) {
return "" , ErrServiceUnavailable
})
fmt .Println (err )
fmt .Println (errors .Is (err , ErrServiceUnavailable ))
}
retry: max attempts reached: service unavailable
true
Respecting the total timeout
package main
import (
"context"
"fmt"
"time"
"github.com/kashifkhan0771/utils/retry"
)
func main () {
opts := retry.Options {
MaxAttempts : 10 ,
TotalTimeout : 2 * time .Second ,
Backoff : retry .ExponentialBackoff (1 * time .Second ),
ShouldRetry : func (err error ) bool { return true },
}
_ , err := retry .Do (context .Background (), opts , func (ctx context.Context ) (string , error ) {
return "" , fmt .Errorf ("service unavailable" )
})
fmt .Println (err )
}
context deadline exceeded