在 Grails (GORM) 中处理并发修改,同时避免过时对象异常

Posted

技术标签:

【中文标题】在 Grails (GORM) 中处理并发修改,同时避免过时对象异常【英文标题】:Handling Concurrent Modifications in Grails (GORM) while avoiding Stale Object Exceptions 【发布时间】:2013-08-15 02:47:57 【问题描述】:

我在 Grails 中遇到了一个问题,我认为这可能是并发处理方式的潜在问题;而且我不确定如何最好地处理这个问题(或者如果已经有解决方案/实践我可以适应)。

背景


我的 Grails 应用程序用作 REST API,并且有一种数据加密方法,该方法依赖于 counter 变量作为密码的加盐机制。

这个计数器变量必须被维护,并且不能更低,因为数据将进入 SIM 卡,其中计数器不能在发行后修改,因此它我正确维护这个计数器很重要。此外,如果计数器不正确,SIM 卡也会拒绝该消息。

当用户调用时,例如:http://example.tld/service/controller/action?id=1,服务器将执行以下操作:

get 具有标识符 1 的对象 controller 修改对象的counter成员/行 save对象

对于超过 20,000 个请求,这很好。但是,有两次,我有一个StaleObjectException,由于对象在同一时间被访问,我已经确定会发生这种情况。我已经确定这种情况正在发生,因为我提供的包装 API 使用了 10 个线程,并且偶然两个线程同时调用了 action

我已阅读并注意到我可以:

关闭乐观锁定(这似乎是一个主意) 打开动态更新(我认为这对我没有帮助,因为我仍在同时更新同一行lock 对象——但我认为这仍然会引发异常,因为对象在第二次被访问时被锁定? 使用executeUpdate,我认为这类似于打开动态更新?在我的用例中可能没用。

问题


我想知道是否有一些我可以使用的交易机制? IE。检查对象当前是否被锁定的方法,如果是,sleep for t 以允许事务在数据库中完成。

最终,我的最终目标是拒绝任何请求,因此如果上面的事务机制存在(我假设这将是某种悲观锁)并且所述事务机制拒绝请求,因为对象被锁定,我宁愿采用其他解决方案,因为我想不惜一切代价避免拒绝对服务的请求,因为这会使之前向客户的交付复杂化。

我正在考虑的当前解决方案是:

try  
    Foo.save()
catch (RespectiveException ex) 
    Thread.sleep(1000)
    if(depth < 3) 
        recursiveCallToThisMethod(depth++)
     else 
        render(letTheUserKnowWhyItFailed)
    

但是是的……很丑。

【问题讨论】:

通常情况下,客户端会处理并发修改异常,然后必须重试操作。 这个问题可能会有所帮助:***.com/questions/129329/… 你有没有想出任何解决方案?我也有同样的问题。我唯一能想到的尝试是有一个同步块锁对象的并发哈希映射,由域对象本身的唯一键引用并由服务单例持有。当调用服务方法时,它首先检查映射以查看域对象键值是否已经存在锁定对象,并在同步块中使用它。如果锁定对象不存在,它会创建一个并将其存储到地图中。完成后,必须在 finally 块中从地图中删除对象 StaleObjectException 发生在对象被数据库锁定时,因此您需要考虑发生这种情况的原因。如果您要存储新数据,则需要进行一些验证,但是如果您只是定期更新,那么您可以等到锁返回并重新尝试操作(但是在高流量系统中,这可能会导致发送到服务器的“最新”内容与数据库中的内容的错误陈述)。我认为,尝试理解为什么必须如此频繁地更新成员可能会更好。或许添加临时数据是一种方法。 尝试使用 object.refresh 和 object.merge。 refresh 将避免 staleobjectexception 和 merge 将允许在 hibenrate 中合并两个具有不同状态的对象。 【参考方案1】:

尝试悲观锁定。第二个请求不会有异常,它只会阻塞直到第一个请求完成。

def controller = Controller.lock(params.id)
controller.counter += 1
controller.save()

【讨论】:

以上是关于在 Grails (GORM) 中处理并发修改,同时避免过时对象异常的主要内容,如果未能解决你的问题,请参考以下文章

Grails 查询不使用 GORM

在 Grails/GORM 中定义默认排序顺序

GORM 在 Grails 中使用 MongoDB 按 ID 获取/查找资源

独立使用 Grails GORM

GORM 无法实现插件中的域类是 GORM 类

如何覆盖 Grails GORM 中关系的级联删除?