Post-hooks do not work with AsyncLocalStorage
See original GitHub issueIssue: Bug Behaviour:
When running a Mongoose query inside of an AsyncLocalStorage context the correct context is not picked up inside post
-type hooks. But it is correctly picked up inside pre
-type hooks.
Reproduction
I couldn’t load this reproduction into REPL (it didn’t like installing mongoose memory server). So I’ve copied and pasted below.
This script creates two models: Model
and TwinModel
. After Model
has saved (post-save hook) it saves aTwinModel
that duplicates its data. Both models use a post-save hook to log the ids of their respective documents into an AsyncLocalStorage context.
The script then runs two functions within that same AsyncLocalStorage context. It then logs the result of that context. The expectation is that each context should have references to the two models (one Model
and one TwinModel
created within those contexts).
// package.json
{
"dependencies": {
"mongodb-memory-server": "^6.9.6",
"mongoose": "^5.12.0"
}
}
// index.js
// @ts-check
const mongoose = require("mongoose")
const { MongoMemoryServer} = require("mongodb-memory-server");
const { AsyncLocalStorage } = require("async_hooks");
(async () => {
/**
* SETUP CTX
*/
const CTX = new AsyncLocalStorage();
/**
* SETUP DATABASE
*/
const mongod = new MongoMemoryServer();
const uri = await mongod.getUri();
/**
* CONNECT MONGOOSE
*/
await mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
/**
* CREATE SCHEMAS
*/
const schema = new mongoose.Schema({ name: String });
const twinSchema = new mongoose.Schema({ twins: String });
// Hook that pushes ID of doc to `CTX`
const pushToCtx = function () {
const name =this.name || this.twins;
const type = this.name ? 'Obj' : 'Twin';
const id = this.id.slice(-4);
console.log(`Pushing ${type} ${name} ${id} to CTX`);
const store = CTX.getStore();
store.ids = (store.ids || []).concat(id);
};
schema.post('save', function () {
return new TwinModel({ twins: this.name }).save();
});
schema.post('save', pushToCtx);
twinSchema.post('save', pushToCtx);
const Model = mongoose.model('Model', schema);
const TwinModel = mongoose.model('TwinModel', twinSchema);
// TEST
await CTX.run({ ctxId: 'a'}, async () => {
const a = new Model({ name: 'a' });
await a.save();
const store = CTX.getStore();
console.log("A", store);
})
await CTX.run({ ctxId: 'b'}, async () => {
const b = new Model({ name: 'b' });
await b.save();
const store = CTX.getStore();
console.log("B", store);
});
process.exit()
})()
Expected Behaviour
When I run a Mongoose query inside of an AsyncLocalStorage context I can retrieve the correct context from inside any of the downstream function calls including any Mongoose hooks.
Versions
Node: v12.21.0 / v14.8.0 Mongoose: 5.12.0
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:10 (4 by maintainers)
I’ve been struggling with this today.
Trying to add instrumentation via a plugin, and I need to pass some state to the
post
hook but it gets lost.@jonathan-wilkinson have you been able to find any workaround that doesn’t involve patching mongoose?
Edit: Ah, just thought of this now, and it solves my problem:
The
pre
hook works, so I can set something likethis._context = storage.getStore();
and then I can read it fromthis._context
in thepost
hook.We’ll have to look into how async-local-storage works. We considered converting to using promises under the hood for a similar issue with a different npm module in #7292 , but decided against it since we found an easy fix. We’ll see if we can do something similar here.