@@ -18,16 +18,59 @@ type ExecuteConfig[IN, DEP any] struct {
18
18
type ExecuteOption [IN , DEP any ] func (* ExecuteConfig [IN , DEP ])
19
19
20
20
type RetryConfig [IN , DEP any ] struct {
21
- // DisableRetry disables the retry mechanism if set to true.
22
- DisableRetry bool
21
+ // Enabled dermines if the retry is enabled for the operation.
22
+ Enabled bool
23
+
24
+ // Policy is the retry policy to control the behavior of the retry.
25
+ Policy RetryPolicy
26
+
23
27
// InputHook is a function that returns an updated input before retrying the operation.
24
28
// The operation when retried will use the input returned by this function.
25
29
// This is useful for scenarios like updating the gas limit.
26
- // This will be ignored if DisableRetry is set to true.
27
30
InputHook func (input IN , deps DEP ) IN
28
31
}
29
32
30
- // WithRetryConfig is an ExecuteOption that sets the retry configuration.
33
+ // newDisabledRetryConfig returns a default retry configuration that is initially disabled.
34
+ func newDisabledRetryConfig [IN , DEP any ]() RetryConfig [IN , DEP ] {
35
+ return RetryConfig [IN , DEP ]{
36
+ Enabled : false ,
37
+ Policy : RetryPolicy {
38
+ MaxAttempts : 10 ,
39
+ },
40
+ }
41
+ }
42
+
43
+ // RetryPolicy defines the arguments to control the retry behavior.
44
+ type RetryPolicy struct {
45
+ MaxAttempts uint
46
+ }
47
+
48
+ // options returns the 'avast/retry' functional options for the retry policy.
49
+ func (p RetryPolicy ) options () []retry.Option {
50
+ return []retry.Option {
51
+ retry .Attempts (p .MaxAttempts ),
52
+ }
53
+ }
54
+
55
+ // WithRetry is an ExecuteOption that enables the default retry for the operation.
56
+ func WithRetry [IN , DEP any ]() ExecuteOption [IN , DEP ] {
57
+ return func (c * ExecuteConfig [IN , DEP ]) {
58
+ c .retryConfig .Enabled = true
59
+ }
60
+ }
61
+
62
+ // WithRetryInput is an ExecuteOption that enables the default retry and provide an input
63
+ // transform function which will modify the input on each retry attempt.
64
+ func WithRetryInput [IN , DEP any ](inputHookFunc func (IN , DEP ) IN ) ExecuteOption [IN , DEP ] {
65
+ return func (c * ExecuteConfig [IN , DEP ]) {
66
+ c .retryConfig .Enabled = true
67
+ c .retryConfig .InputHook = inputHookFunc
68
+ }
69
+ }
70
+
71
+ // WithRetryConfig is an ExecuteOption that sets the retry configuration. This provides a way to
72
+ // customize the retry behavior specific to the needs of the operation. Use this for the most
73
+ // flexibility and control over the retry behavior.
31
74
func WithRetryConfig [IN , DEP any ](config RetryConfig [IN , DEP ]) ExecuteOption [IN , DEP ] {
32
75
return func (c * ExecuteConfig [IN , DEP ]) {
33
76
c .retryConfig = config
@@ -69,39 +112,53 @@ func ExecuteOperation[IN, OUT, DEP any](
69
112
return previousReport , nil
70
113
}
71
114
72
- executeConfig := & ExecuteConfig [IN , DEP ]{retryConfig : RetryConfig [IN , DEP ]{}}
115
+ executeConfig := & ExecuteConfig [IN , DEP ]{
116
+ retryConfig : newDisabledRetryConfig [IN , DEP ](),
117
+ }
73
118
for _ , opt := range opts {
74
119
opt (executeConfig )
75
120
}
76
121
77
122
var output OUT
78
123
var err error
79
124
80
- if executeConfig .retryConfig .DisableRetry {
81
- output , err = operation .execute (b , deps , input )
82
- } else {
125
+ if executeConfig .retryConfig .Enabled {
83
126
var inputTemp = input
84
- output , err = retry .DoWithData (func () (OUT , error ) {
85
- return operation .execute (b , deps , inputTemp )
86
- }, retry .OnRetry (func (attempt uint , err error ) {
127
+
128
+ // Generate the configurable options for the retry
129
+ retryOpts := executeConfig .retryConfig .Policy .options ()
130
+ // Use the operation context in the retry
131
+ retryOpts = append (retryOpts , retry .Context (b .GetContext ()))
132
+ // Append the retry logic which will log the retry and attempt to transform the input
133
+ // if the user provided a custom input hook.
134
+ retryOpts = append (retryOpts , retry .OnRetry (func (attempt uint , err error ) {
87
135
b .Logger .Infow ("Operation failed. Retrying..." ,
88
136
"operation" , operation .def .ID , "attempt" , attempt , "error" , err )
89
137
90
138
if executeConfig .retryConfig .InputHook != nil {
91
139
inputTemp = executeConfig .retryConfig .InputHook (inputTemp , deps )
92
140
}
93
141
}))
142
+
143
+ output , err = retry .DoWithData (
144
+ func () (OUT , error ) {
145
+ return operation .execute (b , deps , inputTemp )
146
+ },
147
+ retryOpts ... ,
148
+ )
149
+ } else {
150
+ output , err = operation .execute (b , deps , input )
94
151
}
95
152
96
153
if err == nil && ! IsSerializable (b .Logger , output ) {
97
154
return Report [IN , OUT ]{}, fmt .Errorf ("operation %s output: %w" , operation .def .ID , ErrNotSerializable )
98
155
}
99
156
100
157
report := NewReport (operation .def , input , output , err )
101
- err = b .reporter .AddReport (genericReport (report ))
102
- if err != nil {
158
+ if err = b .reporter .AddReport (genericReport (report )); err != nil {
103
159
return Report [IN , OUT ]{}, err
104
160
}
161
+
105
162
if report .Err != nil {
106
163
return report , report .Err
107
164
}
@@ -175,14 +232,15 @@ func ExecuteSequence[IN, OUT, DEP any](
175
232
childReports ... ,
176
233
)
177
234
178
- err = b .reporter .AddReport (genericReport (report ))
179
- if err != nil {
235
+ if err = b .reporter .AddReport (genericReport (report )); err != nil {
180
236
return SequenceReport [IN , OUT ]{}, err
181
237
}
238
+
182
239
executionReports , err := b .reporter .GetExecutionReports (report .ID )
183
240
if err != nil {
184
241
return SequenceReport [IN , OUT ]{}, err
185
242
}
243
+
186
244
if report .Err != nil {
187
245
return SequenceReport [IN , OUT ]{report , executionReports }, report .Err
188
246
}
@@ -229,6 +287,7 @@ func loadPreviousSuccessfulReport[IN, OUT any](
229
287
return typedReport , true
230
288
}
231
289
}
290
+
232
291
// No previous execution was found
233
292
return Report [IN , OUT ]{}, false
234
293
}
0 commit comments