I've got a Seam web application working with Seam & Hibernate (JDBC to SQLServer).
It's working well, but under heavy load (stress test with JMeter), I have some LockAcquisitionException
or OptimisticLockException
.
The LockAquisitionException
is caused by a SQLServerException
"Transaction (Process ID 64) was deadlock on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction".
I've then written a Seam Interceptor to rerun such transactions for LockAquisitionException
:
@AroundInvoke
public Object aroundInvoke(final InvocationContext invocationContext) throws Exception {
if (instanceThreadLocal.get() == null && isMethodInterceptable(invocationContext)) {
try {
instanceThreadLocal.set(this);
int i = 0;
PersistenceException exception = null;
do {
try {
return invocationContext.proceed();
} catch (final PersistenceException e) {
final Throwable cause = e.getCause();
if (!(cause instanceof LockAcquisitionException)) {
throw e;
}
exception = e;
i++;
if (i < MAX_RETRIES_LOCK_ACQUISITION) {
log.info("Swallowing a LockAcquisitionException - #0/#1", i, MAX_RETRIES_LOCK_ACQUISITION);
try {
if (Transaction.instance().isRolledBackOrMarkedRollback()) {
Transaction.instance().rollback();
}
Transaction.instance().begin();
} catch (final Exception e2) {
throw new IllegalStateException("Exception while rollback the current transaction, and begining a new one.", e2);
}
Thread.sleep(1000);
} else {
log.info("Can't swallow any more LockAcquisitionException (#0/#1), will throw it.", i, MAX_RETRIES_LOCK_ACQUISITION);
throw e;
}
}
} while (i < MAX_RETRIES_LOCK_ACQUISITION);
throw exception;
} finally {
instanceThreadLocal.remove();
}
}
return invocationContext.proceed();
}
First question : do you think this interceptor will correctly do the job ?
By googling around and saw that Alfresco (with a forum talk here), Bonita and Orchestra have some methods to rerun such transactions too, and they are catching much more Exceptions, like StaleObjectStateException
for instance (the cause of my OptimisticLockException
).
My 2nd question follows : for the StaleObjectStateException
("Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)"), normaly you can't just rerun the transaction, as it's a problem of synchronisation with the database and @Version
fields isn't it ? Why Alfresco for instance tries to rerun such Transactions caused by such Exceptions ?
EDIT :
For LockAcquisitionException
caused by SQLServerException
, I've looked at some some resources on the web, and even if I should double check my code, it seems that it can happend anyway ... here are the links :
- An article on the subject (with a comment which says it can happend by running out of resources also)
- Another article with sublinks :
Even Microsoft says "Although deadlocks can be minimized, they cannot be completely avoided. That is why the front-end application should be designed to handle deadlocks."