MCPcopy
hub / github.com/cenkalti/backoff / Retry

Function Retry

retry.go:108–188  ·  view source on GitHub ↗

Retry attempts the operation until it succeeds, returns a Permanent error, or backoff completes. It ensures the operation is executed at least once. On success it returns the operation result and a nil error. On any failure it returns the last result and a *RetryError whose Cause reports why it sto

(ctx context.Context, operation Operation[T], opts ...RetryOption)

Source from the content-addressed store, hash-verified

106// abort an in-flight attempt. To bound only how long backoff keeps retrying,
107// without affecting in-flight attempts, use WithMaxElapsedTime instead.
108func Retry[T any](ctx context.Context, operation Operation[T], opts ...RetryOption) (T, error) {
109 // Initialize default retry options.
110 args := &retryOptions{
111 BackOff: NewExponentialBackOff(),
112 Timer: &defaultTimer{},
113 MaxElapsedTime: DefaultMaxElapsedTime,
114 }
115
116 // Apply user-provided options to the default settings.
117 for _, opt := range opts {
118 opt(args)
119 }
120
121 defer args.Timer.Stop()
122
123 startedAt := time.Now()
124 args.BackOff.Reset()
125 for numTries := uint(1); ; numTries++ {
126 // Execute the operation.
127 res, err := operation()
128 if err == nil {
129 return res, nil
130 }
131
132 // Stop immediately on a permanent error; surface it as a RetryError.
133 var perm *permanent
134 if errors.As(err, &perm) {
135 return res, &RetryError{LastErr: perm.err, Cause: ErrPermanent}
136 }
137
138 // A RetryAfterError carries the delay before the next attempt; if it also
139 // carries an underlying error, that is the meaningful error to report as
140 // LastErr should retrying stop (mirrors how a permanent error surfaces its
141 // inner error). errors.As matches it whether returned directly or wrapped.
142 lastErr := err
143 var retryAfter *RetryAfterError
144 if errors.As(err, &retryAfter) && retryAfter.err != nil {
145 lastErr = retryAfter.err
146 }
147
148 // Stop retrying if maximum tries exceeded.
149 if args.MaxTries > 0 && numTries >= args.MaxTries {
150 return res, &RetryError{LastErr: lastErr, Cause: ErrExhausted}
151 }
152
153 // Stop retrying if context is cancelled.
154 if cerr := context.Cause(ctx); cerr != nil {
155 return res, &RetryError{LastErr: lastErr, Cause: cerr}
156 }
157
158 // Calculate next backoff duration.
159 next := args.BackOff.NextBackOff()
160 if next == Stop {
161 return res, &RetryError{LastErr: lastErr, Cause: ErrExhausted}
162 }
163
164 // Reset backoff if a RetryAfterError requested a specific delay.
165 if retryAfter != nil {

Callers 15

TestRetryFunction · 0.85
TestRetryWithDataFunction · 0.85
TestRetryContextFunction · 0.85
TestRetryErrorFunction · 0.85
TestRetryPermanentFunction · 0.85
TestRetryPermanentErrorFunction · 0.85
TestIssue177Function · 0.85
TestRetryContextDeadlineFunction · 0.85
TestRetryNotifyFunction · 0.85
TestRetryAfterFunction · 0.85

Calls 6

NewExponentialBackOffFunction · 0.85
StopMethod · 0.65
ResetMethod · 0.65
NextBackOffMethod · 0.65
StartMethod · 0.65
CMethod · 0.65

Tested by 15

TestRetryFunction · 0.68
TestRetryWithDataFunction · 0.68
TestRetryContextFunction · 0.68
TestRetryErrorFunction · 0.68
TestRetryPermanentFunction · 0.68
TestRetryPermanentErrorFunction · 0.68
TestIssue177Function · 0.68
TestRetryContextDeadlineFunction · 0.68
TestRetryNotifyFunction · 0.68
TestRetryAfterFunction · 0.68

Used in the wild real call sites across dependent graphs

searching dependent graphs…