47
47
import java .util .concurrent .Future ;
48
48
import java .util .concurrent .RejectedExecutionException ;
49
49
import java .util .concurrent .ScheduledExecutorService ;
50
- import java .util .concurrent .TimeUnit ;
51
- import org .junit .jupiter .api .AfterEach ;
52
50
import org .junit .jupiter .api .Test ;
53
- import org .junit .jupiter .params .ParameterizedTest ;
54
- import org .junit .jupiter .params .provider .MethodSource ;
55
51
import org .mockito .Mockito ;
56
52
57
53
class ScheduledRetryingExecutorTest extends AbstractRetryingExecutorTest {
58
- private ScheduledExecutorService scheduler = Executors .newSingleThreadScheduledExecutor ();
59
54
60
55
// Number of test runs, essential for multithreaded tests.
61
56
private static final int EXECUTIONS_COUNT = 5 ;
62
57
63
58
@ Override
64
59
protected RetryingExecutorWithContext <String > getExecutor (RetryAlgorithm <String > retryAlgorithm ) {
65
- return getRetryingExecutor (retryAlgorithm , scheduler );
60
+ return getRetryingExecutor (retryAlgorithm , scheduledExecutorService );
66
61
}
67
62
68
63
@ Override
@@ -78,30 +73,24 @@ private RetryingExecutorWithContext<String> getRetryingExecutor(
78
73
return new ScheduledRetryingExecutor <>(retryAlgorithm , scheduler );
79
74
}
80
75
81
- @ AfterEach
82
- void after () {
83
- scheduler .shutdownNow ();
84
- }
85
-
86
76
@ Test
87
77
void testSuccessWithFailuresPeekAttempt () throws Exception {
78
+ RetrySettings retrySettings =
79
+ FAST_RETRY_SETTINGS
80
+ .toBuilder ()
81
+ .setTotalTimeoutDuration (java .time .Duration .ofMillis (1000L ))
82
+ .setMaxAttempts (100 )
83
+ .build ();
88
84
for (int executionsCount = 0 ; executionsCount < EXECUTIONS_COUNT ; executionsCount ++) {
89
- final int maxRetries = 100 ;
90
85
91
- ScheduledExecutorService localExecutor = Executors .newSingleThreadScheduledExecutor ();
92
86
FailingCallable callable = new FailingCallable (15 , "request" , "SUCCESS" , tracer );
93
87
94
- RetrySettings retrySettings =
95
- FAST_RETRY_SETTINGS
96
- .toBuilder ()
97
- .setTotalTimeoutDuration (java .time .Duration .ofMillis (1000L ))
98
- .setMaxAttempts (maxRetries )
99
- .build ();
100
-
101
88
RetryingExecutorWithContext <String > executor =
102
- getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), localExecutor );
89
+ getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), scheduledExecutorService );
103
90
RetryingFuture <String > future =
104
- executor .createFuture (callable , FakeCallContext .createDefault ().withTracer (tracer ));
91
+ executor .createFuture (
92
+ callable ,
93
+ FakeCallContext .createDefault ().withTracer (tracer ).withRetrySettings (retrySettings ));
105
94
callable .setExternalFuture (future );
106
95
107
96
assertNull (future .peekAttemptResult ());
@@ -131,28 +120,28 @@ void testSuccessWithFailuresPeekAttempt() throws Exception {
131
120
assertFutureSuccess (future );
132
121
assertEquals (15 , future .getAttemptSettings ().getAttemptCount ());
133
122
assertTrue (failedAttempts > 0 );
134
- localExecutor .shutdownNow ();
135
123
}
136
124
}
137
125
138
126
@ Test
139
127
void testSuccessWithFailuresGetAttempt () throws Exception {
128
+ int maxRetries = 100 ;
129
+ RetrySettings retrySettings =
130
+ FAST_RETRY_SETTINGS
131
+ .toBuilder ()
132
+ .setTotalTimeoutDuration (java .time .Duration .ofMillis (1000L ))
133
+ .setMaxAttempts (maxRetries )
134
+ .build ();
140
135
for (int executionsCount = 0 ; executionsCount < EXECUTIONS_COUNT ; executionsCount ++) {
141
- final int maxRetries = 100 ;
142
136
143
- ScheduledExecutorService localExecutor = Executors .newSingleThreadScheduledExecutor ();
144
137
FailingCallable callable = new FailingCallable (15 , "request" , "SUCCESS" , tracer );
145
- RetrySettings retrySettings =
146
- FAST_RETRY_SETTINGS
147
- .toBuilder ()
148
- .setTotalTimeoutDuration (java .time .Duration .ofMillis (1000L ))
149
- .setMaxAttempts (maxRetries )
150
- .build ();
151
138
152
139
RetryingExecutorWithContext <String > executor =
153
- getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), localExecutor );
140
+ getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), scheduledExecutorService );
154
141
RetryingFuture <String > future =
155
- executor .createFuture (callable , FakeCallContext .createDefault ().withTracer (tracer ));
142
+ executor .createFuture (
143
+ callable ,
144
+ FakeCallContext .createDefault ().withTracer (tracer ).withRetrySettings (retrySettings ));
156
145
callable .setExternalFuture (future );
157
146
158
147
assertNull (future .peekAttemptResult ());
@@ -185,75 +174,64 @@ void testSuccessWithFailuresGetAttempt() throws Exception {
185
174
assertFutureSuccess (future );
186
175
assertEquals (15 , future .getAttemptSettings ().getAttemptCount ());
187
176
assertTrue (checks > 1 && checks <= maxRetries , "checks is equal to " + checks );
188
- localExecutor .shutdownNow ();
189
177
}
190
178
}
191
179
192
- @ ParameterizedTest
193
- @ MethodSource ("data" )
194
- void testCancelGetAttempt (boolean withCustomRetrySettings ) throws Exception {
195
- setUp (withCustomRetrySettings );
196
- for (int executionsCount = 0 ; executionsCount < EXECUTIONS_COUNT ; executionsCount ++) {
197
- ScheduledExecutorService localExecutor = Executors .newSingleThreadScheduledExecutor ();
198
- final int maxRetries = 20 ;
199
-
200
- FailingCallable callable = new FailingCallable (maxRetries - 1 , "request" , "SUCCESS" , tracer );
201
- RetrySettings retrySettings =
202
- FAST_RETRY_SETTINGS
203
- .toBuilder ()
204
- .setTotalTimeoutDuration (java .time .Duration .ofMillis (5000L ))
205
- .setMaxAttempts (maxRetries )
206
- .build ();
207
-
208
- RetryingExecutorWithContext <String > executor =
209
- getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), localExecutor );
210
- RetryingFuture <String > future = executor .createFuture (callable , retryingContext );
211
- callable .setExternalFuture (future );
212
-
213
- assertNull (future .peekAttemptResult ());
214
- assertSame (future .getAttemptResult (), future .getAttemptResult ());
215
- assertFalse (future .getAttemptResult ().isDone ());
216
- assertFalse (future .getAttemptResult ().isCancelled ());
217
-
218
- future .setAttemptFuture (executor .submit (future ));
219
-
220
- CustomException exception ;
221
- CancellationException cancellationException = null ;
222
- int checks = 0 ;
223
- int failedCancelations = 0 ;
224
- do {
225
- exception = null ;
226
- checks ++;
227
- Future <String > attemptResult = future .getAttemptResult ();
228
- try {
229
- attemptResult .get ();
230
- assertNotNull (future .peekAttemptResult ());
231
- } catch (CancellationException e ) {
232
- cancellationException = e ;
233
- } catch (ExecutionException e ) {
234
- exception = (CustomException ) e .getCause ();
235
- }
236
- assertTrue (attemptResult .isDone ());
237
- if (!future .cancel (true )) {
238
- failedCancelations ++;
239
- }
240
- } while (exception != null && checks < maxRetries );
241
-
242
- assertTrue (future .isDone ());
243
- assertNotNull (cancellationException );
244
- // future.cancel(true) may return false sometimes, which is ok. Also, the every cancellation
245
- // of
246
- // an already cancelled future should return false (this is what -1 means here)
247
- assertEquals (2 , checks - (failedCancelations - 1 ));
248
- assertTrue (future .getAttemptSettings ().getAttemptCount () > 0 );
249
- assertFutureCancel (future );
250
- localExecutor .shutdownNow ();
251
- }
180
+ @ Test
181
+ void testCancelGetAttempt () throws Exception {
182
+ int maxRetries = 100 ;
183
+ RetrySettings retrySettings =
184
+ FAST_RETRY_SETTINGS
185
+ .toBuilder ()
186
+ .setInitialRpcTimeoutDuration (java .time .Duration .ofMillis (50L ))
187
+ .setMaxRpcTimeoutDuration (java .time .Duration .ofMillis (50L ))
188
+ .setTotalTimeoutDuration (java .time .Duration .ofMillis (5000L ))
189
+ .setMaxAttempts (maxRetries )
190
+ .build ();
191
+ FailingCallable callable = new FailingCallable (maxRetries - 1 , "request" , "SUCCESS" , tracer );
192
+
193
+ RetryingExecutorWithContext <String > executor =
194
+ getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), scheduledExecutorService );
195
+ RetryingFuture <String > future =
196
+ executor .createFuture (
197
+ callable ,
198
+ FakeCallContext .createDefault ().withTracer (tracer ).withRetrySettings (retrySettings ));
199
+ callable .setExternalFuture (future );
200
+
201
+ assertNull (future .peekAttemptResult ());
202
+ assertSame (future .getAttemptResult (), future .getAttemptResult ());
203
+ assertFalse (future .getAttemptResult ().isDone ());
204
+ assertFalse (future .getAttemptResult ().isCancelled ());
205
+
206
+ future .setAttemptFuture (executor .submit (future ));
207
+
208
+ CustomException exception ;
209
+ CancellationException cancellationException = null ;
210
+ int checks = 0 ;
211
+ do {
212
+ exception = null ;
213
+ checks ++;
214
+ Future <String > attemptResult = future .getAttemptResult ();
215
+ try {
216
+ attemptResult .get ();
217
+ assertNotNull (future .peekAttemptResult ());
218
+ } catch (CancellationException e ) {
219
+ cancellationException = e ;
220
+ } catch (ExecutionException e ) {
221
+ exception = (CustomException ) e .getCause ();
222
+ }
223
+ future .cancel (true );
224
+ assertTrue (attemptResult .isDone ());
225
+ } while (exception != null && checks < maxRetries );
226
+
227
+ assertTrue (future .isDone ());
228
+ assertNotNull (cancellationException );
229
+ assertTrue (future .getAttemptSettings ().getAttemptCount () > 0 );
230
+ assertFutureCancel (future );
252
231
}
253
232
254
233
@ Test
255
234
void testCancelOuterFutureAfterStart () throws Exception {
256
- ScheduledExecutorService localExecutor = Executors .newSingleThreadScheduledExecutor ();
257
235
RetrySettings retrySettings =
258
236
FAST_RETRY_SETTINGS
259
237
.toBuilder ()
@@ -278,9 +256,11 @@ void testCancelOuterFutureAfterStart() throws Exception {
278
256
for (int executionsCount = 0 ; executionsCount < EXECUTIONS_COUNT ; executionsCount ++) {
279
257
FailingCallable callable = new FailingCallable (4 , "request" , "SUCCESS" , tracer );
280
258
RetryingExecutorWithContext <String > executor =
281
- getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), localExecutor );
259
+ getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), scheduledExecutorService );
282
260
RetryingFuture <String > future =
283
- executor .createFuture (callable , FakeCallContext .createDefault ().withTracer (tracer ));
261
+ executor .createFuture (
262
+ callable ,
263
+ FakeCallContext .createDefault ().withTracer (tracer ).withRetrySettings (retrySettings ));
284
264
callable .setExternalFuture (future );
285
265
future .setAttemptFuture (executor .submit (future ));
286
266
@@ -301,27 +281,29 @@ void testCancelOuterFutureAfterStart() throws Exception {
301
281
assertTrue (future .getAttemptSettings ().getAttemptCount () > 0 );
302
282
assertTrue (future .getAttemptSettings ().getAttemptCount () < 4 );
303
283
}
304
- localExecutor .shutdown ();
305
- localExecutor .awaitTermination (10 , TimeUnit .SECONDS );
306
284
}
307
285
308
286
@ Test
309
287
void testCancelProxiedFutureAfterStart () throws Exception {
288
+ RetrySettings retrySettings =
289
+ FAST_RETRY_SETTINGS
290
+ .toBuilder ()
291
+ .setInitialRetryDelayDuration (java .time .Duration .ofMillis (1_000L ))
292
+ .setMaxRetryDelayDuration (java .time .Duration .ofMillis (1_000L ))
293
+ .setTotalTimeoutDuration (java .time .Duration .ofMillis (10_0000L ))
294
+ .build ();
310
295
// this is a heavy test, which takes a lot of time, so only few executions.
311
296
for (int executionsCount = 0 ; executionsCount < 2 ; executionsCount ++) {
297
+ // Use a test local executor for this test case due to the reasons listed below
312
298
ScheduledExecutorService localExecutor = Executors .newSingleThreadScheduledExecutor ();
313
299
FailingCallable callable = new FailingCallable (5 , "request" , "SUCCESS" , tracer );
314
- RetrySettings retrySettings =
315
- FAST_RETRY_SETTINGS
316
- .toBuilder ()
317
- .setInitialRetryDelayDuration (java .time .Duration .ofMillis (1_000L ))
318
- .setMaxRetryDelayDuration (java .time .Duration .ofMillis (1_000L ))
319
- .setTotalTimeoutDuration (java .time .Duration .ofMillis (10_0000L ))
320
- .build ();
300
+
321
301
RetryingExecutorWithContext <String > executor =
322
302
getRetryingExecutor (getAlgorithm (retrySettings , 0 , null ), localExecutor );
323
303
RetryingFuture <String > future =
324
- executor .createFuture (callable , FakeCallContext .createDefault ().withTracer (tracer ));
304
+ executor .createFuture (
305
+ callable ,
306
+ FakeCallContext .createDefault ().withTracer (tracer ).withRetrySettings (retrySettings ));
325
307
callable .setExternalFuture (future );
326
308
future .setAttemptFuture (executor .submit (future ));
327
309
@@ -330,8 +312,8 @@ void testCancelProxiedFutureAfterStart() throws Exception {
330
312
// Note that shutdownNow() will not cancel internal FutureTasks automatically, which
331
313
// may potentially cause another thread handing on RetryingFuture#get() call forever.
332
314
// Canceling the tasks returned by shutdownNow() also does not help, because of missing
333
- // feature
334
- // in guava's ListenableScheduledFuture, which does not cancel itself, when its delegate is
315
+ // feature in guava's ListenableScheduledFuture, which does not cancel itself, when its
316
+ // delegate is
335
317
// canceled.
336
318
// So only the graceful shutdown() is supported properly.
337
319
localExecutor .shutdown ();
0 commit comments