【问题标题】:Grails StaleObjectStateException in lock() methodGrails StaleObjectStateException in lock() 方法
【发布时间】:2014-10-07 17:38:31
【问题描述】:

我在尝试锁定事务服务(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

这失败了。如果我禁用该域类中的乐观锁定(版本:false),一切正常(休眠只使用 id 锁定行)。

正如Marc Palmer's Blog 中发布的那样:

在保持乐观锁定开启的同时避免 StaleObjectException(s) 的唯一万无一失的方法是在事务中完成所有 GORM 工作,并始终使用 Domain.lock(id) 加载对象。使用动态查找器或标准时,您需要指定“锁定”选项以预先锁定结果

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

是否有任何安全的方法来避免 StaleObjectStateException 与锁定和乐观锁定 ON ?

如果我禁用乐观锁定(版本:false),我会遇到什么其他问题。我对此感到担忧,因为此域类是从其他服务更新的?

提前致谢。

【问题讨论】:

    标签: java multithreading hibernate grails


    【解决方案1】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-10
      • 2022-12-02
      • 1970-01-01
      • 2023-03-17
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多