2016-05-18 3 views
4

У меня проблема с дублирующимся ключом. Долгое время не может найти ответ. Пожалуйста, помогите мне решить эту проблему или объяснить, почему я получаю повторяющуюся ключевую ошибку.Ошибка повторения ключа Mongoose с upsert

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) 

но в мониторе я использую 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 
}); 

индекс

monitorSchema.index({_id: -1}); 
Monitor = db.model('Monitor', monitorSchema); 

и увеличение свойством

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); 
     } 
    }); 
}; 

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)); 
}; 
+1

Не пытаться управлять «_id» по своему усмотрению, если это не очень очень надо ... –

+0

В моей задаче это очень и очень необходимо. – cheks

+0

Никто не знает, почему я получаю эту ошибку? – cheks

ответ

10

Ускорение, которое приводит к вставке документа, не является полностью атомной операцией. Подумайте об этом, выполнив следующие дискретные шаги:

  1. Запрос на обнаруженный документ для подтверждения.
  2. Если документ существует, выполните атомарное обновление существующего документа.
  3. Else (документ не существует), атомарно вставьте новый документ, который включает в себя поля запроса и обновления.

Таким образом, шаги 2 и 3 являются атомарными, но после первого шага может произойти другое повышение, поэтому вашему коду необходимо проверить дублируемую ошибку ключа, а затем повторить попытку, если это произойдет. В этот момент вы знаете документ с тем, что существует _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); 
     } 
    } 
}); 

См here для соответствующей документации.

Возможно, вам все еще интересно, почему это может произойти, если вставка является атомарной, но это означает, что обновления не будут вставлены в вставленном документе до тех пор, пока они не будут полностью записаны, а не никакая другая вставка документа с тем же _id.

Кроме того, вам не нужно вручную создавать индекс на _id, так как все коллекции MongoDB имеют уникальный индекс на _id независимо. Таким образом, вы можете удалить эту строку:

monitorSchema.index({_id: -1}); // Not needed 
+0

Вы хотите сказать, мне нужно изменить .update methond, чтобы найти + update \ insert? – cheks

+0

@cheks Нет, я говорю, что это те шаги, которые выполняет «обновление» upsert. Вот почему вы не можете избежать возможности попадания в дубликат ключевой ошибки, поэтому вам нужно проверить наличие ошибки и восстановить ее в своем коде. – JohnnyHK

+0

Я думаю, что раньше, но я не уверен, что это лучший способ. Благодаря! – cheks

0

Автоматически уникальный индекс будет создан из _id области.

Если вы управляете _id по custom value или управляющим mongodb ObjectId в поле _id.

Вы пытаетесь ввести запись с _id: 24392490, которая уже существует. Итак, хотя вы использовали upsert, это не позволит, потому что _id - уникальный индекс.

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

В соответствии с вашей реализации для генерации _ID, это позволит insertation только 1 запись в 1 - 1,5 минут (ровно одну минуту, если раунд не используется).

+0

Я думаю, вы не знаете, как работает обновление с upsert ... теперь у меня есть более 5000 обновлений в минуту! но некоторое время я получаю двойную ключевую ошибку. – cheks

 Смежные вопросы

  • Нет связанных вопросов^_^