假设我有一个具有两个属性的人的实体:
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
时使用交易以确保重要数据永不丢失?
或者更一般地说,如果我使用事务来更新实体的任何属性,我应该使用事务来对该实体进行所有更新吗?
答案 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
已取消,默认情况下会重试。