2013-08-02 6 views
7

nel mio server/server.jsMeteor: potrebbe accadere una condizione di gara con Meteor.collections sul lato server?

Meteor.methods({ 
    saveOnServer: function() { 
     var totalCount = Collections.find({ 
      "some": "condition" 
     }).count(); 
     if (totalCount) { 
      var customerId = Collections.update('someId', { 
       "$addToSet": { 
        objects: object 
       } 
      }, function(err) { 
       if (err) { 
        throw err; 
       } else { 
        return true; 
       } 
      }); 
     } else {} 
    } 
}); 

Ho paura che quando saveOnServer() viene chiamato da 2 clienti, allo stesso tempo, verrà restituito lo stesso totalCount per ogni cliente e fondamentalmente finire l'inserimento stesso numero intero nell'id oggetto. L'obiettivo finale è inserire la riga sul lato server con un'operazione atomica che viene completata solo quando viene restituito correttamente totalCount e il documento viene inserito assicurando che non esiste alcun ID duplicato? Sto provando a non usare il _id di mongodb ma ho il mio intero che incrementa la colonna id.

Mi chiedo come posso garantire che un campo venga incrementato automaticamente per ogni operazione di inserimento? Attualmente sto facendo affidamento sul numero totale di documenti. È possibile una condizione di gara qui? In tal caso, qual è il modo migliore per affrontare questo problema?

+0

in linea di principio, il codice della meteora è troppo lontano dai dati per garantire questo. è un'idea migliore usare gli strumenti dbms per una tale convalida. – dandavis

+1

sul lato server tutto è sincrono giusto per evitare l'inferno di callback utilizzando node-fiber? Ma meteor.collection gestisce mongodb, c'è un modo per garantire un oggetto meteor.collection sarà atomico o race condition free? – KJW

+1

La stessa domanda è qui e ha risposto http://stackoverflow.com/questions/15886833/how-can-i-create-an-auto-increment-field-on-meteor. Il tuo codice non è un problema a meno che tu non stia girando su più di un server. – user728291

risposta

9

Nel modello di concorrenza di Meteor, è possibile immaginare un intero metodo come un blocco ininterrotto di cose che accade. Per fare in modo che Meteor passi da un metodo a metà a dire, iniziando un altro metodo, è necessario "produrre": il metodo deve segnalare "Posso essere interrotto".

I metodi restituiscono quando fanno qualcosa di asincrono, il che significa in pratica ogni volta che si esegue un aggiornamento del database o si chiama un metodo con un callback in Meteor 0.6.5 e versioni successive. Dato che tu chiami il tuo update richiamando, Meteor sarà sempre prova a fare qualcosa tra la chiamata a update e il callback update. Tuttavia, in Meteor 0.6.4.2 e precedenti, gli aggiornamenti del database non erano interrompibili, indipendentemente dall'uso di callback.

Tuttavia, più chiamate a saveOnServer avverranno in ordine e non causeranno una condizione di competizione. È possibile chiamare this.unblock() per consentire a più chiamate a saveOnServer di verificarsi "simultaneamente" -i.e., Non condividere la stessa coda, etichettata come saveOnServer queue, di blocchi non interrompibili di roba.

Dato il codice che si possiede, un altro metodo che modifica Collections può modificare il valore di count() tra la chiamata e l'aggiornamento.

È possibile evitare che un metodo di fare l'altra metà non valida mediante l'attuazione dei seguenti modelli di dati:

saveOnServer : function() { 
// ... 
    Collections.update({_id:someId, initialized:true, collectionCount: {$gt: 0}}, 
    {$addToSet: {objects: object}}); 
///... 
} 

Quando si aggiungono oggetti da Collections:

insertObject: function() { 
//... 
    var count = Collections.find({some: condition}).count(); 
    Collections.insert({_id:someId, initialized:false, collectionCount: count}); 
    Collections.update({initialized:false}, 
    {$set:{initialized:true}, $inc: {collectionCount: 1}}); 
} 

nota, mentre questo può sembrare inefficiente, riflette il costo esatto di fare un aggiornamento e inserire in metodi diversi si comportano come tu intendi. In saveOnServer non è possibile inserire.

Al contrario, se si è rimosso il callback da Collections.update, si verificherà in modo sincrono e non ci saranno condizioni di gara Meteor 0.6.5 e successive.

+3

Accadrà solo se si sta utilizzando solo 1 server. Se hai 2 o più server, penso che avrai condizioni di gara su mongodb writes. – gabrielhpugliese

+0

Controlla la mia nota per chiarire come ho capito meglio il codice Meteor. – DoctorPangloss

+0

Nuova nota aggiunta. I miei commenti sull'assegnazione di callback a 'update' sono stati inaccurati fino a 0.6.5 – DoctorPangloss

1

È possibile effettuare questa collezione hanno una chiave univoca su un campo indice, e poi tenerlo aggiornato come segue:

1) Ogni volta che si inserisce nella collezione, prima di fare una query per ottenere il massimo indice e inserto il documento con indice + 1.

2) Per trovare il numero di documenti basta fare la query per ottenere il massimo dell'indice.

L'inserimento è ora una coppia di query, una lettura e una scrittura, quindi può fallire. (DB ops può sempre fallire, però.) Tuttavia, può mai lasciare il database in uno stato incoerente - l'indice di Mongo lo garantirà.

La sintassi per la costruzione di un indice in Meteor è questo:

MyCollection._ensureIndex('index', {unique: 1}); 
1

Un altro modo per fare questo è da un meccanismo di Hibernate/JPA segue - e che è quello di creare un campo di collisione. La maggior parte delle volte, può essere un timestamp di aggiornamento impostato su ciascun aggiornamento. Appena prima di eseguire qualsiasi aggiornamento, interrogare la data/ora dell'aggiornamento. Quindi puoi specificare l'aggiornamento in cui il timestamp di aggiornamento è quello che hai appena recuperato. Se è stato modificato nel frattempo, l'aggiornamento non avverrà e verificherete il codice di ritorno/conteggio che la riga è stata aggiornata o meno. JPA fa questo automaticamente per te quando aggiungi un'annotazione per questo campo di collisione - ma questo è essenzialmente ciò che fa dietro le quinte

Problemi correlati