3

我知道这是一个经典问题,但我仍然不知道该怎么做。在 Google App Engine 上,我有一个成员注册表单,它使用 jQuery 的验证来检查用户名是否存在。当然存在并发问题:多个用户尝试注册,输入相同的用户名,Validation 找到可用的用户名,并允许他们几乎同时按下“添加”。验证不会检测到这一点。在我的应用程序中,用户名、电子邮件和个人 ID 都应该是唯一的。如何防止以下代码出现并发问题:

member = Member()
member.username = self.request.get('username')
member.Pid = self.request.get('Pid')
member.email = self.request.get('email')
...
4

3 回答 3

3

这对 memcache 很有用。您的 Ajax 验证函数应该在 memcache 中添加一个条目以记录用户名已被请求。它还应该检查 memcache 和数据存储以确保用户名是免费的。同样,注册码应该检查 memcache 以确保当前用户是请求用户名的用户。

这很好地解决了您的并发问题,最好的事情是 memcache 中的条目会自行过期,无论是在定时的基础上还是在缓存太满时。

于 2013-01-24T14:32:49.033 回答
3

由于唯一性约束在用户名上,因此您必须将其用作数据存储中的键并使用事务。

def txn():
    key = ndb.Key(Member, username)
    member = key.get()
    if member is not None:
        raise CustomAlreadyExistsException(member)  # This will abort txn
    member = Member(
        id=username,
        Pid=self.request.get('Pid'),
        email=self.request.get('email'),
        ...)
    member.put()
ndb.transaction(txn)

这确保只有一个人可以注册用户名。

jQuery 助手会检查是否ndb.Key(Member, userid).get()给出结果。GET 不是事务性的。

为了提高可用性客户端在检查可用性后“保留”用户名,您可以按照 Daniel 的建议使用 memcached,但我会调用 YAGNI,跳过复杂性,而是让某些人在提交表单后得到验证错误。请注意,memcached 是尽力而为,并且不能保证任何事情。

如果您需要保证多个字段的唯一性,则必须为它们添加模型类并签入跨组 (XG) 事务。

class Pid(ndb.Model):
    member = ndb.KeyProperty()

class Email(ndb.Model):
    member = ndb.KeyProperty()

class Member(ndb.Model):
    pid = ndb.KeyProperty()
    email = ndb.KeyProperty()

    @property
    def pid_value(self):
        return self.pid.id()

    @property
    def email_value(self):
        return self.email.id()

def txn():
    member_key = ndb.Key(Member, username)
    pid_key = ndb.Key(PersonalId, self.request.get('Pid'))
    email_key = ndb.Key(Email, self.request.get('email'))

    member, pid, email = ndb.get_multi([member_key, pid_key, email_key])

    if member is not None or pid is not None or email is not None:
        raise CustomAlreadyExistsException(member, pid, email)  # This will abort txn

    # Create instances referencing each other
    email = Email(key=email_key, member=member_key)
    pid = Pid(key=pid_key, member=member_key)
    member = Member(
        key=member_key,
        pid=pid_key,
        email=email_key,
        ...)
    ndb.put_multi([member, pid, email])

ndb.transaction(txn, xg=True)
于 2013-02-15T08:01:34.890 回答
0

我同意tesdal。如果您仍想实现 Daniel 建议的 memcache tric,您应该执行类似“memcache.add(usernameA, dummy value, short period);”的操作。所以你知道 usernameA 会被保留一小段时间并且不会与“memcache.add(usernameB, ...”

于 2013-02-15T20:36:28.840 回答