@TimeLimiter times out slow method but does not cancel running future
See original GitHub issueResilience4j version: 1.3.1
Java version: 1.8.0_241
Spring Boot version: 2.2.5.RELEASE
If a method has the @TimeLimiter
annotation added that references a time limiter configured with cancelRunningFuture: true
then, when a timeout event successfully occurs, the currently running CompleteableFuture
does not appear to be cancelled. Instead it just keeps on running.
In the below set up the slow method being called from the protected method just sleeps for 30 seconds and then writes a message to announce it has woken up. This is deliberately being done to try and simulate a very slow/hung method.
application.yml
resilience4j:
instances:
mytimelimiter:
baseConfig: default
timeoutDuration: 5000
cancelRunningFuture: true
thread-pool-bulkhead:
instances:
mybulkhead:
coreThreadPoolSize: 10
maxThreadPoolSize: 10
queueCapacity: 5
SlowService.java
@Service
public class SlowService {
public String slowMethod() {
LOGGER.info("slowMethod going to sleep for 30 seconds...");
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LOGGER.info("...slowMethod awake after 30 seconds");
. . .
return "Successful call to slowMethod";
}
}
MyController.java
@RestController
public class MyController {
. . .
@Autowired
private final SlowService slowService;
. . .
@Bulkhead(name = "mybulkhead", type = Type.THREADPOOL)
@TimeLimiter(name = "mytimelimiter")
@GetMapping("/slow")
public CompletableFuture<String> slowHandler() {
return CompletableFuture.supplyAsync(slowService::slowMethod);
}
The presence of the @TimeLimiter
annotation on the slowHandler() method above ensures that requests to it time out after 5000ms (just as configured for the mytimelimiter instance in the application.yml values). Perfect. However, after another 25 seconds or so the slowMethod() logs that it has woken up and then can presumably carry on going about its work. That seems to indicate that the future was not cancelled when the time out successfully occurred on the calling method.
Is the above working as expected? Perhaps it is the case that resilience4j.timelimiter.instances.{name}.cancelRunningFuture
is not expected to have any effect when the time limiter is referenced from a @TimeLimiter
annotation? Alternatively, maybe my calling method decorated with the annotation needs to be rewritten in order to enable the cancelling of the CompleteableFuture
?
Issue Analytics
- State:
- Created 4 years ago
- Comments:15 (7 by maintainers)
Top GitHub Comments
No, TimeLimiter (TimeLimiterAspect) still throws a TimeoutException. See our Spring Boot 2 demo. But TimeLimiter cannot cancel a CompletableFuture. Otherwise we would have to implement our own CompletableFuture, but that is too cumbersome to maintain.
The TimeLimiter also works for all reactive types from Spring Reactor or RxJava2 and RxJava3
@RobWin I’m trying to understand this, but I struggle with the following:
I tried the @TimeLimiter-Annotation on a method simply returning an object/String/whatever --> I get an error that the TimeLimiter-Annotation needs a future return type --> fine with me
on the other hand you mention, that the @TimeLimiter-Annotation cannot cancel running CompletableFutures and therefore doesn’t work as expected
So what return-type do I need for the @TimeLimiter-Annotation to work as expected (cancel running execution and throw TimeoutException)?
We had a running functional implementation, but we are trying to switch away from this to an annotation-based style. Simply because we want to get rid of the gluecode (decorating suppliers…).