Is CompletableFuture is must use return type for AAsync spring boot annotation? - spring-boot

I want to Use #Async annotation on method for running it asynchronously. I have defined my ThreadExecutor as follows:
#Bean("threadPoolTaskExecutor")
public TaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(200);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
executor.setThreadNamePrefix("async-");
executor.setQueueCapacity(50);
executor.initialize();
return executor;
}
My question is, is it mandatory to use CompletableFuture as return type for the method using #Async annotation? Will it work if my third party REST calls returns different/Custom types? for e.g.
#Async("threadPoolTaskExecutor")
public ResponseDTO getCapa(final List<String> vins) {
for (String vin : vins) {
CapabilityDTO capabilityDTO = new CapabilityDTO();
// call third party
Optional<ResponseDTO> response=thirdPartyClient.getInfo();
..........
return response.get();
}
}
or I must use CompletableFuture<ResponseDTO> ??

As per the Spring Async javadoc:
the return type is constrained to either void or Future. In the latter case, you may declare the more specific ListenableFuture or CompletableFuture types which allow for richer interaction with the asynchronous task and for immediate composition with further processing steps.
In practice, I believe that if you declare another type, the caller will receive a null value since no value will be available at the moment of the call and it has to return immediately, so the caller won’t be able to retrieve the result.
If you want to be able to access the result, you must thus return a Future containing it. Easiest is probably to just use
return CompletableFuture.completedFuture(response.get())
Spring Async will then take care of forwarding your result to the caller when your method returns.

Related

Using Quarkus Cache with Reactive and Mutiny correctly

I'm trying to migrate my project to Quarkus Reactive with Hibernate Reactive Panache and I'm not sure how to deal with caching.
My original method looked like this
#Transactional
#CacheResult(cacheName = "subject-cache")
public Subject getSubject(#CacheKey String subjectId) throws Exception {
return subjectRepository.findByIdentifier(subjectId);
}
The Subject is loaded from the cache, if available, by the cache key "subjectId".
Migrating to Mutiny would look like this
#CacheResult(cacheName = "subject-cache")
public Uni<Subject> getSubject(#CacheKey String subjectId) {
return subjectRepository.findByIdentifier(subjectId);
}
However, it can't be right to store the Uni object in the cache.
There is also the option to inject the cache as a bean, however, the fallback function does not support to return an Uni:
#Inject
#CacheName("subject-cache")
Cache cache;
//does not work, cache.get function requires return type Subject, not Uni<Subject>
public Uni<Subject> getSubject(String subjectId) {
return cache.get(subjectId, s -> subjectRepository.findByIdentifier(subjectId));
}
//This works, needs blocking call to repo, to return response wrapped in new Uni
public Uni<Subject> getSubject(String subjectId) {
return cache.get(subjectId, s -> subjectRepository.findByIdentifier(subjectId).await().indefinitely());
}
Can the #CacheResult annotations be used with Uni / Multi and everything is handled under the hood correctly?
Your example with a #CacheResult on a method that returns Uni should actually work. The implementation will automatically "strip" the Uni type and only store the Subject in the cache.
The problem with caching Unis is that depending on how this Uni is created, multiple subscriptions can trigger some code multiple times. To avoid this you have to memoize the Uni like this:
#CacheResult(cacheName = "subject-cache")
public Uni<Subject> getSubject(#CacheKey String subjectId) {
return subjectRepository.findByIdentifier(subjectId)
.memoize().indefinitely();
}
This will ensure that every subscription to the cached Uni will always return the same value (item or failure) without re-executing anything of the original Uni flow.

Mono response from a method which returns void

I have a service method which does not result anything, but can return an HttpException.
example
class Service{
public void myService() throws HttpException{
//do something
}
}
My calling class has a method which is supposed to return a Mono. This method calls myService().
class Caller{
#Autowire
Service service;
public Mono<Response> callMyService(){
return Mono.just("abc")
.doOnSuccess(service.myService())
.thenReturn(new Response()); //this should return Mono<Response>
}
}
My question, is how can I write callMyService() in a good way? Mono.just("abc") doesn't seem right implementation.
You should use Mono<Void> for this purpose. This mono will not forward any data, it will only signal error or completion.
You can create it using then()
Also, remember that doOnSuccess() is side effect. You should not use it for data processing, maybe use map() or flatMap(). For you case, maybe you can use Mono.fromCallable(()->service.myService()), but that may not be correct depending on what service actually does.

