Search code examples
javaspring-bootthreadpoolthreadpoolexecutor

Queue size exceeds from configured max queue capacity in ThreadPoolTaskExecutor used in spring boot


Please Look at this below piece of code:

@Autowired  
private ThreadPoolTaskExecutor executor;  
  
@PostConstruct
public void setupTaskExecutor() {
    this.executor.setCorePoolSize(getExecutorCorePoolSize());
    this.executor.setMaxPoolSize(getExecutorMaxPoolSize());
    this.executor.setQueueCapacity(getExecutorMaxQueueCapacity());
}  
  
public ZoneDetail getZoneDetails(ConfigRequest configRequest) {

    LOGGER.debug("QueueSize = {}", executor.getThreadPoolExecutor().getQueue().size());
    LOGGER.debug("PoolSize = {}", executor.getPoolSize());      

    Future<ZoneDetail> future = executor.submit(() -> {
        return getPrimaryProvider(context).getZoneDetails(context, 
        configRequest);
    });

    try {
        ZoneDetail zoneDetail = future.get(getPrimaryProviderTimeout(),
        TimeUnit.MILLISECONDS);
    } catch (ExecutionException | TimeoutException e) { 
        // fetch data from secondary provider
    }
        
}  

My configured values are
core-pool-size=4
max-pool-size=16
max-queue-capacity=5

I run a PT using SoapUI with following parameters,

Threads: 20  Strategy: Simple Test Delay: 0 Limit: 5 seconds

i.e. I am hitting 20 Threads at a time for 5 seconds.

In the console, I see QueueSize = 15 i.e. my queue size exceeds the configured max queue capacity 5. And PoolSize = 4 i.e. my pool size never exceeds core pool size since extra threads are going into queue.

Note: I am hitting a REST API that calls getZoneDetails() method

How is it happening? What am I doing wrong ?


Solution

    • I don't think your setup is correct. You are autowiring ThreadPoolTaskExecutor and it means it would have been already initialised the ThreadPoolExecutor with QueueCapacity of Integer.MAX_VALUE and created a queue with that capacity. So whatever you do in setupTaskExecutor() is not going to take effect.

    • Instead do the following in one go

        @Bean(name = "myThreadPoolTaskExecutor")
        public Executor threadPoolTaskExecutor() {
            ThreadPoolTaskExecutor threadPoolTaskExecutor = 
                                                 new ThreadPoolTaskExecutor();
            threadPoolTaskExecutor.setCorePoolSize(getExecutorCorePoolSize());
            threadPoolTaskExecutor.setMaxPoolSize(getExecutorMaxPoolSize());
            threadPoolTaskExecutor.setQueueCapacity(getExecutorMaxQueueCapacity());
            threadPoolTaskExecutor.initialize();
            return threadPoolTaskExecutor;
        }
    
    • If you add LOGGER.debug("RemainingCapacity = {}", this.executor.getThreadPoolExecutor().getQueue().remainingCapacity()) as the last line of setupTaskExecutor(), it should confirm this answer

    • And then use it

        @Autowired
        @Qualifier("myThreadPoolTaskExecutor")
        public ThreadPoolTaskExecutor executor;