0

我正在尝试基于解决赛车问题,以防止重复用户注册。因此,如果帐户存在或电子邮件已被使用,则不会创建任何实体。

@ndb.transactional
def get_or_insert2(account, email):
    accountExists, emailExists = False, False
    entity = Member.get_by_id(account)
    if entity is not None:
        accountExists = True
    if Member.query(Member.email==email).fetch(1):
        emailExists = True
    if not accountExists and not emailExists:
        entity = Member(id=account)
        entity.put()
    return (entity, accountExists, emailExists)

我的问题:

  1. 我收到一条错误消息:BadRequestError:事务中只允许祖先查询。问题是什么?

  2. 代码是否正确?我的意思是,它真的能解决赛车问题吗?

谢谢。

4

1 回答 1

3

事务适用于实体组,您可以在跨组事务中包含最多 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()

虽然这最终是比您预期的更多的代码行,但它在概念上是简单和直接的,当您稍后再看它时,您可以很容易地弄清楚发生了什么。

于 2013-01-27T22:10:03.087 回答