spring boot how to handle fault tolerance in async method?

Suppose I have a caller to distribute work to multiple async tasks:
public class Caller{
public boolean run() {
for (int i = 0: i< 100; i++) {
worker.asyncFindOrCreate(entites[i]);
}
return true;
}
public class Worker{
#Autowired
Dao dao;
#Async
public E asyncFindOrCreate(User entity) {
return dao.findByName(entity.getName).elseGet(() -> dao.save(entity));
}
}
If we have 2 same entities:
with the synchronized method, the first one will be created and then the second one will be retrieved from the existing entity;
with async, the second entities might pass the findByName and go to save because the first entity hasn't been saved yet, which cause the save of the second entity throws unique identifier error.
Is there a way to add some fault tolerance mechanic to have some features like retry and skipAfterRetry, in particular for database operations.
In this special case you should convert your array to a map. Use the name property as a key, so there will be no duplicated entries.
However, if this method also can be called by multiple threads (ie. it's in a web-server) or there are multiple instances running it's still not fail-safe.
In generic, you should let the DB to check the uniqueness. There is no safest/easiest way to do that. Put the save method inside a try-catch block and check/handle the unique identifier exception.

How to pass data from business method to fallback method while using spring-boot circuit breaker (Hystrix)?

In official https://spring.io/guides/gs/circuit-breaker/ manual there are
business method (readingList)
fallback method (reliable)
#HystrixCommand(fallbackMethod = "reliable")
public String readingList() {
URI uri = URI.create("http://localhost:8090/recommended");
return this.restTemplate.getForObject(uri, String.class);
}
public String reliable() {
return "Cloud Native Java (O'Reilly)";
}
How to pass data from business method to fallback method? Use ThreadLocal, immutable collections, concurrent collections, any ideas/best practice?
Use ThreadLocal?
#HystrixCommand and the corresponding fallbackMethod are normally executed (together) in a separate thread because of the default execution.isolation.strategy which is ExecutionIsolationStrategy.THREAD.
So that means that if you use ThreadLocal to set any variables before #HystrixCommand is executed, they won't be available to the #HystrixCommand because the thread will be different.
If the above is necessary you can use a different isolation strategy - ExecutionIsolationStrategy.SEMAPHORE.
To override the default isolation strategy you can do it on the hystrix command definition (or in properties files):
#HystrixCommand(fallbackMethod = "reliable",
commandProperties = {
#HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
}
)
Passing input parameters data
Methods annotated with #HystrixCommand and the corresponding fallbackMethod need to have the same method signature (plus optional param in the fallback for exceptions thrown), so naturally the fallback method will have access to all input parameters to the #HystrixCommand.
Passing exceptions data
Adding Throwable in the fallback method signature will include the exception produced from the #HystrixCommand:
public String reliable(Throwable t) {
return "Cloud Native Java (O'Reilly)";
}
Passing execution data
It's not practical to expect any execution data to be passed from main method to the fallback method. You don't know when the main method will fail.
A key thing is to try and define better input parameters, which will be shared with the fallback anyway.
For example in the code that you've given the URL can become input parameter, so it will be available to the fallback method as well:
#HystrixCommand(fallbackMethod = "reliable")
public String readingList(String url) {
URI uri = URI.create(url);
return this.restTemplate.getForObject(uri, String.class);
}
public String reliable(String url, Throwable t) {
return "Cloud Native Java (O'Reilly)";
}

#Async in spring mvc working without a Future return type?

Sometimes when I am using #Async without return type of Future it returns null but sometimes it returns String. In documentation it is mentioned that return type of Future is must. I am confused why this is happening?
e.g.this one is working
#Service
public class MyClass implements MyClass2{
#Async
#Override
public String getString() {
return "hello";
}
}
Please don't return a String from an #Async method. This is against the intentions of the makers. Excerpt from the Spring Execution & Scheduling docu:
Even methods that return a value can be invoked asynchronously. However, such methods are required to have a Future typed return value. This still provides the benefit of asynchronous execution so that the caller can perform other tasks prior to calling get() on that Future.
If your asynchronous method has a return type, it MUST be a Future object!
Why? Because otherwise the method invoker won't be able to wait for the completion of the Thread that has handled the #Async method. The waiting for Thread-completion is handled by Future.get

Resources