Un upsert che risulta in un inserimento di un documento non è un'operazione completamente atomica. Pensa all'upsert come all'esecuzione dei seguenti passaggi discreti:
- Richiesta di inserimento del documento identificato.
- Se il documento esiste, aggiorna atomicamente il documento esistente.
- Altrimenti (il documento non esiste), inserire atomicamente un nuovo documento che incorpori i campi di query e l'aggiornamento.
Quindi i passaggi 2 e 3 sono ciascuno atomico, ma dopo il passaggio 1 potrebbe verificarsi un altro upsert, quindi il codice deve verificare l'errore della chiave duplicata e quindi riprovare l'upsert se ciò si verifica. A quel punto conosci il documento con quel _id
esiste quindi avrà sempre successo.
Ad esempio:
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);
}
}
});
Vedi qui per la relativa documentazione.
Potresti ancora chiederti perché questo può accadere se l'inserto è atomico, ma ciò significa che non si verificheranno aggiornamenti sul documento inserito fino a quando non sarà completamente scritto, non che nessun altro inserto di un documento con lo stesso _id
può verificarsi.
Inoltre, non è necessario creare manualmente un indice su _id
poiché tutte le raccolte MongoDB hanno un indice univoco su _id
indipendentemente. Quindi puoi rimuovere questa riga:
monitorSchema.index({_id: -1}); // Not needed