3

これが古典的な問題であることは知っていますが、まだ方法がわかりません。Google App Engine には、jQuery の検証を使用してユーザー名が存在するかどうかを確認するメンバー登録フォームがあります。もちろん、同時実行の問題があります。複数のユーザーが登録を試み、同じユーザー名を入力すると、検証により使用可能なユーザー名が検出され、ほぼ同時に「追加」を押すことができます。検証はこれを検出しません。私のアプリケーションでは、ユーザー名、電子メール、および個人 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)

これにより、ユーザー名を登録できるのは 1 人だけになります。

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

私はテスダルに同意した。Daniel が提案した memcache tric を引き続き実装したい場合は、「memcache.add(usernameA, dummy value, short period);」のようにする必要があります。したがって、usernameA は短期間予約されており、「memcache.add(usernameB, ...」と競合しないことがわかります。

于 2013-02-15T20:36:28.840 に答える