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.
Related
I have to write an operation to database only if a subsequent (non transactional) operation succeeds.
The condition is that the operation (let's say, send an e-mail) is slow, and I want to rollback de transaction if this operation fails. The structure is very simple:
#Transactional // A transaction starts and a connection is taken.
public void doStuff(String text) {
writeToDataBase(text);
sendEmail(text); //Rollback previous operation if this fails.
}
It works fine like this, the problem is that if the "sendEmail(text)" operation is slow, then the database connection keeps blocked until this operation finishes, the transaction is commited and the connection released.
I am performing load tests and the connection seems to be behaving in a way that I dont understand, it throws this error:
Connection oracle.jdbc.driver.T4CConnection#50e5e340 marked as broken because of SQLSTATE(72000), ErrorCode(1013)
How should this kind of logic be managed?
Thanks!
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.
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.
I have an injected JDBCTemplate instance, and the code basically executes
private JdbcTemplate template;
public OutputType getOutput(InputType input) {
CallType call = new CallType(input);
CallbackType callback = new CallbackType(input);
OutputType output = (OutputType) template.execute(call, callback);
...
}
I assume the execute method actually connects to the database and retrieves the result. However, I am having trouble finding out how the control flow works from the documentation.
Is the response from execute blocking (thread occupies a CPU core the entire time waiting for the database response)? Is it synchronous, but not blocking (i.e. thread sleeps/is not scheduled until the response is ready)? Is it asynchronous (execute returns immediately but output is incomplete/null, all database processing logic is in the callback)?
I have used several different databases so I am unsure of what actually happens in JdbcTemplate. If my terminology is incorrect please let me know. Thanks!
The JDBC protocol itself is synchronous and blocking - it will block on socket I/O awaiting the database response. While this doesn't mean you couldn't asynchronously call a JDBC provider (manually spawn a separate thread, use actors, etc.), the actual connection to the database will always be synchronous.
JDBCTemplate is also fully synchronous and blocking, there is no thread magic going on under the hood.
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.