Un upsert qui se traduit par une insertion de document n'est pas une opération entièrement atomique. Considérez l'upsert comme effectuant les étapes discrètes suivantes :
- Requête pour le document identifié à mettre à jour.
- Si le document existe, mettre à jour le document existant de manière atomique.
- Sinon (le document n'existe pas), insérez de manière atomique un nouveau document qui intègre les champs de requête et la mise à jour.
Ainsi, les étapes 2 et 3 sont chacune atomiques, mais un autre upsert peut se produire après l'étape 1. Votre code doit donc vérifier l'erreur de clé en double, puis réessayer l'upsert si cela se produit. À ce stade, vous connaissez le document avec ce _id
existe donc il réussira toujours.
Par exemple :
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);
}
}
});
Voir ici pour la documentation associée.
Vous vous demandez peut-être encore pourquoi cela peut arriver si l'insertion est atomique, mais cela signifie qu'aucune mise à jour ne se produira sur le document inséré jusqu'à ce qu'il soit complètement écrit, pas qu'aucune autre insertion d'un document avec le même _id
peut se produire.
De plus, vous n'avez pas besoin de créer manuellement un index sur _id
car toutes les collections MongoDB ont un index unique sur _id
indépendamment. Vous pouvez donc supprimer cette ligne :
monitorSchema.index({_id: -1}); // Not needed