mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-14 03:33:43 +08:00
fix(scheduling): cancel unstarted delayed jobs during shutdown
This commit is contained in:
parent
3843dc04b7
commit
511b689387
@ -41,6 +41,9 @@ public class SchedulingConfig implements SchedulingConfigurer {
|
||||
scheduler.setPoolSize(POOL_SIZE);
|
||||
scheduler.setThreadNamePrefix("sched-");
|
||||
scheduler.setRemoveOnCancelPolicy(true);
|
||||
// Pending future ticks belong to the next application lifetime. Keeping
|
||||
// them queued would consume the grace period and outlive bean teardown.
|
||||
scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
scheduler.setAwaitTerminationSeconds(30);
|
||||
scheduler.setWaitForTasksToCompleteOnShutdown(true);
|
||||
return scheduler;
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
package vip.mate.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SchedulingConfigShutdownTest {
|
||||
private ThreadPoolTaskScheduler scheduler() {
|
||||
var scheduler = (ThreadPoolTaskScheduler) new SchedulingConfig().taskScheduler();
|
||||
scheduler.initialize();
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@Test
|
||||
void futureTaskDoesNotHoldGracefulShutdownOpen() throws Exception {
|
||||
var scheduler = scheduler();
|
||||
var ran = new AtomicBoolean();
|
||||
var pending = scheduler.schedule(() -> ran.set(true), Instant.now().plusSeconds(3600));
|
||||
try (var closer = Executors.newSingleThreadExecutor()) {
|
||||
try {
|
||||
var closed = closer.submit(scheduler::destroy);
|
||||
assertDoesNotThrow(() -> closed.get(2, TimeUnit.SECONDS),
|
||||
"unstarted delayed tasks must not consume the graceful shutdown window");
|
||||
assertTrue(pending.isCancelled());
|
||||
assertFalse(ran.get());
|
||||
} finally {
|
||||
scheduler.getScheduledExecutor().shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void alreadyRunningTaskStillFinishesWithoutInterruption() throws Exception {
|
||||
var scheduler = scheduler();
|
||||
var started = new CountDownLatch(1);
|
||||
var release = new CountDownLatch(1);
|
||||
var interrupted = new AtomicBoolean();
|
||||
var completed = new AtomicBoolean();
|
||||
scheduler.schedule(() -> {
|
||||
started.countDown();
|
||||
try {
|
||||
if (release.await(5, TimeUnit.SECONDS)) completed.set(true);
|
||||
} catch (InterruptedException exception) {
|
||||
interrupted.set(true);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}, Instant.now());
|
||||
try (var closer = Executors.newSingleThreadExecutor()) {
|
||||
try {
|
||||
assertTrue(started.await(2, TimeUnit.SECONDS));
|
||||
var closed = closer.submit(scheduler::destroy);
|
||||
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(2);
|
||||
while (!scheduler.getScheduledExecutor().isShutdown() && System.nanoTime() < deadline) {
|
||||
Thread.sleep(5);
|
||||
}
|
||||
assertTrue(scheduler.getScheduledExecutor().isShutdown());
|
||||
assertFalse(closed.isDone(), "shutdown must wait for the running task");
|
||||
release.countDown();
|
||||
closed.get(2, TimeUnit.SECONDS);
|
||||
assertTrue(completed.get());
|
||||
assertFalse(interrupted.get());
|
||||
} finally {
|
||||
release.countDown();
|
||||
scheduler.getScheduledExecutor().shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user