node-continuation-local-storage
node-continuation-local-storage copied to clipboard
Lost context inside Mongoose query callback function
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?
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)
}
Hi Qard, your fix is not work. I checked that the addQueue function has never been called in my code at runtime.
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!
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.
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!