并行更新到不同的实体属性

时间:2012-11-20 09:21:09

标签: java google-app-engine jdo objectify

我正在使用JDO访问数据存储区实体。我目前遇到问题,因为不同的进程并行访问相同的实体,我不确定如何解决这个问题。

我有包含值和计算值的实体:(键,值1,值2,值3,已计算)

计算发生在单独的任务队列中。 用户可以随时编辑值。 如果更新了值,则会将新任务推送到覆盖旧计算值的队列。

我目前遇到的问题是以下情况:

  1. 用户创建实体
  2. 任务已启动
  3. 用户在初始条目中注意到错误并快速更新实体
  4. 任务基于旧数据(来自步骤1)完成并覆盖整个实体,同时删除新输入的值(来自步骤3)
  5. 用户不满意
  6. 所以我的问题:

    • 我可以在步骤4中更新任务失败吗?由于最终的一致性(或者很可能,我对数据存储区事务的理解是错误的),在事务中包装任务似乎并不能解决所有情况下的问题。
    • 使用低级setProperty方法是更新实体的单个字段的唯一方法,这会解决我的问题吗?
    • 如果以上都不是,那么处理像这样的用例的最佳方法是什么

    背景:

    目前,我不介意交易表现的一致性。我会关心以后的表现。

    这是我的第一个AppEngine应用程序,因为它是一个学习过程,所以它没有使用一些最佳实践。我很清楚,事后看来,我应该对我的数据模式进行更长时间的思考。例如,我的实体都没有使用适合他们的祖先关系。我来自一个关系背景,它显示出来。

    我正计划进行一次重大的重构,可能会转向Objectify,但与此同时我还有一些需要尽快解决的紧急问题。我想首先完全理解数据存储区。

3 个答案:

答案 0 :(得分:2)

显然,JDO会为事务提供乐观的并发检查(如果用户启用它),这会阻止/减少此类事件的发生。乐观并发同样适用于关系数据存储,因此您可能知道它的作用。

Google的JDO插件显然使用了低级API setProperty()方法。日志甚至会告诉您进行了哪些低级别调用(就PUT和GET而言)。转移到其他API不会自己解决这些问题。

答案 1 :(得分:2)

每当你需要在GAE中处理写冲突时,你几乎总是需要交易。但是,它不仅仅是“使用交易”这么简单:

  1. 首先,确保可以在事务中定义每个逻辑工作单元。交易有限制;没有祖先的查询,只能访问一定数量的实体组。您可能会发现在交易开始之前需要做一些额外的工作(即,参与交易的实体的查找键)。
  2. 确保每个工作单元幂等。这很关键。某些工作单元是自动幂等的,例如“将我的电子邮件地址设置为xyz”。某些工作单元不是自动幂等的,例如“将5美元从帐户A转移到帐户B”。您可以通过在事务开始之前创建实体,然后删除事务内的实体来使事务处于幂等状态。检查交易开始时是否存在实体,如果已被删除,只需返回(完成txn)。
  3. 运行事务时,请捕获ConcurrentModificationException并循环重试该过程。现在当任何txn发生冲突时,它只会重试直到成功为止。
  4. 这里碰撞唯一的坏处是它会减慢系统速度并在重试期间浪费精力。但是,如果您有XG事务处理吞吐量,您每秒至少会获得一个已完成的事务。

    Objectify4为您处理重试;只需将您的工作单元定义为run()方法,并使用ofy()。transact()运行它。只要确保你的工作是幂等的。

答案 2 :(得分:1)

我看到它的方式,你可以阻止第一个任务更新对象,因为某些值已经从首次启动任务时发生了变化。

或者您可以在任务请求中嵌入对象的值,以便第二个计算任务将使用一致的值和计算成员恢复对象状态。