4 Stimmen

MongoDB: Strategien zur Lösung eines Rennzustands

Wir haben ein Szenario, in dem wir mehrere Feeds unter einem Website-Modell speichern müssen, wie folgt:

{
  id: site_id
  name: site_name
  feeds: [
    {
      url: feed_url_1
      date: feed_update_date_1
    },
    {
      url: feed_url_2
      date: feed_update_date_2
    },
    ...
  ]
}

Da feeds ein Array ist, können wir es mit $set, $push oder $addToSet aktualisieren.

Es können 2 verschiedene Race Conditions (Schreibverzug) auftreten, wenn unsere gleichzeitige Anwendung (Warteschlange) versucht, dasselbe Website-Modell zu aktualisieren.

Wenn wir $set wählen und Duplikate auf der Clientseite verhindern, kann es vorkommen, dass bei einem Schreibvorgang an dasselbe Website zwei Feeds verloren gehen können, wie in folgender Sequenz:

Gehen Sie davon aus, dass eine Wordpress-Seite 2 Feeds (RSS und ATOM) extrahiert und an Q1 und Q2 sendet.
Q1: vorhandenen Feed laden, überprüfen, ob RSS-Feed neu ist
Q2: vorhandenen Feed laden, überprüfen, ob ATOM-Feed neu ist
Q1: $set feeds => [RSS]
Q2: $set feeds => [ATOM]

Jetzt geht der RSS-Feed verloren.

Wenn wir $push oder $addToSet wählen, kann Folgendes passieren:

Benutzer A fügt eine Website hinzu und fügt den RSS-Feed Q1 hinzu
Benutzer B fügt dieselbe Website hinzu und fügt denselben RSS-Feed Q2 hinzu
Q1: vorhandenen Feed laden, überprüfen, ob RSS-Feed neu ist
Q2: vorhandenen Feed laden, überprüfen, ob RSS-Feed neu ist
Q1: $push RSS
Q2: $push RSS

Jetzt wurde der RSS-Feed dupliziert

Wenn unser Datenmodell einfach { url } wäre, würde $addToSet gegen doppelte Feeds schützen. Leider ist dies jedoch nicht der Fall, da sich das date-Attribut unterscheiden kann. Daher ist $addToSet nicht viel sicherer als $push.

Wir haben einige mögliche Lösungsansätze für dieses Problem durchdacht, aber keiner davon ist ideal angesichts unseres straffen Zeitplans.

  1. Entkoppeln Sie Feeds von der Website in eine eigene Sammlung, schützen Sie sie nur mit url und passen Sie unser Modell und Repository entsprechend an.

  2. Fügen Sie zuerst ein teilweises { url } in das Website-Modell ein und aktualisieren Sie sie dann mit zusätzlichen Informationen. Dadurch sollte $addToSet verwendbar werden, aber es könnte andere Warteschlangen brechen, die erfordern, dass das date immer vorhanden ist (Test erforderlich).

  3. Lassen Sie die Race Condition wie sie ist, fügen Sie zuerst den Feed mit $push hinzu und verwenden Sie eine Hintergrundwarteschlange, um Duplikate später zu erkennen und zu entfernen.

(Es könnte eine 4. Lösung geben, falls Upsert mit Positionsabfrage funktioniert, aber soweit ich weiß, hat MongoDB v2.4 dies noch nicht)

Ich frage mich, ob es bessere Alternativen zur Lösung dieser Art von Race Condition gibt. Oder ob es bewährte Verfahren dafür gibt.

4voto

ppetermann Punkte 306

Sie sollten sich vielleicht tokumx ansehen, eine Abspaltung von MongoDB, die Transaktionen unterstützt (neben einigen anderen nützlichen Funktionen).

3voto

kali Punkte 228

Sie können einen Wächter auf dem Update-Selektor verwenden:

alice(mongod-2.4.8) test> db.foo.save({_id: 12 })
1 neuen Datensatz(en) in 1ms aktualisiert
alice(mongod-2.4.8) test> db.foo.update({ _id: 12, "feeds.url" : {$ne: "baz"} }, 
    { $push :     { feeds : { url: "baz" } } } )
1 vorhandenen Datensatz(en) in 1ms aktualisiert
alice(mongod-2.4.8) test> db.foo.update({ _id: 12, "feeds.url" : {$ne: "baz"} },
    { $push : { feeds : { url: "baz" } } } )
0 Datensatz(e) in 1ms aktualisiert
alice(mongod-2.4.8) test> db.foo.find({_id: 12 })
{
    "_id": 12,
    "feeds": [
        {
            "url": "baz"
        }
    ]
}
1 Datensatz(e) in 1ms abgerufen -- Index[_id_]

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X