Search code examples
javaspring-bootcompletable-future

What happens to running thread when CompletableFuture throws TimeoutException


I have a spring boot application and i am sending multiple request in async manner.

Here is the TaskExecutor configuration:

@Bean(name = "someWorkExecutor")
    public TaskExecutor afterPlayTriggersExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10));
        executor.setMaxPoolSize(10);
        executor.setThreadNamePrefix("OtherThreadD-");
        executor.setAwaitTerminationMillis(3000);

        executor.initialize();
        return executor;
    }

I have a service for completableFuture:

@Slf4j
@Service
@RequiredArgsConstructor
public class MyAsynService {

    @Async("someWorkExecutor")
    public CompletableFuture<MyApiResponse> asyncCall(...) {
        return CompletableFuture.completedFuture(callFeignClient(..., ...));
    }
}

Right now this service is consumed by clientService and handle the timeout exception. My question is that when there is timeout exception what happens to the workers in which is trying to send rest call. It will stop the call immediately or keep trying to work?


@Slf4j
@Service()
public class ClientService {

 
    // ....
    public ClientResponse execute(....) {
        int atLeast = 500; // send the same request at least 500 times
        CompletableFuture<MyApiResponse>[] asyncCallResponses = new CompletableFuture[atLeast];
        for (int index = 0; index < atLeast; index ++) {
            asyncCallResponses[index] = myAsynService.asyncCall();
        }

        final List<OtherResponseType> dump = getResponseAsyncThreads(asyncCallResponses...);
        // ...       
    }



    private List<OtherResponseType> getResponseAsyncThreads(CompletableFuture<MyApiResponse>[] asyncCallResponses) {
        try {
            CompletableFuture.allOf(asyncCallResponses).get(5000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException ex) {
            // even if there is exception, others workers in the someWorkExecutor will continue to its work ??
        } 

        // ....
      
    }

}

Solution

  • One of the biggest issues with CompletableFuture is that it does not stop the task if a call to get times out.

    Structured Concurrency may be usable here with its joinUntil method, but I haven't read whether it will also interrupt tasks on timeout. You can try it, but unfortunately Structured Concurrency will go into its 3rd preview in Java 23; it won't be available as non-preview feature until at least Java 24.

    If you really need to be able to cancel jobs, I think the only option is to replace CompletableFuture with Future, with AsyncResult used as implementation in your method. That means you can't use CompletableFuture.allOf but instead will have to do more manual work.