MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Mongoose doppelter Schlüsselfehler mit Upsert

Ein Upsert, das zu einer Dokumenteneinfügung führt, ist keine vollständig atomare Operation. Stellen Sie sich das Upsert so vor, dass es die folgenden diskreten Schritte ausführt:

  1. Abfrage nach dem identifizierten Dokument zum Upsert.
  2. Wenn das Dokument vorhanden ist, aktualisieren Sie das vorhandene Dokument atomar.
  3. Sonst (das Dokument existiert nicht), fügen Sie atomar ein neues Dokument ein, das die Abfragefelder und die Aktualisierung enthält.

Die Schritte 2 und 3 sind also jeweils atomar, aber nach Schritt 1 könnte ein weiteres Upsert auftreten, sodass Ihr Code nach dem doppelten Schlüsselfehler suchen und dann das Upsert wiederholen muss, wenn dies auftritt. An diesem Punkt kennen Sie das Dokument mit dieser _id existiert, also wird es immer erfolgreich sein.

Zum Beispiel:

var minute = utils.minute();
Monitor.update({ _id: minute }, { $inc: update }, { upsert: true }, function(err) {
    if (err) {
        if (err.code === 11000) {
            // Another upsert occurred during the upsert, try again. You could omit the
            // upsert option here if you don't ever delete docs while this is running.
            Monitor.update({ _id: minute }, { $inc: update }, { upsert: true },
                function(err) {
                    if (err) {
                        console.trace(err);
                    }
                });
        }
        else {
            console.trace(err);
        }
    }
});

Siehe hier für die zugehörige Dokumentation.

Sie fragen sich vielleicht immer noch, warum dies passieren kann, wenn die Einfügung atomar ist, aber das bedeutet, dass keine Aktualisierungen am eingefügten Dokument erfolgen, bis es vollständig geschrieben ist, nicht, dass keine andere Einfügung eines Dokuments mit derselben _id auftreten können.

Außerdem müssen Sie keinen Index für _id manuell erstellen da alle MongoDB-Sammlungen einen eindeutigen Index für _id haben trotzdem. Sie können also diese Zeile entfernen:

monitorSchema.index({_id: -1}); // Not needed