use spring jpaRepository in scheduled task without wrapping in transaction - spring

Is it possible to use a jpaRepository to perform CRUD operations within a scheduled Task (annotated with #Scheduled) without wrapping the scheduled method with #Transactional?
Use Case:
We have a scheduler to pop jobs and each job has certain operations to perform and fetches + saves data accordingly.
private void processJob(T job) throws Exception {
updateJobToTaken(job);
internalProcess(job); //calls a service to run the job operations
updateJobToDone(job);
}
1- If we don't wrap the method with #Transactional, all operations done within the internalProcess() are not saved to the DB. So we added #Transactional(propagation.REQUIRES_NEW)
2- This caused a regressions on our dryRun flag: We added a dryRun flag that is passed to the internalProcess method. If the flag is true, we skip calling repository.save(entities) and expected that none of the work was saved. So we simply logged the changes to the console.
if (!dryRun) {
setupsGroupRepository.save(setupsGroup);
}
But since the method is transactional, Spring committed the changes and everything was committed.
What we would like?
Controlling DB access explicitly. Meaning if I don't call repository.save(), i'm not expecting anything to be committed. And vice versa, when i call repository.save(), i'm expecting my updates to be committed fully to the DB.
When we call the internalProcess method directly without having a scheduledTask, everything runs perfectly. but now, it feels that spring transaction management gets in the way at every turn.

Related

Nested transaction in SpringBatch tasklet not working

I'm using SpringBatch for my app. In one of the batch jobs, I need to process multiple data. Each data requires several database updates. And I need to make one transaction for one data. Meaning, if when processing one data an exception is thrown, database updates are rolled back for that data, then keep processing the next data.
I've put all database updates in one method in service layer. In my springbatch tasklet, I call that method for each data, like this;
for (RequestViewForBatch request : requestList) {
orderService.processEachRequest(request);
}
In the service class the method is like this;
Transactional(propagation = Propagation.NESTED, timeout = 100, rollbackFor = Exception.class)
public void processEachRequest(RequestViewForBatch request) {
//update database
}
When executing the task, it gives me this error message
org.springframework.transaction.NestedTransactionNotSupportedException: Transaction manager does not allow nested transactions by default - specify 'nestedTransactionAllowed' property with value 'true'
but i don't know how to solve this error.
Any suggestion would be appreciated. Thanks in advance.
The tasklet step will be executed in a transaction driven by Spring Batch. You need to remove the #Transactional on your processEachRequest method.
You would need a fault-tolerant chunk-oriented step configured with a skip policy. In this case, only faulty items will be skipped. Please refer to the Configuring Skip Logic section of the documentation. You can find an example here.

JPA Spring Refresh issue

I'm working with SpringBoot 2.3.5 with Hibernate and Hikari Pool.
I've an entity, lets call it A, this entity has an "execution status counter" long field, incremented by an Async method, at the end of its executions, so I can split the execution on multiple threads, having a progress counter.
The increment is performed like this:
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Lock(LockModeType.PESSIMISTIC_WRITE)
default void incrementStatusCount(String lotto, Long progressivo) {
A a = findById(...).get();
a.setStatoLdSN(a.getStatoLdSN() != null ? a.getStatoLdSN()+1:1L);
saveAndFlush(a);
}
And it works fine, so externally, using a DB Tool, I can see the counter update.
Now, at the end of my whole execution, I change the string status of my whole execution, so I load the entity, but the entity is already in the cache, so I call a refresh on the entity manager, I can see the query performad and also the retrieved values from the Hibernate log....and my counter 0.
Now, Oracle default isolation leve is READ_COMMITTED (and I verified it on the connection), and my incremented value is committed because I saw it using the DB client, no?
So why JPA, not even calling refresh is loading the right value?

Hystrix timeout doesn't rollback transaction in spring boot

I am trying to implement hystrix with #Transactional on a method in spring boot.
#Transactional(transactionManager="primaryTrnsManager")
#HystrixCommand(commandKey ="createRecord", fallbackMethod="createRecordFallback", commandProperties={
#HystrixProperty(name="execution.siolation.thread.timeoutInMilliseconds",value="1000"),
#HystrixProperty(name="circuitBreaker.requestVoulumeThreshold",value="20"),
#HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="5000"),
#HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="50")})
public String createRecord(String name){
...............
//Dbcall
}
#Transactional(transactionManager="backUptranManager",propagation=propagation.REQUIRES_NEW)
public String createRecordFallback(){
//dbcall
}
What what is happeneing is when the hystrix timeout happens the call i made to database is not getting rollback and the hystrix is falling back to secondary and again calling the database with same sql query. In case of timeout, i want to abort the previous transaction and start another one. Usually #Transactional does that but with hystrix,i am inserting dubplicate records.
Any Help?
This happen because the HistryxCommand is ran in a completely different thread, so, when the "Histryx Admin Thread" (the one which is watching your command thread) reaches the timeout, it's marking the command thread to be interrupted, but what happen inside of it can't be managed by Hyxtrix.
So, the best approach to reach out the behaviour that you want, is set the timeout for the transaction or even at the jdbc library level, so, the timeout will be managed from inside the command thread, if it's reached out, the Timeout Exception will be throwed from inside the command thread, and Hyxtrix will manage it properly.

Write call/transaction is dropped in TransactionalEventListener

I am using spring-boot(1.4.1) with hibernate(5.0.1.Final). I noticed that when I try to write to the db from within #TransactionalEventListener handler the call is simply ignored. A read call works just fine.
When I say ignore, I mean there is no write in the db and there are no logs. I even enabled log4jdbc and still no logs which mean no hibernate session was created. From this, I reckon, somewhere in spring-boot we identify that its a transaction event handler and ignore a write call.
Here is an example.
// This function is defined in a class marked with #Service
#TransactionalEventListener
open fun handleEnqueue(event: EnqueueEvent) {
// some code to obtain encodeJobId
this.uploadService.saveUploadEntity(uploadEntity, encodeJobId)
}
#Service
#Transactional
class UploadService {
//.....code
open fun saveUploadEntity(uploadEntity: UploadEntity, encodeJobId: String): UploadEntity {
// some code
return this.save(uploadEntity)
}
}
Now if I force a new Transaction by annotating
#Transactional(propagation = Propagation.REQUIRES_NEW)
saveUploadEntity
a new transaction with connection is made and everything works fine.
I dont like that there is complete silence in logs when this write is dropped (again reads succeed). Is there a known bug?
How to enable the handler to start a new transaction? If I do Propogation.Requires_new on my handleEnqueue event, it does not work.
Besides, enabling log4jdbc which successfully logs reads/writes I have following settings in spring.
Thanks
I ran into the same problem. This behavior is actually mentioned in the documentation of the TransactionSynchronization#afterCompletion(int) which is referred to by the TransactionPhase.AFTER_COMMIT (which is the default TransactionPhase attribute of the #TransactionalEventListener):
The transaction will have been committed or rolled back already, but the transactional resources might still be active and accessible. As a consequence, any data access code triggered at this point will still "participate" in the original transaction, allowing to perform some cleanup (with no commit following anymore!), unless it explicitly declares that it needs to run in a separate transaction. Hence: Use PROPAGATION_REQUIRES_NEW for any transactional operation that is called from here.
Unfortunately this seems to leave no other option than to enforce a new transaction via Propagation.REQUIRES_NEW. The problem is that the transactionalEventListeners are implemented as transaction synchronizations and hence bound to the transaction. When the transaction is closed and its resources cleaned up, so are the listeners. There might be a way to use a customized EntityManager which stores events and then publishes them after its close() was called.
Note that you can use TransactionPhase.BEFORE_COMMIT on your #TransactionalEventListener which will take place before the commit of the transaction. This will write your changes to the database but you won't know whether the transaction you're listening on was actually committed or is about to be rolled back.

handling http calls from within an EJB transaction

This is the code I have:
//EJB
beanclass 1{
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public String method1(){
method2();
DBupdates();
return "";
}
}
//plain java class
class 2{
method 2(){
//call which may take a long time (but dont want to wait for it to complete)
makes http calls to an external URL method();
}}
The issue is: the Http call may take a long time. However the response of the call decides the next steps in method1 -> db updates and response.the response needs to go back to the end-user, and i cannot make the end-user wait for ever.
i can handle this situation in two ways:
move method2 into the EJB and put TransactionAttributeType.NEVER, so that the http call is not in the transaction, and the transaction of method1 is not waiting on it. In this case, the container manages the transaction of method1 and does no db updates and returns null if it didnt hear back from method2. How long does the method1's transaction wait before "returning"?
i can use JBoss annotation and put a TransactionTimeout of 2 minutes on method1(): in this case, if http call does not complete within 2 minutes, method1 can return null and do no DB updates.
Which of these two methods is advisable and fault-proof?
Thanks for your insights.
When you use TransactionAttributeType.NEVER, the transaction isn't propagated further.
You can use #Asynchronous annotation for a method which returns Future<V> object. Then you can invoke get(timeout, unit) on the object to get the result type V which waits for the given time for manipulation, but it's EJB-3.1 specific.
Can try JBoss specific annotation #TransactionTimeout at method or class level. Also can configure it in jboss.xml or jboss-service.xml depending on your server version. This will be fine with EJB-3.0, but will loose portability of application.

Resources