Domanda

Ho colpito un piccolo dilemma! Ho un gestore chiamato voto; quando viene richiamata imposta voto di un utente a quello che hanno raccolto. Per ricordare quali opzioni hanno precedentemente raccolti, ho memorizzare una scelta VoteRecord che dettaglia che cosa il loro voto corrente è impostato.

Naturalmente, la prima volta votano, devo creare l'oggetto e memorizzarlo. Ma successive voti dovrebbero solo cambiare il valore della VoteRecord esistente. Ma arriva il problema: in alcune circostanze possono essere creati due VoteRecords. E 'raro (è successo solo una volta in tutti i 500 voti che abbiamo visto finora), ma ancora male quando lo fa.

Il problema si verifica perché due gestori separati entrambi fanno essenzialmente questo:

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')

La mia domanda è: qual è il più efficace e veloce per gestire queste richieste, garantendo nel contempo che nessuna richiesta è perso e nessuna richiesta crea due oggetti VoteRecord

È stato utile?

Soluzione

La questione è questa parte:

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

Senza una transazione, il codice potrebbe funzionare in questo ordine in due casi interprete:

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()

o una combinazione strana della stessa. Il problema è il test conteggio viene eseguito nuovamente prima put è verificato, in modo che il secondo filo passa attraverso la prima parte del condizionale posto del secondo.

È possibile risolvere questo problema inserendo il codice in una funzione e quindi utilizzando

db.run_in_transaction()

per eseguire la funzione.

Il problema è che ti sembra di essere basandosi sul conteggio degli oggetti restituiti da una query per la logica decisione che deve essere messo nella transazione. Se leggete i colloqui di Google I / O o guardare il gruppo vedrete che questo non è raccomandato. Ecco perché non si può transactionalize una query. Invece, è necessario memorizzare il conteggio come valore dell'entità da qualche parte, query per fuori della funzione di transazione e quindi passare la chiave per tale entità alla funzione transazione.

Ecco un esempio di una funzione transazione che controlla una proprietà entità. E 'passata la chiave come parametro:

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

Solo un utente alla volta può bloccare questa entità, e non ci sarà mai alcun blocco duplicato.

Altri suggerimenti

Il modo più semplice per farlo è quello di utilizzare i nomi delle chiavi per gli oggetti voto, e utilizzare Model.get_or_insert. In primo luogo, venire con uno schema di denominazione per i vostri nomi chiave - denominazione dopo il sondaggio è una buona idea - e poi fare un get_or_insert per andare a prendere o creare l'entità rilevante:

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()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top