|
15 | 15 | import java.util.concurrent.CompletableFuture; |
16 | 16 | import java.util.concurrent.Executors; |
17 | 17 | import java.util.concurrent.ScheduledExecutorService; |
| 18 | +import java.util.concurrent.ScheduledThreadPoolExecutor; |
| 19 | +import java.util.concurrent.TimeUnit; |
18 | 20 | import java.util.concurrent.atomic.AtomicBoolean; |
19 | 21 | import java.util.concurrent.atomic.AtomicInteger; |
20 | 22 |
|
21 | 23 | import static java.util.Arrays.asList; |
22 | 24 | import static java.util.Collections.singletonList; |
23 | 25 | import static org.awaitility.Awaitility.await; |
24 | 26 | import static org.awaitility.Duration.TWO_SECONDS; |
| 27 | +import static org.dataloader.fixtures.TestKit.idLoader; |
25 | 28 | import static org.dataloader.fixtures.TestKit.snooze; |
26 | 29 | import static org.hamcrest.MatcherAssert.assertThat; |
27 | 30 | import static org.hamcrest.Matchers.equalTo; |
@@ -350,6 +353,87 @@ public void chain_data_loaders_will_hang_if_not_in_ticker_mode(TestDataLoaderFac |
350 | 353 | registry.close(); |
351 | 354 | } |
352 | 355 |
|
| 356 | + @Test |
| 357 | + public void ticker_mode_keeps_one_pending_task_however_often_dispatch_all_is_called() { |
| 358 | + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); |
| 359 | + // a tick that never fires during the test, so the queue is only inspected between ticks |
| 360 | + ScheduledDataLoaderRegistry registry = tickerRegistry(executor, Duration.ofHours(1), 20, alwaysDispatch); |
| 361 | + |
| 362 | + for (int i = 0; i < 50; i++) { |
| 363 | + registry.dispatchAll(); |
| 364 | + } |
| 365 | + |
| 366 | + assertThat(executor.getQueue().size(), equalTo(1)); |
| 367 | + registry.close(); |
| 368 | + executor.shutdownNow(); |
| 369 | + } |
| 370 | + |
| 371 | + @Test |
| 372 | + public void ticker_mode_reschedule_now_keeps_one_pending_task() { |
| 373 | + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); |
| 374 | + ScheduledDataLoaderRegistry registry = tickerRegistry(executor, Duration.ofHours(1), 20, alwaysDispatch); |
| 375 | + registry.dispatchAll(); |
| 376 | + |
| 377 | + for (int i = 0; i < 50; i++) { |
| 378 | + registry.rescheduleNow(); |
| 379 | + } |
| 380 | + |
| 381 | + assertThat(executor.getQueue().size(), equalTo(1)); |
| 382 | + registry.close(); |
| 383 | + executor.shutdownNow(); |
| 384 | + } |
| 385 | + |
| 386 | + @Test |
| 387 | + public void ticker_mode_keeps_ticking_after_a_dispatch_predicate_throws() throws Exception { |
| 388 | + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); |
| 389 | + AtomicBoolean throwOnNextCheck = new AtomicBoolean(); |
| 390 | + DispatchPredicate throwingOnce = (key, dl) -> { |
| 391 | + if (throwOnNextCheck.getAndSet(false)) { |
| 392 | + throw new IllegalStateException("predicate failure"); |
| 393 | + } |
| 394 | + return true; |
| 395 | + }; |
| 396 | + ScheduledDataLoaderRegistry registry = tickerRegistry(executor, Duration.ofMillis(10), 1, throwingOnce); |
| 397 | + registry.dispatchAll(); |
| 398 | + throwOnNextCheck.set(true); |
| 399 | + |
| 400 | + DataLoader<String, String> loader = registry.getDataLoader("loader-0"); |
| 401 | + CompletableFuture<String> late = loader.load("late"); |
| 402 | + |
| 403 | + assertThat(late.get(2, TimeUnit.SECONDS), equalTo("late")); |
| 404 | + registry.close(); |
| 405 | + executor.shutdownNow(); |
| 406 | + } |
| 407 | + |
| 408 | + @Test |
| 409 | + public void close_stops_the_ticker_but_dispatch_all_still_dispatches() throws Exception { |
| 410 | + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); |
| 411 | + ScheduledDataLoaderRegistry registry = tickerRegistry(executor, Duration.ofMillis(10), 1, alwaysDispatch); |
| 412 | + registry.dispatchAll(); |
| 413 | + registry.close(); |
| 414 | + await().atMost(TWO_SECONDS).until(() -> executor.getQueue().isEmpty()); |
| 415 | + |
| 416 | + DataLoader<String, String> loader = registry.getDataLoader("loader-0"); |
| 417 | + CompletableFuture<String> afterClose = loader.load("after-close"); |
| 418 | + registry.dispatchAll(); |
| 419 | + |
| 420 | + assertThat(afterClose.getNow(null), equalTo("after-close")); |
| 421 | + assertThat(executor.getQueue().size(), equalTo(0)); |
| 422 | + executor.shutdownNow(); |
| 423 | + } |
| 424 | + |
| 425 | + private ScheduledDataLoaderRegistry tickerRegistry(ScheduledExecutorService executor, Duration schedule, int loaderCount, DispatchPredicate loaderPredicate) { |
| 426 | + ScheduledDataLoaderRegistry.Builder builder = ScheduledDataLoaderRegistry.newScheduledRegistry() |
| 427 | + .scheduledExecutorService(executor) |
| 428 | + .tickerMode(true) |
| 429 | + .schedule(schedule) |
| 430 | + .dispatchPredicate(neverDispatch); |
| 431 | + for (int i = 0; i < loaderCount; i++) { |
| 432 | + builder.register("loader-" + i, idLoader("loader-" + i), loaderPredicate); |
| 433 | + } |
| 434 | + return builder.build(); |
| 435 | + } |
| 436 | + |
353 | 437 | @Test |
354 | 438 | public void executors_are_shutdown() { |
355 | 439 | ScheduledDataLoaderRegistry registry = ScheduledDataLoaderRegistry.newScheduledRegistry().build(); |
|
0 commit comments