Search code examples
spring-bootamazon-s3spring-aopspring-transactions

Spring @Transactional doesn't work with third party API call


I am developing Spring-boot application, there is file upload feature from service layer Here is code snippet-

@Transactional(rollbackOn = AmazonServiceException.class)
@Override
public Post createPost(PostEntity postEntity, MultipartFile attachments) {
    uploadOnS3(postEntity.getPostMedia(), attachments); // push into s3 bucket
    .....
    ....
    postRespository.save(postEntity);//store into application databse
}

Problem Scenario:

  1. Lets say file uploaded successfully into s3 bucket and its return file url

  2. exception occurred after next step and transaction has been rolled backed

  3. Since file already has been uploaded, so application wouldn't have any information

What I have been tried so far:

  1. I have tied to insert local db things before s3 bucket upload but I didn't get any callback like local db insertion has been completed now we should start s3 task.

  2. At the the same time I have to also sent notification to client regarding status

Does spring provide any mechanism by which we can know local db insertion has been done ? OR Is there any better approach to handle such type of scenario?


Solution

  • Essentially the third party API can not be made part of spring transaction. You can opt in one of following approaches. All these approaches have their pros and cons thus you'll have to choose depending upon what suits you best -

    1. Use a ThreadLocal to save postEntity object (or any other object having uploaded file information for that matter). In your error handling routine, check for this object and do what ever you want with it. If there's no error then this would have been in DB already. Please make sure to cleanup threadlocal value appropriately to ensure it's usable safely across multiple invocations.
    2. Use a Annotation-Driven Event Async Listener and mark this listener as Transactional. This way the object will be saved in DB using a separate transaction even if there's a failure in main transaction. You may want to do it from within your main flow OR you may opt to do in your error handling routine (say by extracting entity object from threadlocal).
    3. Use Transaction Bound Events and bind it to AFTER_ROLLBACK phase. This event will be triggered ONLY if transaction has been rolled back. This may simplify your use case as this will avoid ThreadLocal type of intermediate storage.