2012-07-10 9 views
10

Sto usando il driver mgo per MongoDB sotto Go.MongoDB in Go (golang) con mgo: come posso aggiornare un record, scoprire se l'aggiornamento ha avuto successo e ottenere i dati in una singola operazione atomica?

La mia applicazione richiede un'attività (con solo una selezione record in Mongo da una raccolta denominata "lavori") e quindi si registra come destinatario per completare tale attività (un aggiornamento per lo stesso record "processo", impostandosi come assegnatario).

Il programma sarà eseguito su diverse macchine, tutte che parlano con lo stesso Mongo. Quando il mio programma elenca le attività disponibili e ne sceglie una, altre istanze potrebbero aver già ottenuto quell'assegnazione e l'assegnazione corrente sarebbe fallita.

Come posso essere sicuro che il record che ho letto e che aggiorni faccia o non abbia un determinato valore (in questo caso, un assegnatario) al momento dell'aggiornamento?

Sto cercando di ottenere un compito, non importa quale, quindi penso che prima dovrei selezionare un compito in sospeso e provare ad assegnarlo, mantenendolo nel caso che l'aggiornamento abbia avuto successo.

Quindi, la mia domanda dovrebbe essere qualcosa di simile:

"Da tutti i record in 'posti di lavoro' di raccolta, aggiornare solo che ha asignee = null, impostando il mio ID come cessionario Poi, dammi. quel record così ho potuto eseguire il lavoro. "

Come potrei esprimerlo con il driver mgo per Go?

risposta

2

Spero che abbiate visto i commenti sulla risposta selezionata, ma quell'approccio non è corretto. Facendo una selezione e poi l'aggiornamento comporterà un viaggio di andata e ritorno e due macchine e recupereranno per lo stesso lavoro prima che uno di loro possa aggiornare lo assignee. È necessario utilizzare il metodo findAndModify invece: http://www.mongodb.org/display/DOCS/findAndModify+Command

+0

In realtà, la soluzione di cui ho bisogno è quella offerta da tux21b. Devo ritirare tutte le "opzioni", quindi selezionarne una, quindi provare ad assegnarla a me stesso. Se non riuscissi, proverei con un altro. –

+0

Perché è necessario selezionare TUTTE le opzioni? Hai detto che devi solo selezionare quello che non è stato preso (aka assignee == null)? –

+0

Hai ragione. Le mie esigenze si sono rivelate diverse da quelle che pensavo quando ho scritto la domanda, ma la tua risposta risponde meglio alla domanda. –

2

ragazzi Il MongoDB descrivono uno scenario simile nella documentazione ufficiale: http://www.mongodb.org/display/DOCS/Atomic+Operations

In sostanza, tutto quello che dovete fare, è per andare a prendere qualsiasi lavoro con assignee=null. Supponiamo che tu riceva il lavoro con il numero _id=42. È quindi possibile andare avanti e modificare il documento localmente, impostando assignee="worker1.example.com" e chiamando Collection.Update() con il selettore {_id=42, assignee=null} e il documento aggiornato. Se il database è ancora in grado di trovare un documento che corrisponde a questo selettore, sostituirà il documento atomicamente. In caso contrario, otterrete ErrNotFound, a indicare che un altro thread ha già rivendicato l'attività. Se è così, prova di nuovo.

+4

Performing selezionare seguita da un aggiornamento non è atomico; esegue due round-trip! Il comando findAndModify eseguirà questa operazione atomicamente. I documenti Mongo per questo comando sono qui: http://www.mongodb.org/display/DOCS/findAndModify+Command I documenti Mgo per fare questo in Go sono qui: http://go.pkgdoc.org/labix .org/v2/MgO # Query.Applica – jorelli

+1

Non è necessario che l'aggiornamento selezionato + sia atomico per questo particolare algoritmo. Finché l'aggiornamento stesso è atomico (che è fondamentalmente un'operazione di CompareAndSwap/CompareExchange) tutto va bene. – tux21b

+1

Se due macchine selezionano lo stesso lavoro nella prima query, una di esse non riuscirà nell'aggiornamento. Certo, il risultato finale può sempre essere che i dati nel database non diventano mai incoerenti, ma il caso di fallimento con l'utilizzo di due operazioni separate causa un sacco di inutili avanti e indietro. Questo è ... l'intero motivo per cui esiste il comando findAndModify. – jorelli

41

Questa è una vecchia questione, ma solo nel caso in cui qualcuno sta ancora guardando a casa, questo è ben supportato tramite il metodo Query.Apply. Esegue il comando findAndModify come indicato in un'altra risposta, ma è convenientemente nascosto dietro Va bontà.

L'esempio nella documentazione corrisponde più o meno esattamente la domanda qui:

change := mgo.Change{ 
     Update: bson.M{"$inc": bson.M{"n": 1}}, 
     ReturnNew: true, 
} 
info, err = col.Find(M{"_id": id}).Apply(change, &doc) 
fmt.Println(doc.N) 
Problemi correlati