MongoDB Node.js native driver silently swallows `bulkWrite` exception

后端 未结 1 715
既然无缘
既然无缘 2020-12-11 09:11

The script below has a bug in the mongo bulkWrite op syntax: $setOnInsert: { count:0 }, is unnecessary and thus mongo throws an exception: \"Cannot update \'cou

相关标签:
1条回答
  • 2020-12-11 09:45

    So as commented, "It's a bug". Specifically the bug is right here:

     // Return a Promise
      return new this.s.promiseLibrary(function(resolve, reject) {
        bulkWrite(self, operations, options, function(err, r) {
          if(err && r == null) return reject(err);
          resolve(r);
        });
      });
    

    The problem is that the "response" ( or r ) in the callback which is being wrapped in a Promise is not actually null, and therefore despite the error being present the condition is therefore not true and reject(err) is not being called, but rather the resolve(r) is being sent and hence this is not considered an exception.

    Correcting would need some triage, but you can either 'work around' as mentioned by inspecting the writeErrors property in the response from the current bulkWrite() implementation or consider one of the other alternatives as:

    Using the Bulk API methods directly:

    const MongoClient = require('mongodb').MongoClient,
          uri  = 'mongodb://localhost:27017/myNewDb';
    
    (async () => {
    
      let db;
    
      try {
    
        db = await MongoClient.connect(uri);
    
        let bulk = db.collection('myNewCollection').initializeOrderedBulkOp();
    
        bulk.find({ foo: 'bar' }).upsert().updateOne({
          $setOnInsert: { count: 0 },
          $inc: { count: 0 }
        });
    
        let result = await bulk.execute();
        console.log(JSON.stringify(result,undefined,2));
    
      } catch(e) {
        console.error(e);
      } finally {
        db.close();
      }
    
    })();
    

    Perfectly fine but of course has the issue of not naturally regressing on server implementations without Bulk API support to using the legacy API methods instead.

    Wrapping the Promise Manually

    (async () => {
    
      let db = await require('mongodb').MongoClient.connect('mongodb://localhost:27017/myNewDb');
    
      let mongoOps = [{
        updateOne: {
          filter: { foo: "bar" },
          update: {
            $setOnInsert: { count:0 },
            $inc: { count:1 },
          },
          upsert: true,
        }
      }];
    
      try {
        let result = await new Promise((resolve,reject) => {
    
          db.collection("myNewCollection").bulkWrite(mongoOps, (err,r) => {
            if (err) reject(err);
            resolve(r);
          });
        });
        console.log(JSON.stringify(result,undefined,2));
        console.log("Success!");
      } catch(e) {
        console.log("Failed:");
        console.log(e);
      }
    
    })();
    

    As noted the problem lies within the implementation of how bulkWrite() is returning as a Promise. So instead you can code with the callback() form and do your own Promise wrapping in order to act how you expect it to.

    Again as noted, needs a JIRA issue and Triage to which is the correct way to handle the exceptions. But hopefully gets resolved soon. In the meantime, pick an approach from above.

    0 讨论(0)
提交回复
热议问题