这个错误一直困扰着我6个月,并且不知道如何解决它。
第一个问题是堆栈跟踪没有给出关于哪个行,查询或控制器导致问题的任何提示。该错误发生在PageFragmentCachingFilter.java
中我做了几个并发的事情,每次调用特定函数时我都会在DB中更新一个计数器。如果两个人在同一时间执行此操作,则可能存在锁定问题。
我的控制器做了三件事:
failOnError = false是试图通过忽略更新错误来解决问题,但它没有工作 - 仍然发生异常。
问题是:
我可以通过同时从两个不同的浏览器窗口调用控制器来重现该问题。
=== update 1 === 我尝试将服务中的步骤3和4放在服务中,理解服务自动启动和结束事务。这并没有解决问题,但是在调用这个新服务来更新计数器的行上抛出相同的exeption,所以这至少指出了问题。
假设有一个域对象报告,如下所示:
class Query {
String sql
String name
int counter
}
现在的步骤是:
两个不同的人在同一个查询对象上同时执行上述步骤。
@Transactional
class aService {
def count(Query query) {
query.counter++
query.save();
}
以上,即使是事务性的,也会失败并出现同样的错误。
我认为我需要做一些像重新读取实例对象的事情。
====更新2 =====
尝试更改服务以执行此操作:
def count(Query query) {
Query q2 = Query.get(query.id)
q2.counter++
q2.save();
}
当两个人同时尝试这样做时,得到同样的错误。不起作用。
=== update 3 ====
寻找一种方法来“选择更新”,这样它就会锁定行,更新它,释放。这必须是多用户安全的。
试过这个:
def count(Query query) {
Query q2 = Query.get(query.id)
q2.lock()
q2.counter++
q2.save();
}
猜猜是什么?同样的错误,但这次把它扔在锁线上。如何进行全新的select更新会导致staleObjectStateException?
=== update 4 ====
试过这个:
def count(Query query) {
Query q2 = Query.lock(query.id)
q2.counter++
q2.save();
}
猜猜是什么?同样的错误。似乎无法更新2人或更多人使用的计数器。此外,我可以100%重现,这是非常奇怪的,因为这个错误应该只发生很少,因为这个简单的更新的速度。有些事情是非常错误的。
=== update 5 ========
变得更加绝望。尝试以下方法:
import org.springframework.transaction.annotation.*
class aService {
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
def count(Query query) {
Query q2 = Query.lock(query.id)
q2.counter++
q2.save(flush:true);
}
锁定行上的相同错误。似乎在grails中不可能做相同的
update query set counter = counter + 1 where id = y
甚至
select * from query where id = 1 for update
update query set counter = counter + 1 where id = 1
commit
=== update 6 ====
有趣的是,在步骤1中发生的读取使用get。我尝试将其更改为读取(id),希望这可能会有所帮助。有趣的是,如果我使用read读取实例,那么应用程序每次都会在此行上失败,即使有1个用户:
Query q2 = Query.lock(query.id)
错误:
attempted to lock a deleted instance
WTH?首先,我没有删除任何实例,其次,我试图锁定一个不错的新读取,第三,我没有编辑或更改原始读取对象。