node-continuation-local-storage icon indicating copy to clipboard operation
node-continuation-local-storage copied to clipboard

Lost context inside Mongoose query callback function

Open airicyu opened this issue 9 years ago • 5 comments

Using CLS with Mongoose is broken inside query callback function. The context is lost.

sample:

console.log("### outside mongoose query", ns.get("foo"));
SomeMongooseModel.find(query, function(err, obj){
   console.log("### inside mongoose query", ns.get("foo"));
});

problem: The problem is that the outside query code can get the variable from CLS namespace, while the inside query callback function cannot get the variable. It apparently lost the context.

temp-solution: I find that explicitly call namespace bind to wrap the callback function can be workaround:

console.log("### outside mongoose query", ns.get("foo"));
SomeMongooseModel.find(query, ns.bind(function(err, obj){
   console.log("### inside mongoose query", ns.get("foo"));
}));

However, this temp-solution require explicit call to wrap the callback function which is ugly and not easy to maintain in future.

supplymentary information: Node version: v4.2.4 CLS version: 3.1.6 Mongoose version: 4.4.3

Are there any cleaner solution for this issue?

airicyu avatar Feb 11 '16 03:02 airicyu

It's a queueing problem. You get that with user-mode queueing/pooling mechanisms. You'll need to patch the function before it enters the queue/pool.

Try this:

var addQueue = mongoose.Collection.prototype.addQueue
mongoose.Collection.prototype.addQueue = function (name, args) {
  var last = args[args.length - 1]
  if (typeof last === 'function') {
    args[args.length - 1] = ns.bind(last)
  }
  return addQueue.call(this, name, args)
}

Qard avatar Feb 12 '16 01:02 Qard

Hi Qard, your fix is not work. I checked that the addQueue function has never been called in my code at runtime.

airicyu avatar Feb 12 '16 02:02 airicyu

Finally make the workaround work.

var find = mongoose.Query.prototype.find;
mongoose.Query.prototype.find = function () {
  var args = arguments;
  var last = args[args.length - 1];
  if (typeof last === 'function') {
    args[args.length - 1] = namespace.bind(last);
  }  
  return find.apply(this, args);
}

By using some code like this, I can wrap the "find" method to get the CLS work inside my callback function.

However, it is still a kind of ugly workaround which I explicitly inject into. I still hope that CLS would have a better solution for similar use cases in some later release. Thanks!

airicyu avatar Feb 12 '16 07:02 airicyu

CLS itself only focuses on supporting node core. There are various plugins to patch some userland modules which break due to queueing/pooling: https://www.npmjs.com/browse/keyword/continuation-local-storage

Sadly, there's not really a reliable way to automatically propagate the context.

Qard avatar Feb 12 '16 21:02 Qard

I just ran into the same issue. I resolved it simply by switching from callback functions to arrow functions. I.e. from SomeMongooseModel.find(query, function(err, obj){ console.log("### inside mongoose query", ns.get("foo")); }); to SomeMongooseModel.find(query, (err, obj) => { console.log("### inside mongoose query", ns.get("foo")); }); Note: No idea why line breaks in the code blocks are ignored. Sorry!

cwienands avatar Dec 14 '17 22:12 cwienands