Uploaded image for project: 'Node.js Driver'
  1. Node.js Driver
  2. NODE-2308

Bulk write error returns incorrect index in WriteError for unordered writes

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 3.3.5, 3.4.0
    • Affects Version/s: 3.3.3
    • Component/s: None
    • None

      When performing a bulk write, if there is a BulkWriteError thrown (this is for MongoDB 4.2 and above), the index of the "WriteError" in the error response can be incorrect depending on the order of operations in the bulk write:

      If I set up a collection like this in the shell:

      db.mycoll.drop()
      db.mycoll.insert(Array.from({length: 4}, (_, i) => ({_id: i, a: i})))
      db.mycoll.createIndex({a: 1}, {unique: true})
      

      and run this Node.js code sample (courtesy of max.hirschhorn):
       

      const util = require('util');
      ​
      const mongodb = require('mongodb');
      ​
      async function main() {
          const client = new mongodb.MongoClient('mongodb://localhost:26000/?replicaSet=replset');
          await client.connect();
      ​
          const collection = client.db('test').collection('mycoll');
      ​
          try {
              const res = await collection.bulkWrite([
                  {insertOne: {_id: 5, a: 0}},
                  {updateOne: {filter: {_id: 1}, update: {$set: {a: 0}}}},
                  {insertOne: {_id: 6, a: 0}},
                  {updateOne: {filter: {_id: 2}, update: {$set: {a: 0}}}},
              ], {ordered: false});
      ​
              console.log(util.inspect(res, {depth: null, maxArrayLength: null}));
          } catch (err) {
              console.log(util.inspect(err, {depth: null, maxArrayLength: null}));
          }
      }
      ​
      main().then(() => {
          process.exit(0);
      }).catch(err => {
          console.error(err.stack);
          process.exit(1);
      });
      

       
      I'll get the following BulkWriteError response:

      { MongoError: E11000 duplicate key error collection: test.mycoll index: a_1 dup key: { a: 0 }
          at Function.create (/Users/adamchel/realm/adapt-69-pg/node_modules/mongodb/lib/core/error.js:44:12)
          at toError (/Users/adamchel/realm/adapt-69-pg/node_modules/mongodb/lib/utils.js:150:22)
          at UnorderedBulkOperation.handleWriteError (/Users/adamchel/realm/adapt-69-pg/node_modules/mongodb/lib/bulk/common.js:1202:11)
          at resultHandler (/Users/adamchel/realm/adapt-69-pg/node_modules/mongodb/lib/bulk/common.js:519:23)
          at handler (/Users/adamchel/realm/adapt-69-pg/node_modules/mongodb/lib/core/topologies/replset.js:1198:22)
          at /Users/adamchel/realm/adapt-69-pg/node_modules/mongodb/lib/core/connection/pool.js:408:18
          at process._tickCallback (internal/process/next_tick.js:61:11)
        name: 'BulkWriteError',
        driver: true,
        code: 11000,
        writeErrors:
         [ WriteError {
             err:
              { index: 0,
                code: 11000,
                errmsg:
                 'E11000 duplicate key error collection: test.mycoll index: a_1 dup key: { a: 0 }',
                op: { _id: 5, a: 0 } } },
           WriteError {
             err:
              { index: 1,
                code: 11000,
                errmsg:
                 'E11000 duplicate key error collection: test.mycoll index: a_1 dup key: { a: 0 }',
                op: { _id: 6, a: 0 } } } ],
        result:
         BulkWriteResult {
           result:
            { ok: 1,
              writeErrors:
               [ WriteError {
                   err:
                    { index: 0,
                      code: 11000,
                      errmsg:
                       'E11000 duplicate key error collection: test.mycoll index: a_1 dup key: { a: 0 }',
                      op: { _id: 5, a: 0 } } },
                 WriteError {
                   err:
                    { index: 1,
                      code: 11000,
                      errmsg:
                       'E11000 duplicate key error collection: test.mycoll index: a_1 dup key: { a: 0 }',
                      op: { _id: 6, a: 0 } } } ],
              writeConcernErrors: [],
              insertedIds: [ { index: 0, _id: 5 }, { index: 1, _id: 6 } ],
              nInserted: 0,
              nUpserted: 0,
              nMatched: 0,
              nModified: 0,
              nRemoved: 0,
              upserted: [],
              lastOp:
               { ts:
                  Timestamp { _bsontype: 'Timestamp', low_: 1, high_: 1573053381 },
                 t: 1 } } },
        [Symbol(mongoErrorContextSymbol)]: {} }
      

      If you look at the writeErrors in either the result or the root object, you'll notice that the first write failure (for the insert of document with _id 5) has the correct index of 0, but the second write failure (for the insert of document with _id 6) has the index of 1, which is not correct. It is likely the index in the response from the server for that particular sub-batch of operations, but it is not the correct index in the array that was passed to the driver. I would expect the index to be 2, since the bulk write looked like:

              const res = await collection.bulkWrite([
                  {insertOne: {_id: 5, a: 0}},
                  {updateOne: {filter: {_id: 1}, update: {$set: {a: 0}}}},
                  {insertOne: {_id: 6, a: 0}}, // this is the write that failed with index 1 instead of 2
                  {updateOne: {filter: {_id: 2}, update: {$set: {a: 0}}}},
              ], {ordered: false});
      

      I believe the incorrect logic lies here: https://github.com/mongodb/node-mongodb-native/blob/v3.3.2/lib/bulk/common.js#L460

      We can currently work around this issue by always doing ordered writes, but we would like to have the performance benefit of minimizing round trips with unordered writes.

            Assignee:
            matt.broadstone@mongodb.com Matt Broadstone
            Reporter:
            adam.chelminski@mongodb.com Adam Chelminski (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: