Frage

Ich habe ein kleines Dilemma schlagen! Ich habe einen Handler genannt Stimme; wenn es aufgerufen wird, setzt er eine Stimme des Benutzers auf, was sie ausgewählt haben. Sich zu erinnern, welche Optionen sie vorher gepflückt, speichere ich eine VoteRecord Optionen, die Details, was ihre aktuellen Abstimmung auf.

Natürlich ist das erste Mal, wenn sie stimmen, muss ich das Objekt erstellen und speichern. Aber aufeinanderfolgenden Abstimmungen sollten nur den Wert des bestehenden VoteRecord ändern. Aber er kommt das Problem: unter Umständen zwei VoteRecords erstellt werden. Es ist selten (nur einmal in allen 500 Stimmen passiert wir bisher gesehen haben), aber immer noch schlecht, wenn es der Fall ist.

Das Problem tritt auf, weil zwei separate Handler beide im Wesentlichen wie folgt vorgehen:

query = VoteRecord.all().filter('user =', session.user).filter('poll =', poll)

if query.count(1) > 0:
 vote = query[0]

 poll.votes[vote.option] -= 1
 poll.votes[option] += 1
 poll.put()

 vote.option = option
 vote.updated = datetime.now()
 vote.put()
else:
 vote = VoteRecord()
 vote.user = session.user
 vote.poll = poll
 vote.option = option
 vote.put()

 poll.votes[option] += 1
 poll.put()

 session.user.votes += 1
 session.user.xp += 3
 session.user.put()

 incr('votes')

Meine Frage ist: Was ist die effektivste und schnellste Weg, um diese Anforderungen zu verarbeiten, während sichergestellt wird, dass keine Anfrage verloren geht und keine Anforderung erstellt zwei VoteRecord Objekte

War es hilfreich?

Lösung

Das Problem ist, dieser Teil:

if vote.count(1) == 0:
    obj = VoteRecord()
    obj.user = user
    obj.option = option
    obj.put()

Ohne eine Transaktion, Ihr Code in dieser Reihenfolge in zwei Dolmetschern Instanzen laufen kann:

if vote.count(1) == 0:
    obj = VoteRecord()
    obj.user = user


if vote.count(1) == 0:
    obj = VoteRecord()
    obj.user = user
    obj.option = option
    obj.put()


    obj.option = option
    obj.put()

Oder irgendeine seltsame Kombination davon. Das Problem ist die Zählung Test erneut ausgeführt wird, bevor der Put aufgetreten ist, so dass der zweite Thread anstelle des zweiten dem ersten Teil des bedingten geht.

Sie können dieses Problem beheben, indem Sie den Code in einer Funktion setzen und dann mit

db.run_in_transaction()

, um die Funktion auszuführen.

Das Problem ist, Sie scheinen auf der Anzahl der Objekte, die von einer Abfrage für Ihre Entscheidungslogik zurückgegeben werden angewiesen, die in der Transaktion gesetzt werden muss. Wenn Sie die Google I / O Gespräche lesen oder die Gruppe buchen, werden Sie sehen, dass dies nicht zu empfehlen ist. Das ist, weil Sie nicht eine Abfrage transactionalize können. Stattdessen sollten Sie die Zählung als Einheit Wert speichern irgendwo, Abfrage für es außerhalb der Transaktion Funktion, und dann den Schlüssel übergeben für diese Entität zu Ihrer Transaktionsfunktion.

Hier ist ein Beispiel für eine Transaktionsfunktion, die ein Unternehmen Eigenschaft überprüft. Es ist der Schlüssel als Parameter übergeben:

def checkAndLockPage(pageKey):
  page = db.get(pageKey)
  if page.locked:
    return False
  else:
    page.locked = True
    page.put()
    return True

Nur ein Benutzer zu einem Zeitpunkt kann diese Einheit sperren, und es wird nie eine doppelte Sperre sein.

Andere Tipps

Der einfachste Weg, dies zu tun, ist der Schlüssel Namen für Ihre Stimme Objekte zu verwenden, und verwenden Sie Model.get_or_insert. Zuerst werden mit einem Namensschema für den Schlüsselnamen - es nach der Umfrage Namensgebung ist eine gute Idee - und tun dann eine get_or_insert zu holen oder die entsprechende Einheit zu erstellen:

vote = VoteRecord.get_or_insert(pollname, parent=session.user, user=session.user, poll=poll, option=option)
if vote.option != option:
  # Record already existed; we need to update it
  vote.option = option
  vote.put()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top