简体   繁体   English

MongoDB Node.js本机驱动程序默默地吞下`bulkWrite`异常

[英]MongoDB Node.js native driver silently swallows `bulkWrite` exception

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 'count' and 'count' at the same time". 下面的脚本在mongo bulkWrite op语法中有一个错误: $setOnInsert: { count:0 },是不必要的,因此mongo抛出异常:“无法同时更新'count'和'count'”。

The problem is, the node.js driver doesn't seem to catch it. 问题是,node.js驱动程序似乎没有捕获它。 This script logs "Success!" 此脚本记录“成功!” to the console. 到控制台。

(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 {
    await db.collection("myNewCollection").bulkWrite(mongoOps);
    console.log("Success!");
  } catch(e) {
    console.log("Failed:");
    console.log(e);
  }

})();

Examining db.system.profile.find({}) with db.setProfileLevel(2) we can see the exception: 使用db.setProfileLevel(2)检查db.system.profile.find({}) ,我们可以看到异常:

{
    "op" : "update",
    "ns" : "myNewDb.myNewCollection",
    "query" : {
        "foo" : "bar"
    },
    "updateobj" : {
        "$setOnInsert" : {
            "count" : 0
        },
        "$inc" : {
            "count" : 1
        }
    },
    "keyUpdates" : 0,
    "writeConflicts" : 0,
    "numYield" : 0,
    "locks" : {
        "Global" : {
            "acquireCount" : {
                "r" : NumberLong(1),
                "w" : NumberLong(1)
            }
        },
        "Database" : {
            "acquireCount" : {
                "w" : NumberLong(1)
            }
        },
        "Collection" : {
            "acquireCount" : {
                "w" : NumberLong(1)
            }
        }
    },
    "exception" : "Cannot update 'count' and 'count' at the same time",
    "exceptionCode" : 16836,
    "millis" : 0,
    "execStats" : {},
    "ts" : ISODate("2017-10-12T01:57:03.008Z"),
    "client" : "127.0.0.1",
    "allUsers" : [],
    "user" : ""
}

Why is the driver swallowing errors like this? 为什么司机吞下这样的错误? I definitely seems like a bug, but I figured I'd ask here first just to be sure. 我看起来肯定是一个错误,但我想我先问这里只是为了确定。

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. 问题是回绕中包含在Promise中的“响应”(或r )实际上并不为null ,因此尽管存在错误,但条件不是true并且没有调用reject(err) ,但是resolve(r)正在被发送,因此这不被视为例外。

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: 修正需要一些分类,但你可以通过检查当前bulkWrite()实现的响应中的writeErrors属性来“解决”,或者考虑以下其他选择之一:

Using the Bulk API methods directly: 直接使用批量API方法:

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. 非常好但当然有一个问题,即如果没有批量API支持使用传统API方法,服务器实现就不会自然消退。

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 . 如前所述,问题在于bulkWrite()作为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. 因此,您可以使用callback()表单进行编码,并执行自己的Promise包装,以便按照您的预期行事。

Again as noted, needs a JIRA issue and Triage to which is the correct way to handle the exceptions. 如上所述,需要一个JIRA问题和Triage,这是处理异常的正确方法。 But hopefully gets resolved soon. 但希望很快得到解决。 In the meantime, pick an approach from above. 与此同时,从上面选择一种方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM