Spring Boot #Async method *sometimes* not invoked - spring

My Spring Boot application provides the following REST controller which invokes two methods of a Service. One of these methods is annotated with #Async so it should run asynchronously.
The main application class is annotated with #EnableAsync.
The problem I observed is: Basically, the async method is executed. I can see the corresponding log entries in the production system. But it seems as if sometimes the async method does not get invoked. There are file ids in the database which do not appear in the logs.
Do you have any idea when this behavior could occur?
REST controller
#PostMapping(consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> uploadDocument(#RequestParam("file") MultipartFile multipartFile) {
long fileId = fileService.save(multipartFile);
file.generateThumbnail(fileId);
return ResponseEntity.ok().build();
}
FileService
#Transactional
public long save(MultipartFile multipartFile) {
// saves the file...
return fileId;
}
#Async
#Transactional
public void generateThumbnail(long fileId) {
// generate thumbnail
log.info("Starting thumbnail generation for fileId {}", fileId);
file.setThumbnailId(thumbnailId);
}

Calling a async method from FileService does not work as async. You must create another Service for async method and call it from your service.
Reason:
self-invocation – calling the async method from within the same class
– won't work the method needs to be public so that it can be proxied.
And self-invocation doesn't work because it bypasses the proxy and
calls the underlying method directly.
Resources: https://www.baeldung.com/spring-async

Related

Need to understand asynchronous usage of Spring WebClient

I have a doubt regarding the usage of webclient in cirumstances when you need to invoke another service which is slow in responding and then use its data to process something and return in the call to your own api.
e.g. my doSometing method is called by service to retrive top order from a list.My service fetches the list from another service "order-service"
#RestController
public class TestController {
#Autowired
private TestService testService;
#GetMapping("/test")
public String doSomething() {
return WebClient.create()
.get()
.uri("http://localhost:9090/order-service")
.retrieve()
.bodyToMono(List.class)
.block().get(0).toString();
//may be do more processing on the list later.
}
As you can see , it currently blocks the calling thread which beats the purpose of async.Also , in future i might need to do more processing on this List before returning.
Am i using webclient correctly(is it serving any purpose here)?

#Async is not working if I give it at service level in spring-boot

I am autowiring service in controller. And in service, I have a scenario where I need to throw an exception and DB changes also. So, I tried #Async
#Transactional
public void verifyOtp(OtpDto otpDto)
...
if(xyz){
deactivateOtp(emp.getId());
throw new ServException("Mobile no requested is already assigned", "error-code");
}
}
#Async
#Transactional //with or without
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
This is not creating new thread. But, if I gives at repo, it works
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Async
#Transactional
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
First of all check that the service is wrapped into proxy (you can place a breakpoint in controller and see the reference to the service, it will be with proxy). Otherwise there is something wrong with the configuration and #Transactional/#Async won't work till you fix that.
Now, assuming this is not an issue, there is an issue in the code:
When the controller calls service.verifyOtp it goes to the proxy (to handle the transaction) and then to your implementation.
But when it reaches your implementation and you call the method that belongs to the same impl, it doesn't pass through the proxy again, instead it directly goes to the deactivateOtp as if there is no spring at all here. Of course, #Async doesn't work.
In terms of resolution:
Consider using self injection if you work with spring 4.3+. Read this thread for more information.
Alternatively, refactor your code so that the deactivateOtp will be a public method of another class. In this case the call won't be "internal" anymore, it will path through Proxy hence the #Async will work.
This is discussed many times.
Basically, Spring AOP will not intercept local method call
(in your case call deactivateOtp() within the same class)
You can read more about this behavior here: Understanding AOP proxies
Highlight:
The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.

Spring #Async annotation

I have a question about Spring #Async annotation.
I have a controller autowired a service(GnInsuranceDetailsService)
#RequestMapping(value="/agn/AP0W01A_010/insertDetail.do")
public ResponseEntity<?> insertDetail(#ModelAttribute("dto")GnInsuranceDetailsDTO dto,HttpSession session) throws Exception {
gnInsuranceDetailsDTO.setBkFlag(getAgnUserInfo(session).getBkFlag());
gnInsuranceDetailsService.insertGnInsuranceDetail(dto);//open another thread to insert data
MessageDTO dto = new MessageDTO(AgnConstant.INSERT_SUCCESS);
return new ResponseEntity<MessageDTO>(dto,HttpStatus.OK);// increase return time for client
}
And the Service insertGnInsuranceDetail method I declare #Async up method.
#Transactional(readOnly = false)
#Async
public void insertGnInsuranceDetail(GnInsuranceDetailsDTO gnInsuranceDetailsDTO) throws Exception{
GnInsuranceDetails entity = gnInsuranceDetailsDTO.convert();
gnInsuranceDetailsDAO.save(detailsEntity);
}
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
Do I lose somethings?Or How can I modify in the easiest way to use?
You would not loose anything, when you put #Async in the method service will be executed in a different thread, Controllers insertDetail method will not be returned until your insertGnInsuranceDetail is returned or thrown any exception.
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
#Async - Annotation that marks a method as a candidate for asynchronous execution. Can also be used at the type level, in which case all of the type's methods are considered as asynchronous.
This #Async annotation will not help you in delaying the response time. To introduce a delay, use Thread.sleep(milliseconds);
If you want main thread(controller) to wait for insert data thread to get a result (successful/failure) you can just invoke db layer code from the controller. Whereas if you want the client response to be sent earlier, then create a new thread inside the controller and use that thread to insert data. In the latter approach of creating a new thread for insert data your client will not know about the status of data insertion, since the thread is created in the controller and it will end in the controller, no feedback/response can be given to the client, since we will not be having client details.
Hope it Helps!
Reference: https://www.tutorialspoint.com/java/lang/thread_sleep_millis.htm
#Async should be first enabled in your #Configuration bean:
#Configuration
#EnableAsync
public class AppConfiguration {
[...]
}
for xml configuration add this: <task:annotation-driven>

