Một nâng cấp dẫn đến việc chèn tài liệu không phải là một hoạt động hoàn toàn nguyên tử. Hãy coi việc nâng cấp giống như thực hiện các bước rời rạc sau:
- Truy vấn tài liệu đã xác định để nâng cấp.
- Nếu tài liệu tồn tại, hãy cập nhật nguyên bản tài liệu hiện có.
- Nếu không (tài liệu không tồn tại), hãy chèn nguyên bản một tài liệu mới kết hợp các trường truy vấn và cập nhật.
Vì vậy, các bước 2 và 3 là mỗi nguyên tử, nhưng một lần nâng cấp khác có thể xảy ra sau bước 1, vì vậy mã của bạn cần phải kiểm tra lỗi khóa trùng lặp và sau đó thử lại phép bổ sung nếu điều đó xảy ra. Tại thời điểm đó, bạn biết tài liệu với _id
đó tồn tại để nó luôn thành công.
Ví dụ:
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);
}
}
});
Xem tài liệu liên quan tại đây.
Bạn vẫn có thể thắc mắc tại sao điều này có thể xảy ra nếu phần chèn là nguyên tử, nhưng điều đó có nghĩa là không có cập nhật nào sẽ xảy ra trên tài liệu được chèn cho đến khi nó được viết hoàn chỉnh, chứ không phải là không có phần chèn nào khác của tài liệu có cùng _id
có thể xảy ra.
Ngoài ra, bạn không cần phải tạo chỉ mục theo cách thủ công trên _id
vì tất cả các bộ sưu tập MongoDB đều có một chỉ mục duy nhất trên _id
bất kể. Vì vậy, bạn có thể xóa dòng này:
monitorSchema.index({_id: -1}); // Not needed