使用upsert进行Mongoose重复键错误

che*_*eks 7 duplicates mongoose mongodb node.js

我有重复键的问题.好久不能找到答案.请帮我解决这个问题或解释为什么我会得到重复的密钥错误.

Trace: { [MongoError: E11000 duplicate key error collection: project.monitor index: _id_ dup key: { : 24392490 }]
name: 'MongoError',
message: 'E11000 duplicate key error collection: project.monitor index: _id_ dup key: { : 24392490 }',
driver: true,
index: 0,
code: 11000,
errmsg: 'E11000 duplicate key error collection: project.monitor index: _id_ dup key: { : 24392490 }' }
at /home/project/app/lib/monitor.js:67:12
at callback (/home/project/app/node_modules/mongoose/lib/query.js:2029:9)
at Immediate.<anonymous> (/home/project/app/node_modules/kareem/index.js:160:11)
at Immediate._onImmediate (/home/project/app/node_modules/mquery/lib/utils.js:137:16)
at processImmediate [as _immediateCallback] (timers.js:368:17)
Run Code Online (Sandbox Code Playgroud)

但在监视器中我使用upsert,为什么我会得到重复的错误?

monitor.js:62-70

监控架构

var monitorSchema = db.Schema({
   _id      : {type: Number, default: utils.minute},
   maxTicks : {type: Number, default: 0},
   ticks    : {type: Number, default: 0},
   memory   : {type: Number, default: 0},
   cpu      : {type: Number, default: 0},
   reboot   : {type: Number, default: 0},
streams  : db.Schema.Types.Mixed
}, {
collection: 'monitor',
strict: false
});
Run Code Online (Sandbox Code Playgroud)

指数

monitorSchema.index({_id: -1});
Monitor = db.model('Monitor', monitorSchema);
Run Code Online (Sandbox Code Playgroud)

并按财产增加

exports.increase = function (property, incr) {
    var update = {};
    update[property] = utils.parseRound(incr) || 1;
    Monitor.update({_id: utils.minute()}, {$inc: update}, {upsert: true}, function (err) {
        if (err) {
            console.trace(err);
        }
    });
};
Run Code Online (Sandbox Code Playgroud)

utils.js

exports.minute = function () {
    return Math.round(Date.now() / 60000);
};

exports.parseRound = function (num, round) {
    if (isNaN(num)) return 0;
    return Number(parseFloat(Number(num)).toFixed(round));
};
Run Code Online (Sandbox Code Playgroud)

Joh*_*yHK 21

导致文档插入的upsert不是完全原子操作.将upsert视为执行以下离散步骤:

  1. 查询要标识的已标识文档.
  2. 如果文档存在,则以原子方式更新现有文档.
  3. 否则(该文档不存在),自动插入包含查询字段和更新的新文档.

因此,步骤2和3都是原子的,但是在步骤1之后可能会发生另一次upsert,因此您的代码需要检查重复的键错误,然后在发生这种情况时重试upsert.那时你就知道文件_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);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

请参阅此处获取相关文档.

您可能仍然想知道如果插入是原子的,为什么会发生这种情况,但这意味着在插入的文档完全写入之前不会发生更新,而不是没有其他插入的文档_id可能会发生.

此外,您无需手动创建索引,_id因为所有MongoDB集合都具有唯一索引_id.所以你可以删除这一行:

monitorSchema.index({_id: -1}); // Not needed
Run Code Online (Sandbox Code Playgroud)