diff --git a/spring-core-lab/src/main/java/pl/mperor/lab/spring/AsyncService.java b/spring-core-lab/src/main/java/pl/mperor/lab/spring/AsyncService.java new file mode 100644 index 0000000..ee832cf --- /dev/null +++ b/spring-core-lab/src/main/java/pl/mperor/lab/spring/AsyncService.java @@ -0,0 +1,36 @@ +package pl.mperor.lab.spring; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +@Service +public class AsyncService { + + private static final Logger log = LoggerFactory.getLogger(AsyncService.class); + + public CompletableFuture delayedMethod(Duration duration, Executor executor) { + return CompletableFuture.supplyAsync(() -> task(duration), executor); + } + + @Async + public CompletableFuture delayedMethod(Duration duration) { + return CompletableFuture.completedFuture(task(duration)); + } + + private static String task(Duration duration) { + try { + var threadName = Thread.currentThread().getName(); + log.info("Time to sleep 😴 [{}] ...", threadName); + Thread.sleep(duration); + return threadName; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/spring-core-lab/src/main/resources/application.yaml b/spring-core-lab/src/main/resources/application.yaml new file mode 100644 index 0000000..682ecf3 --- /dev/null +++ b/spring-core-lab/src/main/resources/application.yaml @@ -0,0 +1,8 @@ +#spring: # Spring ThreadPoolTaskExecutor customization +# task: +# execution: +# pool: # Doesn't have an effect if virtual threads are enabled. +# core-size: 5 # (default = 8) +# max-size: 10 # Ignored if the queue is unbounded. +# queue-capacity: 100 +# thread-name-prefix: async- \ No newline at end of file diff --git a/spring-core-lab/src/test/java/pl/mperor/lab/spring/SpringAsyncConfigTest.java b/spring-core-lab/src/test/java/pl/mperor/lab/spring/SpringAsyncConfigTest.java new file mode 100644 index 0000000..bce248e --- /dev/null +++ b/spring-core-lab/src/test/java/pl/mperor/lab/spring/SpringAsyncConfigTest.java @@ -0,0 +1,59 @@ +package pl.mperor.lab.spring; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; + +@EnableAsync +@SpringBootTest +class SpringAsyncConfigTest { + + @Autowired + private ThreadPoolTaskExecutor executor; + @Autowired + private AsyncService asyncService; + + @Test + void shouldHaveDefaultExecutorConfiguration() { + assertThat(executor).isNotNull(); + assertThat(executor.getCorePoolSize()).isEqualTo(8); + assertThat(executor.getMaxPoolSize()).isEqualTo(Integer.MAX_VALUE); + assertThat(executor.getQueueCapacity()).isEqualTo(Integer.MAX_VALUE); + assertThat(executor.getKeepAliveSeconds()).isEqualTo(60); + assertThat(executor.getThreadNamePrefix()).isEqualTo("task-"); + } + + @Test + void shouldExecuteTasksInParallel() { + long start = System.currentTimeMillis(); + + var completableFutures = IntStream.range(0, 4) + .mapToObj(_ -> asyncService.delayedMethod(Duration.ofMillis(200))) + .toList(); + var tasks = completableFutures.stream() + .map(CompletableFuture::join) + .toList(); + + long duration = System.currentTimeMillis() - start; + + // 4 x 200ms sequentially = ~800ms + // In parallel = ~200ms, so we assert under 400ms + assertThat(duration).isLessThan(400); + assertThat(tasks).allMatch(task -> task.startsWith("task-")); + } + + @Test + void shouldToRunAsynchronouslyWithoutAsyncAnnotation() { + var done = asyncService.delayedMethod(Duration.ofMillis(10), executor) + .join(); + assertThat(done).startsWith("task-"); + } +} \ No newline at end of file