Is #Recovery method necessary when using Spring-Retry? - spring

My question is regarding spring-retry.
Assume a simple sample code where I have a the Service layer and Controller class.,
This is the testService Interface
public interface testService{
#Retryable(value = { KnownExceptiomn.class }, backoff = #Backoff(delay = 1000), maxAttempts = 2)
Address getAddress(String emailAddress);
}
This is the implementation of the service
public class testServiceImpl{
public Address getAddress(String emailAddress){
//addressRepository is a crud repository
return addressRepository.getAddressFromEmail(emailAddress);
}
}
And the controller is
#GetMapping("path/{emailId}")
public ResponseEntity<?> getAddress(#PathVariable("emailId") final String email){
final Address address;
try{
address= testService.getAddress(String emailAddress);
if(address != null) return new ResponseEntity<Address>(address,HttpStatus.OK);
return new ResponseEntity<String>("Invalid Email", HttpStatus.BAD_REQUEST);
}catch(KnownException e){
return errorMessage("Error");
}
}
As seen the #Retryble is in the Service interface. However I have not implemented an #Recover method. My thought here was since i dont really have any secondary DB and If the DB is down there really isn't a recovery option I did not add a #Recovery method. Instead the exception was caught in the controller and handled.
My questions are:
Is the above approach wrong. If so, how to do it the right way?
Is it always necessary to have a recovery method? If so what would be recovery in these kind of scenarios like DB being down and no alternative source of Data.
Is it wrong to catch exceptions in controller and handle
them accordingly? (I was told the service lay should handle all the
exceptions in some discussions).
Everywhere I've see is some sort of recovery method but couldn't find a solid example with proper recovery handler for this type of scenario if there is.

#Recovery is optional; if there is none, after retries are exhausted, the last exception is thrown to the caller, who has to handle the exception.
It's perfectly normal to not have a #Recovery and handle the exception in the caller, by whatever means you like.

#Vipin Menon: I think you are referring to #Recover annotation, so the answer is No. #Recover annotation is provided just to give the programmer flexibility to create a recovery method to return a default (fallback) response if retry attempts also fails.
In a nutshell, don't create a recovery method using #Recover annotation if you don't need any default behavior when retry attempts fail. Simply use #Retryable annotation on the actual service method.

Related

How to track the result of transaction in spring?

You can imagine I have some service, say it will be money service. Also assume I have one method, that perform actual transfer (Quite mundane example, I know). And I have to return true if transaction ended up successfully, and false, if it is not. So, here is the think that I do not actually grasp - how do I track the result of transaction in Spring Framework? (May be even for just simple logging purposes) Example of my transfer method is present below. Appreciate any help.
#Transactional
public boolean transferMoneyFromOneAccountToAnother(MoneyTransferForm moneyTransferForm) {
final UserBankAccount sourceBankAccount = bankAccountRepository.findBankAccountByIdentifier(
moneyTransferForm.getSourceAccountIdentifier()
);
final UserBankAccount targetBankAccount = bankAccountRepository.findBankAccountByIdentifier(
moneyTransferForm.getTargetAccountIdentifier()
);
subtractMoneyFromSourceAccount(moneyTransferForm, sourceBankAccount);
appendMoneyToTargetAccount(moneyTransferForm, targetBankAccount);
bankAccountRepository.updateUserBankAccount(sourceBankAccount);
bankAccountRepository.updateUserBankAccount(targetBankAccount);
}
I can think of two ways to do it:
You can simply enclose your method call with try/catch block and if there are no exception then your transaction was committed successfully.
try{
transferMoneyFromOneAccountToAnother()
logger.info("Transacton Done Successfully");
}catch(Exception ex){
//transaction failed
logger.error("Transaction failed")
}
You can have a method which is annotated with #TransactionalEventListener and listening to your custom event. You can check these links for more understanding of how it works:
https://www.baeldung.com/spring-events
#TransactionalEventListener annotated method not invoked in #Transactional test

How to propagate JTA state when using reactive-messaging?

