lock()方法中的Grails StaleObjectStateException

时间:2014-08-14 06:52:43

标签: java multithreading hibernate grails

我正在尝试锁定事务服务中的域对象(grails 2.3.8)时收到StaleObjectStateException:

@Transactional
class AnalyticsService {
    boolean newStreamView(Long streamId) {

    Stream stream = Stream.lock(streamId) // The exception is launched here

当然,只有在对此服务进行多次并发调用时才会出现这种情况。 我可以看到,hibernate试图用ID和版本参数锁定:

select id from stream where id =442 and version =305 for update

然后失败了。如果我在该域类中禁用乐观锁定(version:false),一切正常(hibernate只使用id来锁定行)。

Marc Palmer's Blog中发布似乎:

  

在保持乐观锁定ON的同时避免StaleObjectException的唯一简单方法是在事务中执行所有GORM工作,并始终使用Domain.lock(id)加载对象。使用动态查找程序或条件时,您需要指定“锁定”选项以使结果预先锁定

他说我们应该保持乐观锁定。

是否有任何安全的方法可以避免使用锁定和乐观锁定的StaleObjectStateException?

如果我禁用乐观锁定(版本:false),我可以期待其他问题。我很担心这个,因为这个域类是从其他服务更新的?

提前致谢。

1 个答案:

答案 0 :(得分:0)

我们通过以下方式解决了100%的StaleStateException / OptimisticLocking问题:

  • 我们没有明确锁定任何东西。我们使用以下
  • 调整了代码流和对象,以最大限度地减少锁争用的可能性
  • 从所有控制器中删除所有@Transactional注释。将域对象从控制器修改为服务的代码。永远不要修改控制器中的域对象。仅委托服务(默认情况下是事务性的)来修改域对象。也许你在这种情况下这样做,但要确保你100%的时间都这样做。
  • 不要禁用乐观锁定,这是有原因的。没有它,您将面临从其他事务覆盖更新的风险。一般来说,如果您在事务处理中使用显式锁定或其他干预解决了这个问题,您真的需要知道自己在做什么。
  • 请记住,如果您的域对象是belongsTo / hasMany关系的一部分,那么当发生任何更新时,所有相关对象的版本号都会被提升。因此,如果两个不同的进程正在更新对象图的不同部分,则第一个提交将使第二个无效并导致此问题。看看Burt Beckwith在这里说的是否相关:https://www.youtube.com/watch?v=-nofscHeEuU。即使他在这里谈论性能,他提出的解决方案也最小化了版本号更新的级联。
  • 同样,您似乎没有在此处执行此操作,但是当我们只需要执行特定更新时,我们可以最大限度地减少传递可能脏的整个域对象。因此,当我们正在设置状态时,我们可能会说orderService.setStatus('foo',orderId)并且让该方法执行get和update,而不是orderService.update(order)。这会使StaleState的机会窗口变得紧张,因为从获取到保存的时间很短。基本上,请确保不要挂起脏域对象超过绝对必要的时间。