【问题标题】:How to prevent "NucleusDataStoreException: Concurrent Modification"?如何防止“Nucleus DataStoreException:并发修改”?
【发布时间】:2015-02-13 14:47:43
【问题描述】:

我们的应用突然获得了大量流量,并且系统存在一些设计缺陷(或者更确切地说,我们从未想过它会获得如此多的流量,所以我们只是选择跳过它)。

正如主题所述,我正在寻找一种防止错误的方法:org.datanucleus.exceptions.NucleusDataStoreException: Concurrent Modification

目前我有一个名为 Group 的实体,看起来像这样:

@PersistenceCapable
public class Group extends PersistableString {
    private static final long serialVersionUID = 6215353466976945628L;

    @Persistent
    private int yesCount;
    @Persistent
    private int noCount;

    public void increaseYesCount()
    {
        yesCount++;
    }
    public void increaseNoCount()
    {
        noCount++;
    }
}

以下代码是实体的更新是如何完成的:

int answer = Integer.parseInt(req.getParameter("answer"))

try {
    PersistenceManager pm = PMF.getPersistenceManager();
    for(String groupId : allGroupsToBeUpdated)
    {
        Group group = pm.getObjectById(Group.class, groupId);
        if(answer == 0)
            group.increaseNoCount();
        else
            group.increaseYesCount();
    }
    pm.close();
} catch (Exception e) {
    e.printStackTrace();
}

allGroupsToBeUpdated 是一个包含大约 30 个字符串 ID 的列表。有什么方法可以避免Concurrent Modification-error 吗?我可以检查我检索的实体是否正在更新,然后丢弃(/忽略)更新?写入实际上是否成功并不重要,我只是想确保我没有收到错误(或者它一直试图成功写入),因为它导致请求需要 10-30 秒。

我是否应该在每次更新之间打开(获取新的PM-instance)并关闭连接(pm.close()),而不是等待所有 30 次更新完成?

我知道分片计数器并且应该(显然)使用它们,但现在我正在寻找解决这个问题的“快速解决方案”。

编辑: 我正在使用:

App Engine SDK 1.8.9

JDO 3.0

可以在以下位置找到 Stacktrace: http://pastebin.com/TWnmkpPU

【问题讨论】:

  • 由于你不发布堆栈跟踪,软件版本,它不可能说什么。
  • 抱歉,我认为这无关紧要。问题是(至少我认为是)这个请求在多个服务器实例和想要更新的同一个组上运行,从而导致错误。我将使用 App 引擎版本和可能的堆栈跟踪更新帖子。
  • 因此并发修改异常是由 Google 的数据库和/或处理代码引发的(即与 JDO 本身无关)。也许升级正在使用的库版本(例如,JDO v2 是古老的),也许在你的代码中使用事务
  • 对不起,我的错!我正在使用 JDO v3。 “通过选择Datanucleus JDO/JPA v2切换到JDO 3.0”,这是我看到和写的数字,但实际上是JDO 3.0。是的,也许升级 App Engine SDK 会有所作为,但我怀疑这就是问题所在。当服务器上传时,我认为它在幕后使用了最新的“引擎”。

标签: java google-app-engine jdo


【解决方案1】:

由于篇幅原因,作为答案发布。

在您的情况下,事务可能并不好,因为您实际上只是想向用户隐藏问题,这在缓慢的请求时间中表现出来。也许启动异步推送任务以在请求之外的后台执行写入将是您最好的选择。

我真的不推荐基于隐藏错误和吞咽异常的设计。想要“防止”一个正在做它应该做的异常(表示由于争用而导致写入失败),意味着你应该首先避免导致它的条件。

我完全理解需要尽早让事情快速运行,但是一旦糟糕的设计决策开始产生影响,现在就开始采用最佳实践可能是个好主意。继续依赖“快速修复”和隐藏问题可能会让您日后陷入困境。

【讨论】:

  • 感谢您对此的意见!当然,我不希望这个解决方案长期存在。但是在当前时间实施分片是一项太大的时间投资。我相信长请求时间是由于异常,写操作“暂停”,因为它想写,但不能。这种类型的成功请求执行不到 1 秒,这就是为什么我认为推送任务只会解决问题。
  • 没问题。是的,任务只会移动问题,但这将是确保问题对用户隐藏的快速修复。您对为什么写操作由于争用而等待是正确的,但是由于操作是同步的,您的代码将阻塞延迟。 AFAIK 没有办法让 JDO 在并发修改时“快速失败”(可能有充分的理由)。
  • 再次感谢您对此问题的澄清!
猜你喜欢
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-11
相关资源
最近更新 更多