I would like to propagate JTA state (= the transaction) between a transactional REST endpoint that emits a message to a reactive-messaging connector.
#Inject
#Channel("test")
Emitter<String> emitter;
#POST
#Transactional
public Response test() {
emitter.send("test");
}
and
#ApplicationScoped
#Connector("test")
public class TestConnector implements OutgoingConnectorFactory {
#Inject
TransactionManager tm;
#Override
public SubscriberBuilder<? extends Message<?>, Void> getSubscriberBuilder(Config config) {
return ReactiveStreams.<Message<?>>builder()
.flatMapCompletionStage(message -> {
tm.getTransaction(); // = null
return message.ack();
})
.ignore();
}
}
As I understand, context-propagation is responsible for making the transaction available (see io.smallrye.context.jta.context.propagation.JtaContextProvider#currentContext). The problem seems to be, that currentContext gets created on subscription, which happens when the injection point (Emitter<String> emitter) get its instance. Which is too early to properly capture the transaction.
What am I missing?
By the way, I am having the same problem when using #Incoming / #Outgoing instead of the emitter. I have decided to give you this example because it is easy to understand and reproduce.
At the moment, you need to pass the current Transaction in the message metadata. Thus, it will be propagated to your different downstream components (as well as the connector).
Note that, Transaction tends to be attached to the request scope, which means that in your connector, it may already be too late to use it. So, make sure your endpoint is asynchronous and only returns when the emitted message is acknowledged.
Context Propagation is not going to help in this case as the underlying streams are built at startup time (at build time in Quarkus) so, there are no capture contexts.

Spring Transactional on method with multiple db operation

I'm little confused. I though #Transactional on method means all operations or none.
Say I have this method:
#Transactional
public void fewDbOpeations(){
calculation1();
myDao.saveResult();
calculation2();
myDao.saveResult();
}
Say calculation2() throw exception or my second call to myDao.saveResult goes wrong , what I see is the even though the whole method annotated with #Transactional the saving result after calculation1() call is successful.
That is my first interaction with database saved the records I want but the second one failed but I thought because the method is #Transactinal even the first call to save to database should be rolled back.
Do I miss something?
#Transactional (rollbackFor = Exception.class)
public void fewDbOpeations(){
calculation1();
myDao.saveResult();
calculation2();
myDao.saveResult();
}
Try using this as well and throw Exceptions.
It depends on how you handle exceptions and if there are still #Transactional annotated on those internal method calls.
To have "all or nothing" behaviour in fewDbOpeations(), make sure the followings for all the internal method calls :
Do not annotated with #Transactional(propagation=REQUIRES_NEW)
Do not catch the exception inside and not throw out. Throw RuntimeException or Error but not checked Exception (Assume you are using default setting).

Transaction rollback and save info

In the service layer, I have some method who have a transactional annotation.
#Transactional
public void process() throws ProcessPaymentException{
try{
.... do some operation
catch (ProcessPaymentException ppe) {
save db problem issue.
}
}
It seem like if there are a issue, there are roll back... and nothing is saved in the db...
ProcessPaymentException extend Exception
Is there a way to rollback the process in the try but do the save in the catch?
Edit
Nested transaction could be a solution if this link is ok
https://www.credera.com/blog/technology-insights/java/common-oversights-utilizing-nested-transactions-spring/
Existing answer of using ControllerAdvise should help in normal setup that incoming requests are coming through Spring MVC (i.e. through a Controller).
For cases that is not, or you do not want to tie your exception handling logic to Spring MVC, here are some alternatives I can think of
(Here I assume you want to rely on declarative transaction control instead of programmatically controlling transactions yourself)
Separate service/component to save error in different transaction.
In short, you can have a separate service, which create its own transaction by propagation REQUIRES_NEW. e.g.
#Service
public class FooService
#Inject
private ErrorAuditService errorAuditService;
#Transactional
public void process() throws ProcessPaymentException{
try{
.... do some operation
catch (ProcessPaymentException ppe) {
errorAuditService.saveErrorAudit(ppe.getErrorText());
throw ppe; // I guess you want to re-throw the exception
}
}
}
#Service
public class ErrorAuditService
#Transactional(propagation=REQUIRES_NEW)
public void saveErrorAudit() {
// save to DB
}
}
One step further, if the error handling it the same for different services, you may create an advise, which will be called when service method throws exception. In that advise, you can save the error in db (using ErrorAuditService), and rethrow the exception.
Because processes of try-catch are wrapped by the same transaction.
The transaction manager do rollback whenever an exception is thrown. So, not thing would be saved.
Is there a way to rollback the process in the try but do the save in the catch?
Yes. Create Exception Handler to save db problem issue after rollback.
this is the idea
#ControllerAdvice
public class HandlerName {
#ExceptionHandler(ProcessPaymentException.class)
public void saveDbIssue(ProcessPaymentException ex) {
// save db problem issue.
}
But it only works if u want to save static data.

Do I have to try-catch JpaRepository

I am using JpaRepository from Spring Data JPA framework. I have a snippet of code below:
#Repository
public interface PresetFolderRepository extends JpaRepository<PresetFolder, Integer>{
#Modifying
#Transactional
#Query("update PresetFolder pf set pf.parentId = :parentId where pf.id = :id")
int updateParentId(#Param("id") int id, #Param("parentId") int parentId);
}
When I invoke this method:
#Autowired PresetFolderRepository repo;
repo.updateParentId(1,2);
public void test(){
Do I have to surround it with a try-catch? How can I know if the self-defined method 'updateParentId' has try-catch implementation in it?
Thanks!
EDIT:
My concern is, if my database went down, does this method catch the exception.
Repositories will always tell you something if a problem happens (i.e. they never swallow exceptions). You'll always get a runtime exception if that's the case.
And you should probably not catch such an exception either, except at the very top of the call stack, where you have the possibility to display an error message to the end user.
No you don't need it to surround by try-catch block. Most of the Spring-Data repositories throw runtime exceptions. With respect to your concern, if the database is down then a runtime exception is generated and you can catch it at the controller level(if you are writing a web application).
The test case would throw out an error for unreachable host, if you are executing the test case when the DB is down.

Resources