2017-12-07 1 views
0

Ich bin mir bewusst, dass MongoDB kann atomare Updates mit findOneAndModify tun, aber dies erlaubt nur grundlegende Operationen wie Set oder Inkrement.Wie führe ich eine atomare benutzerdefinierte Aktualisierung eines Mongodb-Dokuments durch?

Was ich brauche, ist eine benutzerdefinierte Funktion anzuwenden mein Dokument zu transformieren:

const updateDoc = async (event) => { 
    const oldState = await db.collection(collectionName).findOne({ name }); 
    const newState = customFunction(oldState, event); 
    await db.collection(collectionName).replaceOne({ name }, newState); 
} 

Diese Funktion wird durch ein System aufgerufen werden, die für einen aufgelösten Versprechen warten nicht weiter zu arbeiten: es können mehrere sein synchrone Anrufe.

Gibt es eine Möglichkeit updateDoc neu zu schreiben, um es atomar zu machen, so dass, wenn wir das tun:

updateDoc(event1); // note the absence of await 
updateDoc(event2); 

Wir können sicher sein, dass die gespeicherte doc customFunction(customFunction(initState, event1), event2) sein wird?

Dank

+0

Wenn Sie befürchten, dass 'oldState 'ist etwas, das Sie entscheiden, Ihr' newState' on aber 'oldState' ist möglicherweise nicht das, was derzeit in der Datenbank ist, wenn Sie' newState' schreiben (es kann eigentlich nicht sein, was in der Datenbank ist, wenn Sie Ihre Entscheidung treffen). Das vielleicht Folgende könnte helfen: http://www.dagolden.com/index.php/2633/no-more-dirty-reads-with-mongodb/ Abhängig davon, wie groß Sie skalieren müssen, können Sie CQRS mit machen ein Ereignisspeicher (Sie aktualisieren niemals Ereignisse, sondern verwenden sie, um den aktuellen Status zu berechnen, und die Daten sind schließlich konsistent). – HMR

+0

Danke. Ich mache eigentlich CQRS + Event Store, ich habe Projektionen gelesen, die in mongodb gespeichert sind und bei neuen Ereignissen neu geschrieben werden, diese Frage betrifft diese Projektionen :) –

Antwort

1

Eine mögliche Lösung wäre eine Aufgabe Warteschlange sein, dass ein Update nach dem anderen shedules:

class Scheduler { 
    constructor(){ 
    this.running = false; 
    this.queue = []; 
    } 

    add(el){ 
    this.queue.push(el); 
    if(!this.running) this.run(); 
    } 

    async run(){ 
    this.running = true; 
    while(this.queue.length) await this.queue.shift(); 
    this.running = false; 
} 
} 

Verwendbar wie folgt aus:

const dbTasks = new Sheduler(); 

const updateDoc = async (event) => dbTasks.add(_ => { 
    const oldState = await db.collection(collectionName).findOne({ name }); 
    const newState = customFunction(oldState, event); 
    await db.collection(collectionName).replaceOne({ name }, newState); 
}); 

updateDoc(evt1); 
updateDoc(evt2); 
+0

Wenn Thomas sich Sorgen macht "Dirty Reads?" Dann würde das helfen? Es sieht so aus, als ob er Daten abrufen, entscheiden möchte, welche neuen Daten auf den Daten basieren, und neue Daten speichern. Irgendwo zwischen Daten holen und Daten speichern kann mongodb einige Änderungen an diesem bestimmten Dokument löschen. – HMR

+2

@hmr soweit ich verstehe das Problem ist, dass derzeit sein * get event1 * -> * get event2 * -> * set event1 * -> * set event2 * und deshalb werden die Änderungen von event1 von event2s nicht berücksichtigt . Mit einer Warteschlange löst dies –

+1

Ich weiß nicht genug über Mongo, aber nicht sicher, ob gelöstes Schreiben Versprechen bedeutet, dass wenn Sie unmittelbar danach lesen (in der Task-Warteschlange) Sie tatsächlich die Daten erhalten, die gerade geschrieben (vor allem wenn gruppiert und optimiert für Schreiben). Abhängig von den Anforderungen des OP kann das Serialisieren aller db Lese-/Schreibvorgänge möglicherweise nicht sehr gut skalieren, aber es beantwortet, wonach er fragt. – HMR

Verwandte Themen