事务适用于实体组,您可以在跨组事务中包含最多 5 个实体组。实体组由单个服务器(或组,已复制)处理,这意味着在检查数据或在实体组内进行祖先查询时,它能够具有一致的内部状态。
常规查询是全局的,索引具有最终一致性。您不知道何时将所有节点的所有更改都包含在索引中。您无法锁定整个数据存储来为您的事务获取一致的快照状态。如果您习惯于为查询使用一致的索引,这是与常规 RDBMS 的关键区别。
对于 1),问题在于您正在事务中执行常规查询,这不像上面解释的那样工作。2) 的答案变成否,查询不能解决赛车问题,你需要显式获取。
您将需要会员、电子邮件和 SSN 的模型。这是一个未经测试的快速示例,希望能帮助您:
class Member(ndb.Model):
email = ndb.KeyProperty()
ssn = ndb.KeyProperty()
# More user properties goes here...
class Email(ndb.Model):
member = ndb.KeyProperty()
class SSN(ndb.Model):
member = ndb.KeyProperty()
@ndb.tasklet
def get_or_insert2(account, email, ssn):
created = False
member_key = ndb.Key(Member, account)
email_key = ndb.Key(Email, email)
ssn_key = ndb.Key(SSN, ssn)
member_obj, email_obj, ssn_obj = yield ndb.get_multi_async([member_key, email_key, ssn_key])
if member_obj is None and email_obj is None and ssn_obj is None:
member_obj = Member(key=member_key, email=email_key, ssn=ssn_key))
email_obj = Email(key=email_key, member=member_key)
ssn_obj = SSN(key=ssn_key, member=member_key)
yield ndb.put_multi_async([member_obj, email_obj])
created = True
raise ndb.Return([created, member_obj, email_obj, ssn_obj])
outcome = ndb.transaction(lambda: get_or_insert2(account, email, ssn), xg=True)
我不确定将 @ndb.tasklet 和 @ndb.transactional(xg=True) 装饰器结合起来是否有效,如果可以,请尝试使用哪种顺序。
如果您需要根据电子邮件或 ssn 查询用户,例如,您可以将 KeyProperties 重命名为 *_ref 并进行类似的操作
@ndb.ComputedProperty
def email(self):
return self.email_ref.id()
虽然这最终是比您预期的更多的代码行,但它在概念上是简单和直接的,当您稍后再看它时,您可以很容易地弄清楚发生了什么。