Spring nested transactions

In my Spring Boot project I have implemented following service method:
#Transactional
public boolean validateBoard(Board board) {
boolean result = false;
if (inProgress(board)) {
if (!canPlayWithCurrentBoard(board)) {
update(board, new Date(), Board.AFK);
throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED);
}
if (!canSelectCards(board)) {
update(board, new Date(), Board.COMPLETED);
throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED);
}
result = true;
}
return result;
}
Inside this method I use another service method which is called update:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Board update(Board board, Date finishedDate, Integer status) {
board.setStatus(status);
board.setFinishedDate(finishedDate);
return boardRepository.save(board);
}
I need to commit changes to database in update method independently of the owner transaction which is started in validateBoard method. Right now any changes is rolling back in case of any exception.
Even with #Transactional(propagation = Propagation.REQUIRES_NEW) it doesn't work.
How to correctly do this with Spring and allow nested transactions ?
This documentation covers your problem - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct.
However, there is an option to switch to AspectJ mode
Using "self" inject pattern you can resolve this issue.
sample code like below:
#Service #Transactional
public class YourService {
//... your member
#Autowired
private YourService self; //inject proxy as an instance member variable ;
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void methodFoo() {
//...
}
public void methodBar() {
//call self.methodFoo() rather than this.methodFoo()
self.methodFoo();
}
}
The point is using "self" rather than "this".
The basic thumb rule in terms of nested Transactions is that they are completely dependent on the underlying database, i.e. support for Nested Transactions and their handling is database dependent and varies with it.
In some databases, changes made by the nested transaction are not seen by the 'host' transaction until the nested transaction is committed. This can be achieved using Transaction isolation in #Transactional (isolation = "")
You need to identify the place in your code from where an exception is thrown, i.e. from the parent method: "validateBoard" or from the child method: "update".
Your code snippet shows that you are explicitly throwing the exceptions.
YOU MUST KNOW::
In its default configuration, Spring Framework’s transaction
infrastructure code only marks a transaction for rollback in the case
of runtime, unchecked exceptions; that is when the thrown exception is
an instance or subclass of RuntimeException.
But #Transactional never rolls back a transaction for any checked exception.
Thus, Spring allows you to define
Exception for which transaction should be rollbacked
Exception for which transaction shouldn't be rollbacked
Try annotating your child method: update with #Transactional(no-rollback-for="ExceptionName") or your parent method.
Your transaction annotation in update method will not be regarded by Spring transaction infrastructure if called from some method of same class. For more understanding on how Spring transaction infrastructure works please refer to this.
Your problem is a method's call from another method inside the same proxy.It's self-invocation.
In your case, you can easily fix it without moving a method inside another service (why do you need to create another service just for moving some method from one service to another just for avoid self-invocation?), just to call the second method not directly from current class, but from spring container. In this case you call proxy second method with transaction not with self-invocatio.
This principle is useful for any proxy-object when you need self-invocation, not only a transactional proxy.
#Service
class SomeService ..... {
-->> #Autorired
-->> private ApplicationContext context;
-->> //or with implementing ApplicationContextAware
#Transactional(any propagation , it's not important in this case)
public boolean methodOne(SomeObject object) {
.......
-->> here you get a proxy from context and call a method from this proxy
-->>context.getBean(SomeService.class).
methodTwo(object);
......
}
#Transactional(any propagation , it's not important in this case)public boolean
methodTwo(SomeObject object) {
.......
}
}
when you do call context.getBean(SomeService.class).methodTwo(object); container returns proxy object and on this proxy you can call methodTwo(...) with transaction.
You could create a new service (CustomTransactionalService) that will run your code in a new transaction :
#Service
public class CustomTransactionalService {
#Transactional(propagation= Propagation.REQUIRES_NEW)
public <U> U runInNewTransaction(final Supplier<U> supplier) {
return supplier.get();
}
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void runInNewTransaction(final Runnable runnable) {
runnable.run();
}
}
And then :
#Service
public class YourService {
#Autowired
private CustomTransactionalService customTransactionalService;
#Transactional
public boolean validateBoard(Board board) {
// ...
}
public Board update(Board board, Date finishedDate, Integer status) {
this.customTransactionalService.runInNewTransaction(() -> {
// ...
});
}
}

Spring Controller - forking request, return value before long run function ends

I have controller and long run function in it, like:
#Controller
#RequestMapping("/deposit")
public class DepositController {
#RequestMapping
public ModelAndView getNewJob(long userId, Model model) {
//execute function that can runs a lot of time ...
longRunFunction();
return new ModelAndView("jobTasks");
}
public void longRunFunction(){
// process long run function
}
}
My question is :
How can I execute the longRunFunction()
and return ModelAndView("jobTasks") answer to the browser without waiting for the end of the function?
Thank you !
Hi, I found nice example here http://krams915.blogspot.co.il/2011/01/spring-3-task-scheduling-via.html
This can be done using Asynch support in Spring Framework, essentially delegate the long running task to another service, the method of which is annotated with #Async annotation, this task would then be executed by a threadpool and control will return back to your caller immediately.
Here is much more detailed reference: http://docs.spring.io/spring-framework/docs/3.2.3.RELEASE/spring-framework-reference/html/scheduling.html#scheduling-annotation-support-async
public class SampleBeanImpl implements SampleBean {
#Async
void longRunFunction() { … }
}
Add #Async to the method declaration of longRunningMethod. But to make this work without AspectJ weaving you need to put this method in an other bean.

Resources