@@ -30,16 +30,20 @@ var DefaultRetryOptions = RetryOptions{
30
30
}
31
31
32
32
func withRetries [T any ](ctx sync.Context , retryOptions RetryOptions , fn func (ctx sync.Context , attempt int ) Future [T ]) Future [T ] {
33
+ attempt := 0
34
+ firstAttempt := Now (ctx )
35
+
36
+ f := fn (ctx , attempt )
37
+
33
38
if retryOptions .MaxAttempts <= 1 {
34
39
// Short-circuit if we don't need to retry
35
- return fn ( ctx , 0 )
40
+ return f
36
41
}
37
42
43
+ // Start a separate co-routine for retries
38
44
r := sync .NewFuture [T ]()
39
45
40
46
sync .Go (ctx , func (ctx sync.Context ) {
41
- firstAttempt := Now (ctx )
42
-
43
47
var result T
44
48
var err error
45
49
@@ -48,32 +52,38 @@ func withRetries[T any](ctx sync.Context, retryOptions RetryOptions, fn func(ctx
48
52
retryExpiration = firstAttempt .Add (retryOptions .RetryTimeout )
49
53
}
50
54
51
- for attempt := 0 ; attempt < retryOptions .MaxAttempts ; attempt ++ {
52
- if ! retryExpiration .IsZero () && Now (ctx ).After (retryExpiration ) {
53
- // Reached maximum retry time, abort retries
55
+ for {
56
+ // Wait for active operation to finish
57
+ result , err = f .Get (ctx )
58
+ if err == nil {
54
59
break
55
60
}
56
61
57
- result , err = fn (ctx , attempt ).Get (ctx )
58
- if err != nil {
59
- if err == sync .Canceled {
60
- break
61
- }
62
+ if err == sync .Canceled {
63
+ break
64
+ }
62
65
63
- backoffDuration := time .Duration (float64 (retryOptions .FirstRetryInterval ) * math .Pow (retryOptions .BackoffCoefficient , float64 (attempt )))
64
- if retryOptions .MaxRetryInterval > 0 {
65
- backoffDuration = time .Duration (math .Min (float64 (backoffDuration ), float64 (retryOptions .MaxRetryInterval )))
66
- }
66
+ backoffDuration := time .Duration (float64 (retryOptions .FirstRetryInterval ) * math .Pow (retryOptions .BackoffCoefficient , float64 (attempt )))
67
+ if retryOptions .MaxRetryInterval > 0 {
68
+ backoffDuration = time .Duration (math .Min (float64 (backoffDuration ), float64 (retryOptions .MaxRetryInterval )))
69
+ }
67
70
68
- if err := Sleep (ctx , backoffDuration ); err != nil {
69
- r .Set (* new (T ), err )
70
- return
71
- }
71
+ if err := Sleep (ctx , backoffDuration ); err != nil {
72
+ r .Set (* new (T ), err )
73
+ return
74
+ }
72
75
73
- continue
76
+ if ! retryExpiration .IsZero () && Now (ctx ).After (retryExpiration ) {
77
+ // Reached maximum retry time, abort retries
78
+ break
79
+ }
80
+
81
+ attempt ++
82
+ if attempt >= retryOptions .MaxAttempts {
83
+ break
74
84
}
75
85
76
- break
86
+ f = fn ( ctx , attempt + 1 )
77
87
}
78
88
79
89
r .Set (result , err )
0 commit comments