Reputation: 5487
I have an @Async task executing some database related background work. After enabling virtual threads (spring.threads.virtual.enabled: true
) the number of Async jobs executed in parallel is no longer limited. The result is an exhausted database connection pool. When virtual threads is disabled only 8 (default) jobs are executed in parallel so less database connections are used.
What would be the recommended approach to solve this problem?
org.apache.tomcat.jdbc.pool.PoolExhaustedException: [task-49889] Timeout: Pool empty. Unable to fetch a connection in 30 seconds, none available[size:100; busy:100; idle:0; lastwait:30000]
@Component
@RequiredArgsConstructor
public class TestTask {
private final MyRepository myRepository;
@Async
public void execute() {
// some database calls.
myRepository.doSomething();
}
}
When executing 200 tasks the pool is exhausted.
IntStream.range(1, 200).forEach(value -> testTask.execute());
I'm using Java 21 with Spring Boot 3.2.1.
Upvotes: 6
Views: 3274
Reputation: 124441
You can configure concurrency in this case through the spring.task.execution.simple.concurrency-limit
property. Default it isn't set and thus means unbounded.
Setting it in your application properties.
spring.task.execution.simple.concurrency-limit=8
This will limit it to 8 concurrent tasks (or the number you choose it to be). The auto-configuration takes care of this.
Upvotes: 6
Reputation: 4896
According to documentation about Virtual Threads you have to use Semaphore to Limit Concurrency :
Sometimes there is a need to limit the concurrency of a certain operation. For example, some external service may not be able to handle more than ten concurrent requests. Because platform threads are a precious resource that is usually managed in a pool, thread pools have become so ubiquitious that they're used for this purpose of restricting concurrency, like in the following example:
As an example something like next one approach:
Semaphore sem = new Semaphore(10);
...
Result foo() {
sem.acquire();
try {
return callLimitedService();
} finally {
sem.release();
}
}
Because When using virtual threads, if you want to limit the concurrency of accessing some service, you should use a construct designed specifically for that purpose: the Semaphore class.
Upvotes: 3