Execute 2 mthods in a songle transaction with separate Retry logic - spring-boot

Below is my requirement
begin trans
insertData();
updateData();
end trans
Also lets say insertDta method throws some error then I need to retry 5 times .Same with updateData().I should not retry both the methods at the same time i.e if i retry m2() 5 times them m1() should not be retried.
Is below the correct way to do it? I call m3() from another class .
My concern is that interceptors are added in correct and deterministic order.
#Repository
DaoA
{
void insertData();
}
#Repository
DaoB
{
void updateData();
}
Below is my Service class .
#Service
ServiceA
{
#Retryable( maxAttempts = 5)
public void m1 ()
{
daoA.insertData();
}
#Retryable( maxAttempts = 5)
public void m2 ()
{
daoB.updateData();
}
#Transactional
public void m3 ()
{
m1();
m2();
}

m3() needs to be in a different bean - calling m1() and m2() directly within the class bypasses the proxy and they won't be retried.
In any case, the transaction should be within the retry logic, not the other way around; you need to start a new transaction for each attempt.

If I got your requirement right, this should work for you.
#Service
ServiceA {
public void m1 () {
daoA.insertData();
}
public void m2 () {
daoB.updateData();
}
#Transactional
#Retryable(value = {Exception.class}, maxAttempts = 5)
public void m3 () {
m1();
m2();
}
}
This will make sure the total number of retries is maxAttempts = 5.

Related

Spring JPA transaction partially retry

I am trying to use Spring retry for my Spring web application (with JPA, hibernate).
Currently, I have a long transaction chain (marked functions as #Transactional) in my application, intuitively:
start transction -> A() -> B() -> C() -> D() - E() -> commit/rollback
now I want to have D() to be retried if any concurrency failure occurred (marked D() with #retryable), but remain A(), B(), C() in current states
I failed, the function D() is not retried at all and just throwed a concurrency failure error (eg. ObjectOptimisticLockingFailureException)
For me, if I want to do such things in database, I have to make a new transaction try catch block with a cursor while loop to handle retries. I wonder is there a simple way I can handle this "partial" transaction retry in Spring?
An example code would be:
#RestController
public DimensionController()
{
...
#Autowired
private TblDimensionService dimensionService;
...
#PutMapping(...)
public ResponseEntity<TblDimensionDTO> update(#Valid #RequestBody TblDimensionDTO dimensionDTO)
{
...
dimensionService.update(dimensionDTO);
...
}
}
#Transactional //transaction in service level
#Service
public TblDimensionService()
{
...
#Autowired
private RetryService retryService;
...
public TblDimensionDTO update(TblDimensionDTO dimensionDTO) throws InterruptedException
{
if (dimensionDTO.getId() == null)
{
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "not found");
}
TblDimension dimension = findByIdOrElseThrow(dimensionDTO.getId()); //call another function to retrieve entity by id with JPA repository
dimension = retryService.updateEntity(dimension, dimensionDTO);
return tblDimensionMapper.toDto(dimension);
}
...
}
#Transactional //transaction in service level
#Service
public RetryService()
{
...
#Autowired
private TblDimensionRepository dimensionRepository;
...
//I want to only retry this part
//but this retry does not work
#Retryable(value = {ConcurrencyFailureException.class})
public TblDimension updateEntity(TblDimension dimension, TblDimensionDTO dimensionDTO) throws InterruptedException
{
Thread.sleep(3000);
dimension.setHeight(dimension.getHeight() + 1);
Thread.sleep(3000);
return dimensionRepository.save(dimension);
}
...
}

Runtime modification of Spring #Retryable maxAttempts value

Scenario : I need to modify maxAttempts value of #Retryable at runtime, so that the number of retries can be driven from database
#Service
public class PropertyHolder {
private int retryCount= 2;
public int retryCount() { return retryCount; }
#Scheduled(fixedRate=3600000) //Query DB and update retryCount every 60mins
private void updateRetryCount(int in) {
this.retryCount = 0; //Fetch retryCount from DB and update retryCount
}
}
#Service
public class SimpleService{
#Retryable( value={ Throwable.class }, maxAttemptsExpression="#{#propertyHolder.retryCount()}")
private void performTask() {
// do some opertion that throws Exception
}
}
PropertyHolder will update retryCount once in every 60 minutes.
This PropertHolder#retryCount needs to be wired to #Retryable in SimpleService#performTask .At present, it takes only the initial value of retryCount (2).Is this a right approach or Am I making some terrible mistake?
No; currently the expression is only evaluated during context initialization; there is an open feature request to add runtime evaluation.
https://github.com/spring-projects/spring-retry/issues/184
Currently you have to wire up your own interceptor with a mutable retry policy and configure it via the interceptor property in #Retryable.

Calling #Transactional methods asynchronously in quarkus

I have an application scoped bean as follows
#ApplicationScoped
public class Worker {
public void process(Long id) {
final Runnable runnable = () -> {
doATransaction(id);
};
executor.execute(runnable);
}
#Transactional
public void doATransaction(Long id) {
User user = User.findById(id);
}
}
I am getting a javax.enterprise.context.ContextNotActiveException. I also tried adding the doATransaction() to another Bean, which was injected in this Worker, as suggested here. Still had the same problem.
Does anyone have an idea of what I could do next?
The Exception I am getting is
Exception in thread "pool-13-thread-3" javax.enterprise.context.ContextNotActiveException
at io.quarkus.arc.impl.ClientProxies.getDelegate(ClientProxies.java:40)
at io.quarkus.hibernate.orm.runtime.RequestScopedSessionHolder_ClientProxy.arc$delegate(RequestScopedSessionHolder_ClientProxy.zig:42)
at io.quarkus.hibernate.orm.runtime.RequestScopedSessionHolder_ClientProxy.getOrCreateSession(RequestScopedSessionHolder_ClientProxy.zig:160)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.acquireSession(TransactionScopedSession.java:103)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.find(TransactionScopedSession.java:168)
at io.quarkus.hibernate.orm.runtime.session.ForwardingSession.find(ForwardingSession.java:68)
at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.findById(AbstractJpaOperations.java:173)
at com.example.User.findById(User.java)

Avoiding multi-instance singletons in single context Spring app

A #PostConstruct function returns a System.identityHashCode(this) different from an #Autowired version of the "same" singleton...
I do not have multiple application contexts there is only a single context involved. I don't expect the presence of threading or static inner classes to be relevant, but I have included them just in case.
#Service
public class A extends Thread {
#PostContruct
public void init() {
start();
}
#Service
public static class B extends A {
public B() {
}
public void run() throws Throwable {
System.err.println("B("+System.identityHashCode(this)+") waiting...");
synchronized(this) {wait(6000000);}
System.err.println("B("+System.identityHashCode(this)+") done waiting the full 6000 seconds and never less");
}
}
private B b;
#Autowired(required=true)
public A(B b) {
this.b = b;
}
public void run() throws Throwable {
Thread.sleep(10000);//just for example
//the following lines could be triggered from any thread
System.err.println("autowire B("+System.identityHashCode(b)+").notifyAll()");
synchronized(b) {b.notifyAll();}
}
}
I expect the original hash code of B to match the Autowired hash code found from class A. However they are different objects and so notifyAll never interrupts B's wait.
B(329759586) waiting...
//10 seconds latter
autowire B(932795037).notifyAll()
//5990 seconds latter
B(329759586) done waiting the full 6000 seconds and never less

spring integration publish subscribe between beans

Thanks for reading ahead of time. In my main method I have a PublishSubscribeChannel
#Bean(name = "feeSchedule")
public SubscribableChannel getMessageChannel() {
return new PublishSubscribeChannel();
}
In a service that does a long running process it creates a fee schedule that I inject the channel into
#Service
public class FeeScheduleCompareServiceImpl implements FeeScheduleCompareService {
#Autowired
MessageChannel outChannel;
public List<FeeScheduleUpdate> compareFeeSchedules(String oldStudyId) {
List<FeeScheduleUpdate> sortedResultList = longMethod(oldStudyId);
outChannel.send(MessageBuilder.withPayload(sortedResultList).build());
return sortedResultList;
}
}
Now this is the part I'm struggling with. I want to use completable future and get the payload of the event in the future A in another spring bean. I need future A to return the payload from the message. I think want to create a ServiceActivator to be the message end point but like I said, I need it to return the payload for future A.
#org.springframework.stereotype.Service
public class SFCCCompareServiceImpl implements SFCCCompareService {
#Autowired
private SubscribableChannel outChannel;
#Override
public List<SFCCCompareDTO> compareSFCC(String state, int service){
ArrayList<SFCCCompareDTO> returnList = new ArrayList<SFCCCompareDTO>();
CompletableFuture<List<FeeScheduleUpdate>> fa = CompletableFuture.supplyAsync( () ->
{ //block A WHAT GOES HERE?!?!
outChannel.subscribe()
}
);
CompletableFuture<List<StateFeeCodeClassification>> fb = CompletableFuture.supplyAsync( () ->
{
return this.stateFeeCodeClassificationRepository.findAll();
}
);
CompletableFuture<List<SFCCCompareDTO>> fc = fa.thenCombine(fb,(a,b) ->{
//block C
//get in this block when both A & B are complete
Object theList = b.stream().forEach(new Consumer<StateFeeCodeClassification>() {
#Override
public void accept(StateFeeCodeClassification stateFeeCodeClassification) {
a.stream().forEach(new Consumer<FeeScheduleUpdate>() {
#Override
public void accept(FeeScheduleUpdate feeScheduleUpdate) {
returnList new SFCCCompareDTO();
}
});
}
}).collect(Collectors.toList());
return theList;
});
fc.join();
return returnList;
}
}
Was thinking there would be a service activator like:
#MessageEndpoint
public class UpdatesHandler implements MessageHandler{
#ServiceActivator(requiresReply = "true")
public List<FeeScheduleUpdate> getUpdates(Message m){
return (List<FeeScheduleUpdate>) m.getPayload();
}
}
Your question isn't clear, but I'll try to help you with some info.
Spring Integration doesn't provide CompletableFuture support, but it does provide an async handling and replies.
See Asynchronous Gateway for more information. And also see Asynchronous Service Activator.
outChannel.subscribe() should come with the MessageHandler callback, by the way.

Resources