diff --git a/src/main/java/com/knecon/fforesight/tenantcommons/task/ApplicationTaskExecutorBeanConfig.java b/src/main/java/com/knecon/fforesight/tenantcommons/task/ApplicationTaskExecutorBeanConfig.java new file mode 100644 index 0000000..301dec2 --- /dev/null +++ b/src/main/java/com/knecon/fforesight/tenantcommons/task/ApplicationTaskExecutorBeanConfig.java @@ -0,0 +1,23 @@ +package com.knecon.fforesight.tenantcommons.task; + +import static org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME; +import static org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.task.TaskExecutorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@ConditionalOnClass(ThreadPoolTaskExecutor.class) +@Configuration +public class ApplicationTaskExecutorBeanConfig { + + @Lazy + @Bean(name = {APPLICATION_TASK_EXECUTOR_BEAN_NAME, DEFAULT_TASK_EXECUTOR_BEAN_NAME}) + public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) { + return builder.build(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/knecon/fforesight/tenantcommons/task/TaskExecutionConfiguration.java b/src/main/java/com/knecon/fforesight/tenantcommons/task/TaskExecutionConfiguration.java index 7902c5e..908b6ba 100644 --- a/src/main/java/com/knecon/fforesight/tenantcommons/task/TaskExecutionConfiguration.java +++ b/src/main/java/com/knecon/fforesight/tenantcommons/task/TaskExecutionConfiguration.java @@ -1,11 +1,47 @@ package com.knecon.fforesight.tenantcommons.task; +import java.util.function.Supplier; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.task.TaskExecutorCustomizer; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import org.springframework.scheduling.annotation.EnableAsync; + +import lombok.extern.slf4j.Slf4j; +import lombok.val; @Configuration(proxyBeanMethods = false) @PropertySource("classpath:tenant-commons-task.properties") @ConditionalOnProperty(prefix = "management.tracing", name = "enabled", havingValue = "false") +@EnableAsync +@Slf4j public class TaskExecutionConfiguration { + + private final static Supplier IDENTITY_KNECON_TASK_DECORATOR_SUPPLIER = () -> runna -> runna; + + @Value("${spring.task.execution.pool.warnPercentageThreshold:80}") + private int executorWarnPercentageThreshold; + + @Bean + public TaskExecutorCustomizer taskExecutorCustomizer(ObjectProvider taskDecorator) { + return taskExecutor -> { + taskExecutor.setTaskDecorator((KneconTaskDecorator) runnable -> { + val taskDecoratorUsed = taskDecorator.getIfUnique(IDENTITY_KNECON_TASK_DECORATOR_SUPPLIER); + val decoratedRunnable = taskDecoratorUsed.decorate(runnable); + val actualQueueSize = taskExecutor.getQueueSize() + 1; + val queueOccupancyPercentage = actualQueueSize * 100.f / taskExecutor.getMaxPoolSize(); + + if (queueOccupancyPercentage >= executorWarnPercentageThreshold) { + log.warn("Executor pool [ " + taskExecutor + " ] queue size reached " + actualQueueSize + "/" + taskExecutor.getMaxPoolSize() + + " entries awaiting execution triggering the warn level set for " + executorWarnPercentageThreshold +"% occupancy."); + } + + return decoratedRunnable; + }); + }; + } }