I am running a Play! application and am debugging a deadlock.
The error messages I see logged from Play! are:
Deadlock found when trying to get lock; try restarting transaction
Could not synchronize database state with session
org.hibernate.exception.LockAcquisitionException: Could not execute JDBC batch update
From the Play! Documentation
Play will automatically manage transactions for you. It will start a transaction for each HTTP request and commit it when the HTTP response is sent. If your code throws an exception, the transaction will automatically rollback.
From the MySQL Documentation
you must write your applications so that they are always prepared to re-issue a transaction if it gets rolled back because of a deadlock.
My question:
How and where in my Play! application can I catch these rolled back transactions, and handle them (choose to reissue them, ignore them, etc...)?
Updated
While I ended up taking the advice in the accepted answer, and looking for the cause of the deadlock (did you know MySQL foreign key constraints increase the chance of deadlocking? Now I do!), here is some code that was working for me to catch and reissue a failed save.
boolean success = false;
int tries = 0;
while (!success && tries++ < 3) {
try {
updated.save();
success = true;
} catch (javax.persistence.PersistenceException e) {
pause(250);
}
}
You can use custom Enhancer and enhance all controllers in your plugin.
For example, enhancer add catch block and restart request invocation. It is more reliable for restart request then only part of logic inside controller:
package plugins;
..
final public class ReliableTxPlugin extends PlayPlugin {
public void enhance(final ApplicationClasses.ApplicationClass applicationClass) throws Exception {
new TxEnhancer().enhanceThisClass(applicationClass);
}
}
package enhancers;
..
class TxEnhancer extends Enhancer {
public static void process(PersistenceException e) throws PersistenceException {
final Throwable cause = e.getCause();
if (cause instanceof OptimisticLockException || cause instanceof StaleStateException) {
final EntityTransaction tx = JPA.em().getTransaction();
if (tx.isActive()) {
tx.setRollbackOnly();
}
Http.Request.current().isNew = false;
throw new Invoker.Suspend(250);
}
throw e;
}
public void enhanceThisClass(final ApplicationClass applicationClass) throws Exception {
// .. general encahcer code
ctMethod.addCatch("enhancers.TxEnhancer.process(_e);return;",
classPool.makeClass("javax.persistence.PersistenceException"), "_e");
//..
}
}
In most cases of a deadlock you can only do a rollback and try a restart. How ever in a Webapp this should be a really unusual case. What you can do in Play is
catch the exception
handle the Transaction in your code
With JPA.em() you will get the EntityManager. You can look into JPAPlugin to see how play handle the transaction. How ever first of all I would evaluate why there is a deadlock and if this is really a realistic situation which can the server handle intelligent.
I built a play1 module based on #xedon work doing the retry for you.
Related
In our application situation is, user has requested to update 2 fields say (A, B) for an account.
An account has multiple stores to which updated fields to be pushed. One of the store is marked as default store.
Field A has some validations to be done for default store (say limit on quantity.).
If validation fails I am throwing exception.
On success field value is added to store_space_table
Field B must be pushed to all stores. Pushing to store can throw exception when that store is down or unreachalbe.
Currently I have written this code in finally block.
I don't want to rollback the first operation on the exception in the second step. Rather I want to consolidate the exception of step 1 and step 2 and propagate that.
void validateFieldAndPushToStore(List<Field> inputFieldList, Account account) throws ServiceException {
List<Store> allStoresOfAccount = getAllStoresOfAccount(account);
Set<Store> storeListToPushData = new HashSet<>();
try{
if(ifFieldAUpdated(inputFieldList)) {
// get default store from list of stores of an account,
Store defaultStore = getDefaultStore(allStoresOfAccount)
// Validate space availability of A on default store, if validation is successful, then update data in store_space_table
validateSpaceOnDefaultStoreForFieldA(defaultStore);
storeListToPushData.add(defaultStore);
}
} finally {
if( ifFieldBUpdated(inputFieldList) ) {
storeListToPushData.addAll(allStoresOfAccount);
}
if( ! storeListToPushData.isEmpty()) {
// This operation reads fields A from DB (store_space_table), reads field B from field_tbl and push to stores.
pushUpdatesToStores(account, storeListToPushData);
}
}
}
As I read on multiple forums, such handling in finally is not correct/efficient. So I am looking for alternative or better approach to handle this situation.
The two updates should be wrapped in a transaction.
#Transaction is a nutshell.
Your service should be structured as follows.
#Transactional
public void validateFieldAndPushToStore(A a, B b) {
serviceA.validateAndPushA(a);
serviceB.validateAndPushB(b);
}
Where the implementations for serviceA and serviceB would be.
#Transactional
public void validateAndPushA(A a){
validate(a); // can throw validation exception from here
persist(a); // can throw persistence exception from here
}
#Transactional
public void validateAndPushB(B b){
validate(b); // can throw validation exception from here
persist(b); // can throw persistence exception from here
}
Please note the #Transactional on top of validateAndPushA and validateAndPushB.
The persist methods should be annotated with #Transactional as well.
If you would structure your code in this way, if any validation or persistence exception would occur, all database changes will be rolled back. This happens because #Transactional has a property called propagationLevel, which if left on the default value will execute any inner transaction (for example those of the persist operations) in a single outer transaction (i.e. validateAndPushA, validateAndPushB, validate and persist will all execute in the same transaction - so any exception thrown by those methods will result in the whole transaction being rollbacked).
#Transactional allows for a lot of fine tuning, such as for which exceptions the transaction should not be rolled back. Please consult the documentation for all the details.
Hope this helps!
As you have 2 operations which are supposed to get executed even if one of the fails and you also want to propagate the exception/error to above layer, following approach can be used-
void methodPerformingTwoOperations() throws Exception {
Exception firstOperationErrror = null;
Exception secondOperationError = null;
try {
performFirstOperation();
} catch(Exception e) {
firstOperationError = e;
}
try {
performSecondOperation();
} catch(Exception e) {
secondOperationError = e;
}
throwExceptionAsPerErrors(firstOperationError, secondOperationError);
}
void throwExceptionAsPerErrors(Exception firstOperationError, Exception secondOperationError) {
// depending upon exceptions compose and throw new exception from here
}
Update
Note that when using #Transactional always validate what parameters you are passing to this annotation. By default propagation property is set to REQUIRED which means all the transactions will run in the same transaction and if exception specified all will be reverted.
If you want to persist data from one operation even if other one fails then you can apply Transactional on the inner methods(but not on the main method). Please refer to the code from the answer by #alexrolea.
Capture the exception instead of using finally:
boolean failed = false;
try {
} catch (YourException ex) {
failed = true;
}
if (failed) {
}
If you want the exception to be propagated you can store it an a variable and then re-throw it.
I have a Java web app A that sends Pojo's to anothet web app B. I want to create a mechanism that retries the sending of the Pojos from A to B if B is down. The way B's api is configured in A is via Spring with B's jars in A's classpath. I have made a stab at it but the retry is not working. Can anyone tell me if I'm going in the right direction?
What is wrong here is that my code first executes, then rightly throws an exception. The exception is caught in the catch block. The catch block re-calls callB() which rightly throws an exception again but then my retry code goes into non source-file exception handling code and stops.
EDIT: I cannot use jcabi-aspects because it is not supported in Gradle.
public Object getB(Object request){
if (bClient != null) {
int retryCount = 0;
try {
return callB(request);
} catch (Exception e) {
if (++retryCount > 3) {
return null;
}
return callB(request);
}
}
return null;
}
private Object callB(Object request){
return bClient.callServiceB(request);
}
As you want code to repeatedly do something until it is successful, consider writing the control logic as a loop. You will find this makes it easy for you to change the number of retries before the code should give up.
I have this scenario:
fetch (read and delete) a record from IncomingMessage table
read record content
insert something to some tables
if an error (any exception) occurred in steps 1-3, insert an error-record to OutgoingMessage table
otherwise, insert an success-record to OutgoingMessage table
So steps 1,2,3,4 should be in a transaction, or steps 1,2,3,5
My process starts from here (it is a scheduled task):
public class ReceiveMessagesJob implements ScheduledJob {
// ...
#Override
public void run() {
try {
processMessageMediator.processNextRegistrationMessage();
} catch (Exception e) {
e.printStackTrace();
}
}
// ...
}
My main function (processNextRegistrationMessage) in ProcessMessageMediator:
public class ProcessMessageMediatorImpl implements ProcessMessageMediator {
// ...
#Override
#Transactional
public void processNextRegistrationMessage() throws ProcessIncomingMessageException {
String refrenceId = null;
MessageTypeEnum registrationMessageType = MessageTypeEnum.REGISTRATION;
try {
String messageContent = incomingMessageService.fetchNextMessageContent(registrationMessageType);
if (messageContent == null) {
return;
}
IncomingXmlModel incomingXmlModel = incomingXmlDeserializer.fromXml(messageContent);
refrenceId = incomingXmlModel.getRefrenceId();
if (!StringUtil.hasText(refrenceId)) {
throw new ProcessIncomingMessageException(
"Can not proceed processing incoming-message. refrence-code field is null.");
}
sqlCommandHandlerService.persist(incomingXmlModel);
} catch (Exception e) {
if (e instanceof ProcessIncomingMessageException) {
throw (ProcessIncomingMessageException) e;
}
e.printStackTrace();
// send error outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,
ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
return;
}
// send success outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId, ProcessResultStateEnum.SUCCEED.getCode());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
}
private void saveOutgoingMessage(OutgoingXmlModel outgoingXmlModel, MessageTypeEnum messageType)
throws ProcessIncomingMessageException {
String xml = outgoingXmlSerializer.toXml(outgoingXmlModel, messageType);
OutgoingMessageEntity entity = new OutgoingMessageEntity(messageType.getCode(), new Date());
try {
outgoingMessageService.save(entity, xml);
} catch (SaveOutgoingMessageException e) {
throw new ProcessIncomingMessageException("Can not proceed processing incoming-message.", e);
}
}
// ...
}
As i said If any exception occurred in steps 1-3, i want insert an error-record:
catch (Exception e) {
if (e instanceof ProcessIncomingMessageException) {
throw (ProcessIncomingMessageException) e;
}
e.printStackTrace();
//send error outgoing-message
OutgoingXmlModel outgoingXmlModel = new OutgoingXmlModel(refrenceId,ProcessResultStateEnum.FAILED.getCode(), e.getMessage());
saveOutgoingMessage(outgoingXmlModel, registrationMessageType);
return;
}
It's SqlCommandHandlerServiceImpl.persist() method:
public class SqlCommandHandlerServiceImpl implements SqlCommandHandlerService {
// ...
#Override
#Transactional
public void persist(IncomingXmlModel incomingXmlModel) {
Collections.sort(incomingXmlModel.getTables());
List<ParametricQuery> queries = generateSqlQueries(incomingXmlModel.getTables());
for (ParametricQuery query : queries) {
queryExecuter.executeQuery(query);
}
}
// ...
}
But when sqlCommandHandlerService.persist() throws exception (here a org.hibernate.exception.ConstraintViolationException exception), after inserting an error-record in OutgoingMessage table, when the transaction want to be committed , i get UnexpectedRollbackException. I can't figure out where is my problem:
Exception in thread "null#0" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:394)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at ir.tamin.branch.insuranceregistration.services.schedular.ReceiveMessagesJob$$EnhancerByCGLIB$$63524c6b.run(<generated>)
at ir.asta.wise.core.util.timer.JobScheduler$ScheduledJobThread.run(JobScheduler.java:132)
I'm using hibernate-4.1.0-Final, My database is oracle, and Here is my transaction-manager bean:
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
This is the normal behavior and the reason is that your sqlCommandHandlerService.persist method needs a TX when being executed (because it is marked with #Transactional annotation). But when it is called inside processNextRegistrationMessage, because there is a TX available, the container doesn't create a new one and uses existing TX. So if any exception occurs in sqlCommandHandlerService.persist method, it causes TX to be set to rollBackOnly (even if you catch the exception in the caller and ignore it).
To overcome this you can use propagation levels for transactions. Have a look at this to find out which propagation best suits your requirements.
Update; Read this!
Well after a colleague came to me with a couple of questions about a similar situation, I feel this needs a bit of clarification.
Although propagations solve such issues, you should be VERY careful about using them and do not use them unless you ABSOLUTELY understand what they mean and how they work. You may end up persisting some data and rolling back some others where you don't expect them to work that way and things can go horribly wrong.
EDIT Link to current version of the documentation
The answer of Shyam was right. I already faced with this issue before. It's not a problem, it's a SPRING feature. "Transaction rolled back because it has been marked as rollback-only" is acceptable.
Conclusion
USE REQUIRES_NEW if you want to commit what did you do before exception (Local commit)
USE REQUIRED if you want to commit only when all processes are done (Global commit) And you just need to ignore "Transaction rolled back because it has been marked as rollback-only" exception. But you need to try-catch out side the caller processNextRegistrationMessage() to have a meaning log.
Let's me explain more detail:
Question: How many Transaction we have? Answer: Only one
Because you config the PROPAGATION is PROPAGATION_REQUIRED so that the #Transaction persist() is using the same transaction with the caller-processNextRegistrationMessage(). Actually, when we get an exception, the Spring will set rollBackOnly for the TransactionManager so the Spring will rollback just only one Transaction.
Question: But we have a try-catch outside (), why does it happen this exception?
Answer Because of unique Transaction
When persist() method has an exception
Go to the catch outside
Spring will set the rollBackOnly to true -> it determine we must
rollback the caller (processNextRegistrationMessage) also.
The persist() will rollback itself first.
Throw an UnexpectedRollbackException to inform that, we need to rollback the caller also.
The try-catch in run() will catch UnexpectedRollbackException and print the stack trace
Question: Why we change PROPAGATION to REQUIRES_NEW, it works?
Answer: Because now the processNextRegistrationMessage() and persist() are in the different transaction so that they only rollback their transaction.
Thanks
I am using Spring-AMQP to monitor a RabbitMQ message queue in a Play application.
The problem is I can not access my database from the listener code since the JPA context is not open in this scope.
I understand Play Framework manages the JPA context so that it is open when processing HTTP requests, but is there a way I can use JPA from outside Play controllers/jobs?
Just found the answer was to use JPAPlugin!
Example listener method:
public void process(Message message) {
JPAPlugin.startTx(false);
boolean rollBack = false;
try {
// work with your models
JPA.em().flush();
} catch (RuntimeException e) {
rollBack = true;
// throw exception to prevent msg ACK, need to refine error handling :)
throw e;
} finally {
JPAPlugin.closeTx(rollBack);
}
}
I want to build a custom AutoCloseable class so I can turn this:
try {
begin();
doThings();
commit();
} finally {
if (transactionIsActive()) rollback();
}
into the easier
try (Transaction t = begin()) { // too bad I have to store it in t though I don't use it
doThings();
}
Transaction would be the AutoCloseable here and in close() it would commit or rollback the transaction as appropriate.
But to make that work, I would need to detect in Transaction.close() whether an exception occurred inside the try block or it completed normally. Is this possible at all?
If it requires parsing the stack trace from a new exception, that's OK. Easier programming would be worth the tiny performance hit that brings.
The closest I could come up with still requires marking the success of the transaction manually as the last statement of the block:
class Transaction implements AutoCloseable {
private boolean rollback = true;
public void success() {
rollback = false;
}
public void close() {
if (rollback) doRollback();
else doCommit();
// …
}
}
class Main {
public static void main(String[] args) {
try (Transaction t = new Transaction()) {
doThings();
t.success();
}
}
}
Although my code is different from yours, I had a similar need to automatically commit (most) transactions and rollback on errors.
Most of the time my code is sprinkled with simple queries that get rolled back automatically, like this:
try(Transaction t : database.beginTransaction()) {
return t.selectUnique(Employee.class, "id=?", 200);
} // implicit rollback here
Some databases donot like queries being rolled back like this, so I've solved this by distinguishing between "write" and "read" transactions. If it is a read transaction, close() will commit it, otherwise rollback. It will also check you are not doing any write actions when you created a read-only transaction. So now I can write:
try(Transaction t : database.beginReadOnlyTransaction()) {
return t.selectUnique(Employee.class, "id=?", 200);
} // implicit commit here
Write transactions still need to call commit themselves at the end, but this is a minority of cases.
I realize this is not what you asked for, but perhaps it is useful still.
The closest I've been able to get is to explicitly call commit(), and assume that any code that exits the transaction block without doing so should be rolled back. This is consistent with transactions in other languages. While you can forget to call commit() (as I often do), at least this part of the code is very likely to get tested. And, it is impossible to forget to rollback on exception, which is less likely to have test coverage.
This is similar to millimoose's idea of setting a flag:
try (Transaction t = new Transaction()) {
doThings();
t.success();
}
except that you just use the active state as the flag. Same amount of code, no new flag necessary. This assumes that any transaction for which commit() has not been explicitly called should be rolled back, resulting in code like this:
try (Transaction t = new Transaction()) {
doThings();
t.commit(); // marks the transaction as successful...
}
class Transaction implements AutoCloseable {
public void close() {
if (isActive())
doRollback();
}
...
}
I still can't believe there is not a cleaner solution to this in the core language.