我有一个使用 Seam & Hibernate(JDBC 到 SQLServer)的 Seam Web 应用程序。
它运行良好,但在重负载下(使用 JMeter 进行压力测试),我有一些LockAcquisitionException
or OptimisticLockException
.
这LockAquisitionException
是由SQLServerException
“事务(进程 ID 64)与另一个进程在锁定资源上发生死锁并已被选为死锁受害者。重新运行事务”引起的。
然后,我编写了一个接缝拦截器来重新运行此类事务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();
}
第一个问题:你认为这个拦截器能正确完成这项工作吗?
通过谷歌搜索并看到Alfresco(这里有一个论坛讨论),Bonita和Orchestra也有一些方法可以重新运行此类事务,并且他们正在捕获更多异常,StaleObjectStateException
例如(我的原因OptimisticLockException
)。
我的第二个问题如下:对于StaleObjectStateException
(“行已被另一个事务更新或删除(或未保存的值映射不正确)”),通常你不能只重新运行事务,因为这是与数据库和@Version
字段同步的问题不是吗?例如,为什么 Alfresco 会尝试重新运行由此类异常引起的此类事务?
编辑:由于LockAcquisitionException
原因SQLServerException
,我查看了网络上的一些资源,即使我应该仔细检查我的代码,它似乎无论如何都会发生......这里是链接:
- 一篇关于这个主题的文章(有评论说它也可能因资源耗尽而发生)
- 另一篇带有子链接的文章:
甚至微软也说“虽然死锁可以最小化,但不能完全避免。这就是为什么前端应用程序应该设计成处理死锁的原因。”