Skip to content

Commit 1f78c35

Browse files
committed
Improve correctness, update README
1 parent a109a76 commit 1f78c35

File tree

8 files changed

+360
-352
lines changed

8 files changed

+360
-352
lines changed

README.md

Lines changed: 138 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,144 @@
1-
# retry
1+
# retry-go: Production Retry Logic for Go
22

33
[![Release](https://img.shields.io/github/release/codeGROOVE-dev/retry-go.svg?style=flat-square)](https://github.com/codeGROOVE-dev/retry-go/releases/latest)
44
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
55
[![Go Report Card](https://goreportcard.com/badge/github.com/codeGROOVE-dev/retry-go?style=flat-square)](https://goreportcard.com/report/github.com/codeGROOVE-dev/retry-go)
66
[![Go Reference](https://pkg.go.dev/badge/github.com/codeGROOVE-dev/retry-go.svg)](https://pkg.go.dev/github.com/codeGROOVE-dev/retry-go)
77

8-
## Fork Information
9-
10-
This is an actively maintained fork of [avast/retry-go](https://github.com/avast/retry-go), focused on reliability and simplicity. We extend our gratitude to the original authors and contributors at Avast for creating this excellent library.
11-
12-
This fork retains the original v4 API provided by the retry-go codebase, and is a drop-in replacement.
13-
14-
**Key improvements in this fork:**
15-
16-
- Zero dependencies (we removed usage of testify and it's dependencies)
17-
- Enhanced reliability and edge case handling
18-
- Active maintenance and bug fixes
19-
- Adherance to Go best practices, namely:
20-
- https://go.dev/wiki/TestComments
21-
- https://go.dev/wiki/CodeReviewComments
22-
- https://go.dev/doc/effective_go
23-
24-
**Original Project:** [github.com/avast/retry-go](https://github.com/avast/retry-go)
25-
26-
# SYNOPSIS
27-
28-
For full package documentation, see https://pkg.go.dev/github.com/codeGROOVE-dev/retry-go
29-
30-
HTTP GET with retry:
31-
32-
url := "http://example.com"
33-
var body []byte
34-
35-
err := retry.Do(
36-
func() error {
37-
resp, err := http.Get(url)
38-
if err != nil {
39-
return err
40-
}
41-
defer resp.Body.Close()
42-
body, err = ioutil.ReadAll(resp.Body)
43-
if err != nil {
44-
return err
45-
}
46-
return nil
47-
},
48-
)
49-
50-
if err != nil {
51-
// handle error
52-
}
53-
54-
fmt.Println(string(body))
55-
56-
HTTP GET with retry with data:
57-
58-
url := "http://example.com"
59-
60-
body, err := retry.DoWithData(
61-
func() ([]byte, error) {
62-
resp, err := http.Get(url)
63-
if err != nil {
64-
return nil, err
65-
}
66-
defer resp.Body.Close()
67-
body, err := ioutil.ReadAll(resp.Body)
68-
if err != nil {
69-
return nil, err
70-
}
71-
72-
return body, nil
73-
},
74-
)
75-
76-
if err != nil {
77-
// handle error
78-
}
79-
80-
fmt.Println(string(body))
81-
82-
[More examples](https://github.com/codeGROOVE-dev/retry-go/tree/master/examples)
83-
84-
85-
86-
# SEE ALSO
87-
88-
* [giantswarm/retry-go](https://github.com/giantswarm/retry-go) - slightly
89-
complicated interface.
90-
91-
* [sethgrid/pester](https://github.com/sethgrid/pester) - only http retry for
92-
http calls with retries and backoff
93-
94-
* [cenkalti/backoff](https://github.com/cenkalti/backoff) - Go port of the
95-
exponential backoff algorithm from Google's HTTP Client Library for Java. Really
96-
complicated interface.
97-
98-
* [rafaeljesus/retry-go](https://github.com/rafaeljesus/retry-go) - looks good,
99-
slightly similar as this package, don't have 'simple' `Retry` method
100-
101-
* [matryer/try](https://github.com/matryer/try) - very popular package,
102-
nonintuitive interface (for me)
103-
104-
## Contributing
105-
106-
Contributions are very much welcome.
107-
108-
### Before pull request
109-
110-
please try:
111-
* run tests (`make test`)
112-
* run linter (`make lint`)
113-
* if your IDE don't automaticaly do `go fmt`, run `go fmt` (`make fmt`)
8+
**Zero dependencies. Memory-bounded. Uptime-focused.**
9+
10+
*Because 99.99% uptime means your retry logic can't be the failure point.*
11+
12+
Actively maintained fork of [avast/retry-go](https://github.com/avast/retry-go) focused on correctness and resource efficiency. 100% API compatible drop-in replacement.
13+
14+
**Key improvements:**
15+
- ⚡ Zero external dependencies
16+
- 🔒 Memory-bounded error accumulation
17+
- 🛡️ Integer overflow protection in backoff
18+
- 🎯 Enhanced readability and debuggability
19+
- 📊 Predictable behavior under load
20+
21+
## Quick Start
22+
23+
### Basic Retry with Error Handling
24+
25+
```go
26+
import (
27+
"net/http"
28+
"github.com/codeGROOVE-dev/retry-go"
29+
)
30+
31+
// Retry API call with exponential backoff + jitter
32+
err := retry.Do(
33+
func() error {
34+
resp, err := http.Get("https://api.stripe.com/v1/charges")
35+
if err != nil {
36+
return err
37+
}
38+
defer resp.Body.Close()
39+
40+
if resp.StatusCode >= 500 {
41+
return fmt.Errorf("server error: %d", resp.StatusCode)
42+
}
43+
return nil
44+
},
45+
retry.Attempts(5),
46+
retry.DelayType(retry.CombineDelay(retry.BackOffDelay, retry.RandomDelay)),
47+
)
48+
```
49+
50+
### Retry with Data Return (Generics)
51+
52+
```go
53+
import (
54+
"context"
55+
"encoding/json"
56+
"github.com/codeGROOVE-dev/retry-go"
57+
)
58+
59+
// Database query with timeout and retry
60+
users, err := retry.DoWithData(
61+
func() ([]User, error) {
62+
return db.FindActiveUsers(ctx)
63+
},
64+
retry.Attempts(3),
65+
retry.Context(ctx),
66+
retry.DelayType(retry.BackOffDelay),
67+
)
68+
```
69+
70+
### Production Configuration
71+
72+
```go
73+
// Payment processing with comprehensive retry logic
74+
err := retry.Do(
75+
func() error { return paymentGateway.Charge(ctx, amount) },
76+
retry.Attempts(5),
77+
retry.Context(ctx),
78+
retry.DelayType(retry.FullJitterBackoffDelay),
79+
retry.MaxDelay(30*time.Second),
80+
retry.OnRetry(func(n uint, err error) {
81+
log.Warn("Payment retry", "attempt", n+1, "error", err)
82+
}),
83+
retry.RetryIf(func(err error) bool {
84+
return !isAuthError(err) // Don't retry 4xx errors
85+
}),
86+
)
87+
```
88+
89+
## Key Features
90+
91+
**Unrecoverable Errors** - Stop immediately for certain errors:
92+
```go
93+
if resp.StatusCode == 401 {
94+
return retry.Unrecoverable(errors.New("auth failed"))
95+
}
96+
```
97+
98+
**Context Integration** - Timeout and cancellation:
99+
```go
100+
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
101+
retry.Do(dbQuery, retry.Context(ctx))
102+
```
103+
104+
**Error-Specific Limits** - Different retry counts per error type:
105+
```go
106+
retry.AttemptsForError(1, sql.ErrTxDone) // Don't retry transaction errors
107+
```
108+
109+
## Performance & Scale
110+
111+
| Metric | Value |
112+
|--------|-------|
113+
| Memory overhead | ~200 bytes per operation |
114+
| Error accumulation | Bounded at 1000 errors (DoS protection) |
115+
| Goroutines | Uses calling goroutine only |
116+
| High-throughput safe | No hidden allocations or locks |
117+
118+
## Library Comparison
119+
120+
**[cenkalti/backoff](https://github.com/cenkalti/backoff)** - Complex interface, requires manual retry loops, no error accumulation.
121+
122+
**[sethgrid/pester](https://github.com/sethgrid/pester)** - HTTP-only, lacks general-purpose retry logic.
123+
124+
**[matryer/try](https://github.com/matryer/try)** - Popular but non-standard API, missing production features.
125+
126+
**[rafaeljesus/retry-go](https://github.com/rafaeljesus/retry-go)** - Similar design but lacks error-specific limits and comprehensive context handling.
127+
128+
**This fork** builds on avast/retry-go's solid foundation with correctness fixes and resource optimizations.
129+
130+
## Installation
131+
132+
```bash
133+
go get github.com/codeGROOVE-dev/retry-go
134+
```
135+
136+
## Documentation
137+
138+
- [API Docs](https://pkg.go.dev/github.com/codeGROOVE-dev/retry-go)
139+
- [Examples](https://github.com/codeGROOVE-dev/retry-go/tree/master/examples)
140+
- [Tests](https://github.com/codeGROOVE-dev/retry-go/tree/master/retry_test.go)
141+
142+
---
143+
144+
*Production retry logic that just works.*

examples/custom_retry_function_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import (
99
"testing"
1010
"time"
1111

12-
"github.com/codeGROOVE-dev/retry-go")
12+
"github.com/codeGROOVE-dev/retry-go"
13+
)
1314

1415
// RetriableError is a custom error that contains a positive duration for the next retry
1516
type RetriableError struct {

examples/delay_based_on_error_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import (
1010
"testing"
1111
"time"
1212

13-
"github.com/codeGROOVE-dev/retry-go")
13+
"github.com/codeGROOVE-dev/retry-go"
14+
)
1415

1516
type RetryAfterError struct {
1617
response http.Response

examples/errors_history_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import (
66
"net/http/httptest"
77
"testing"
88

9-
"github.com/codeGROOVE-dev/retry-go")
9+
"github.com/codeGROOVE-dev/retry-go"
10+
)
1011

1112
// TestErrorHistory shows an example of how to get all the previous errors when
1213
// retry.Do ends in success

examples/http_get_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import (
77
"net/http/httptest"
88
"testing"
99

10-
"github.com/codeGROOVE-dev/retry-go")
10+
"github.com/codeGROOVE-dev/retry-go"
11+
)
1112

1213
func TestGet(t *testing.T) {
1314
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)