GAE:防止无意中覆盖数据

时间:2015-07-05 12:55:35

标签: google-app-engine transactions google-cloud-datastore app-engine-ndb

假设我有一个具有两个属性的人的实体:

class Person(ndb.Model)
    payments = ndb.IntegerProperty(default=0)
    name = ndb.StringProperty()

我原以为我会在交易中更新payments,但在更新name时我不需要交易。

但是,似乎在以下情况中,我可能会失去payments的值:

TIME | INSTANCE1                 | INSTANCE2 (in transaction)
=====================================================
||   | person1 = key1.get()      |
||   | person1.name = "John"     | person1 = key1.get()
||   | [other stuff that]        | person1.payments = 100
||   | [takes some time]         | person1.put()
||   | person1.put()             |
||   |
\/

在这种情况下,似乎INSTANCE1将覆盖INSTANCE2中写入的付款金额,并且付款金额将会丢失。

这是否意味着我还需要在更新name时使用交易以确保重要数据永不丢失?

或者更一般地说,如果我使用事务来更新实体的任何属性,我应该使用事务来对该实体进行所有更新吗?

3 个答案:

答案 0 :(得分:2)

事务仅在.put()调用期间播放...因此,无论您是否使用事务,您的示例在两个实例中都会产生相同的效果。您将需要实现自己的锁定机制来识别对象自加载以来是否已更改...比如检查last_updated DateTimeProperty ...然后引发异常(https://en.m.wikipedia.org/wiki/Optimistic_concurrency_control

(抱歉格式平淡......我在移动浏览器上)

答案 1 :(得分:1)

是的,为所有更新使用事务可能是最安全的,以避免您描述的情况。您唯一的选择(我能想到的)是使用@Nick Franceschina建议的锁定机制。

答案 2 :(得分:1)

是的,在这种情况下,您将丢失payments的新值。数据存储区没有更新属性值的概念,实体总是被完全覆盖。

  

数据存储区API不区分创建新实体和更新现有实体。如果对象的键表示已存在的实体,则put()方法将覆盖现有实体。

当两者都是交易时,它运作正常。这就是它的确切表现。

  

当事务开始时,App Engine通过检查事务中使用的实体组的上次更新时间来使用乐观并发控制。在为实体组提交事务后,App Engine将再次检查事务中使用的实体组的上次更新时间。如果自我们的初始检查后它已经改变,则抛出异常。

在此特定情况下,当提交实例A中的事务时,App Engine会注意到自事务启动以来该实体已在其他位置更新过。交易A已取消,默认情况下会重试。