【问题标题】:Concurrently modifying NDB entities inside transactions not working as expected在事务中同时修改 NDB 实体未按预期工作
【发布时间】:2017-06-26 00:17:23
【问题描述】:

我的理解是ndb.transactional 用于确保函数处理最新数据。我在本地 Google App Engine 开发服务器的交互式控制台中测试了以下代码:

from google.appengine.ext import ndb

class UserModel(ndb.Model):

    level = ndb.IntegerProperty(default=0)

@ndb.transactional(retries=0)
def inc_user_lvl(user_key, recurse=True):
    user = user_key.get()
    print(user.level)
    user.level += 1
    if recurse:
        inc_user_lvl(user_key, recurse=False)
    user.put()

user_key = UserModel().put()
inc_user_lvl(user_key)
user = user_key.get()
print(user.level)

docs say

尝试重试的次数有限制(默认为 3);如果事务仍然不成功,NDB 会引发TransactionFailedError

在这种情况下,重试次数为 0,因此我预计用户的级别会增加到 1 并提高 TransactionFailedError

相反,函数调用都成功了,对inc_user_lvl 的第二次调用在级别 1 的用户上运行(在第一次调用放置实体之前)。两次通话结束后,用户的等级为 2。为什么会这样?

【问题讨论】:

    标签: python google-app-engine transactions app-engine-ndb


    【解决方案1】:

    事务中编写的代码将使用上下文缓存(特定于线程)

    来自the docs

    如果您不知道发生了什么,事务行为和 NDB 的缓存行为可能会使您感到困惑。如果您在事务中修改实体但尚未提交事务,则 NDB 的上下文缓存具有修改后的值,但底层数据存储仍具有未修改的值。

    这意味着对inc_user_lvl 的第二次调用将从上下文缓存中提取UserModel 实体,而不是ping 数据存储。您可以通过在 ndb 模型上设置 _use_cache = False 来解决此问题。例如

    class UserModel(ndb.Model):
        _use_cache = False
        level = ndb.IntegerProperty(default=0)
    

    所以现在用户的级别在函数调用之后是 1 但没有引发异常...

    事务中的写入不会影响后续读取

    由于某种原因,ndb 文档中没有提到这一点。你得看看被取代的版本(db)docs

    这种一致的快照视图还扩展到事务内部的写入后读取。与大多数数据库不同,Cloud Datastore 事务中的查询和获取不会看到该事务中先前写入的结果。具体来说,如果一个实体在事务中被修改或删除,查询或获取会返回该实体在事务开始时的原始版本,如果该实体当时不存在,则返回任何内容。

    这意味着,因为对inc_user_lvl 的第二次调用是在第一次调用的事务中,所以获取用户实体将返回事务开始时的用户。

    您可以使用ndb.transactional 中的kwarg propagation=ndb.TransactionOptions.INDEPENDENT 来启动单独的事务。有关交易选项的完整列表,请参阅 the docs

    @ndb.transactional(retries=0, propagation=ndb.TransactionOptions.INDEPENDENT)
    def inc_user_lvl(user_key, recurse=True):
        user = user_key.get()
        user.level += 1
        if recurse:
            inc_user_lvl(user_key, recurse=False)
        user.put()
    

    这会引发预期的TransactionFailedError

    【讨论】:

      猜你喜欢
      • 2021-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 2020-08-29
      • 2016-02-04
      相关资源
      最近更新 更多