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