Skip to content

Commit 52a83d0

Browse files
committed
[#0] fix saga.WithRetry -> add ticker and context handing
1 parent b2729c3 commit 52a83d0

File tree

1 file changed

+38
-18
lines changed

1 file changed

+38
-18
lines changed

sage/entity.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -104,42 +104,62 @@ type RetryOptions struct {
104104

105105
// WithRetry returns a function with retry logic for execution.
106106
// It makes the specified number of attempts to call fn until it succeeds.
107-
// Before the first attempt, it waits for the specified Delay.
107+
// Between attempts, it waits for the specified Delay using a ticker.
108+
// The function respects context cancellation and will return context.Err() if done.
109+
//
110+
// Behavior:
111+
// - If Attempts = 0, the function immediately returns nil without executing fn
112+
// - Before each attempt, it waits for the Delay period
113+
// - The function stops retrying on first successful execution
114+
// - Context cancellation is respected between attempts
115+
// - All errors from failed attempts are collected
116+
//
108117
// If all attempts fail, behavior depends on ReturnAllAroseErr:
109-
// - if true - returns all errors via `errors.Join(...)`.
110-
// - if false - returns the lat error.
118+
// - if true - returns all errors via errors.Join(...)
119+
// - if false - returns only the last error that occurred
120+
//
121+
// Example:
122+
//
123+
// opts := RetryOptions{
124+
// Attempts: 3,
125+
// Delay: time.Second,
126+
// ReturnAllAroseErr: true,
127+
// }
128+
//
129+
// retryableFn := WithRetry(opts, func(ctx context.Context) error {
130+
// return someOperation(ctx)
131+
// })
111132
//
112-
// If Attempts = 0, the function immediately returns nil without executing fn.
133+
// err := retryableFn(ctx) // Will try up to 3 times with 1s delays
113134
func WithRetry(opt RetryOptions, fn func(ctx context.Context) error) func(ctx context.Context) error {
114135
return func(ctx context.Context) error {
115136
if opt.Attempts == 0 {
116137
return nil
117138
}
118139

119-
time.Sleep(opt.Delay)
120-
121140
var (
122141
err error
123142
retryErr []error
124-
i = 0
143+
ticker = time.NewTicker(opt.Delay)
125144
)
126-
for ; i < int(opt.Attempts); i++ {
127-
err = fn(ctx)
128-
if err == nil {
129-
break
145+
defer ticker.Stop()
146+
147+
for i := uint32(0); i < opt.Attempts; i++ {
148+
select {
149+
case <-ctx.Done():
150+
return ctx.Err()
151+
case <-ticker.C:
152+
err = fn(ctx)
153+
if err == nil {
154+
break
155+
}
156+
retryErr = append(retryErr, fmt.Errorf("retry [%d]: %w", i, err))
130157
}
131-
132-
retryErr = append(retryErr, fmt.Errorf("retry [%d]: %w", i, err))
133-
}
134-
135-
if len(retryErr) == 0 {
136-
return nil
137158
}
138159

139160
if opt.ReturnAllAroseErr {
140161
return errors.Join(retryErr...)
141162
}
142-
143163
return err
144164
}
145165
}

0 commit comments

Comments
 (0)