Ускорение, которое приводит к вставке документа, не является полностью атомной операцией. Подумайте о том, что upsert выполняет следующие дискретные шаги:
Таким образом, шаги 2 и 3 являются атомарными, но другое upsert может произойти после шага 1, поэтому вашему коду нужно проверить наличие дублированной ключевой ошибки, а затем повторить попытку, если это произойдет. В этот момент вы знаете документ с тем, что _id
существует, поэтому он всегда будет успешным.
Например:
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);
}
}
});
См. здесь для связанных с документацией.
Вы все еще можете задаться вопросом, почему это может произойти, если вставка является атомарной, но это означает, что обновления не будут вставлены в вставленном документе до тех пор, пока они не будут полностью записаны, а не что никакая другая вставка из документа с тем же _id
.
Кроме того, вам не нужно вручную создавать индекс на _id
, поскольку все коллекции MongoDB имеют уникальный индекс на _id
независимо. Таким образом, вы можете удалить эту строку:
monitorSchema.index({_id: -1}); // Not needed
Вы можете сделать это легко с помощью запроса ниже. вам не нужно вкладывать в это столько логики.
select column_name from table_name order by RAND() limit 1