我的理解是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)
尝试的重试次数有限制(默认为3);如果事务仍然没有成功,NDB会引发
TransactionFailedError
。
在这种情况下,重试次数为0,因此我希望用户的级别增加到1并且要提升TransactionFailedError
。
相反,函数调用成功,第二次调用inc_user_lvl
对级别为1的用户进行操作(在第一次调用放置实体之前)。两次通话完成后,用户的等级为2。为什么会这样?
答案 0 :(得分:2)
来自the docs:
如果您不知道发生了什么,交易行为和NDB的缓存行为可能会让您感到困惑。如果修改事务中的实体但尚未提交事务,则NDB的上下文缓存具有修改后的值,但基础数据存储仍具有未修改的值。
这意味着对inc_user_lvl
的第二次调用将从上下文缓存中提取UserModel
实体,而不是ping数据存储区。您可以通过在ndb模型上设置_use_cache = False
来解决此问题。 E.g。
class UserModel(ndb.Model):
_use_cache = False
level = ndb.IntegerProperty(default=0)
所以现在函数调用后用户的级别为1,但没有引发异常...
由于某种原因,ndb文档中没有提到这一点。你必须查看被取代的版本(db)docs:
此一致快照视图还扩展到在事务内写入后的读取。与大多数数据库不同,查询和获取Cloud Datastore事务不会在该事务中看到先前写入的结果。具体来说,如果在事务中修改或删除了实体,则查询或获取将返回事务开始时实体的原始版本,如果该实体不存在则返回任何内容。
这意味着,因为对inc_user_lvl
的第二次调用是在第一次调用的事务中,所以获取用户实体将返回用户在事务开始时的状态。
您可以使用propagation=ndb.TransactionOptions.INDEPENDENT
中的kwarg ndb.transactional
开始单独的交易。有关交易选项的完整列表,请参阅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
。