-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Post hooks called outside request context #10478
Comments
Try the workaround in this comment: #7292 (comment) . There is some unfortunate issues with how CLS libraries handle Node's TCP sockets that we haven't been able to work around. |
I was still able to repro this issue in Mongoose 8: const Koa = require('koa');
const mongoose = require('mongoose');
const { v4: uuidv4 } = require('uuid');
const cls = require('node-cls');
const app = new Koa();
async function contextMiddleware(ctx, next) {
const context = cls.create('request-context');
context.id = uuidv4();
context.ctx = ctx;
await context.start();
await next();
cls.exit('request-context');
}
app.use(contextMiddleware);
mongoose.connect('mongodb://localhost:27017/koa-mongoose');
const TestSchema = new mongoose.Schema({ name: String });
// Add pre hook (context will be available here)
TestSchema.pre('insertMany', function (next) {
const context = cls.get('request-context');
if (context) {
console.log('Pre Hook - Context ID:', context.id);
}
next();
});
// Add post hook (context may not be available here)
TestSchema.post('insertMany', function (docs, next) {
const context = cls.get('request-context');
if (context) {
console.log('Post Hook - Context ID:', context.id);
} else {
console.log('Post Hook - Context not available');
}
next();
});
const TestModel = mongoose.model('Test', TestSchema);
// Define a route to trigger the insertMany operation
app.use(async (ctx) => {
try {
await TestModel.insertMany([{ name: 'Test1' }, { name: 'Test2' }]);
ctx.body = 'Documents inserted';
} catch (err) {
ctx.body = 'Error inserting documents';
console.error(err);
}
});
// Start the server
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
}); Output:
As a workaround, you can use Node's built-in AsyncLocalStorage, which does work: const Koa = require('koa');
const mongoose = require('mongoose');
const { v4: uuidv4 } = require('uuid');
const { AsyncLocalStorage } = require('async_hooks');
const app = new Koa();
const asyncLocalStorage = new AsyncLocalStorage();
async function contextMiddleware(ctx, next) {
const context = { id: uuidv4(), ctx };
// Run each request within an AsyncLocalStorage context
await asyncLocalStorage.run(context, async () => {
await next();
});
}
app.use(contextMiddleware);
mongoose.connect('mongodb://localhost:27017/test');
const testSchema = new mongoose.Schema({ name: String });
// Pre hook: Accessing context data
testSchema.pre('insertMany', function (next) {
const context = asyncLocalStorage.getStore();
if (context) {
console.log('Pre Hook - Request ID:', context.id);
} else {
console.log('Pre Hook - No context found');
}
next();
});
// Post hook: Accessing context data
testSchema.post('insertMany', function (docs, next) {
const context = asyncLocalStorage.getStore();
if (context) {
console.log('Post Hook - Request ID:', context.id);
} else {
console.log('Post Hook - No context found');
}
next();
});
const TestModel = mongoose.model('Test', testSchema);
// Route to test insertMany
app.use(async (ctx) => {
await TestModel.insertMany([{ name: 'Test 1' }, { name: 'Test 2' }]);
ctx.body = 'Data inserted';
});
// Start the Koa server
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Output:
We will investigate and see if there's anything we can do re: node-cls |
I did some digging and the same issue from 2021 persists: node-cls simply cannot retain context when TCP streams are involved. For example, context is defined here but lost here. Deep in the internals of the MongoDB Node driver where it handles writing to socket and then reading back off the socket. I'm going to close this issue, I don't think it is worth digging any further since Node's built-in AsyncLocalStorage works as expected and offers a fairly straightforward alternative. |
We use koajs as backend server. Therefore I used
node-cls
and wrote a small middleware to access context related data:In pre hooks I can access the context data with
const context = cls.get('request-context');
as expected (but only if it's the first line inside the middleware function, otherwise some async calls are already finished and destroyed their context on the stack and the chain is broken). This issue I can handle.But when I use post hooks, the stack chain refers to outside the context. To trace the issue, I modified the
getContext(ns)
function from `node-cls:To test I have the following middleware:
When making a request, using the insertMany() method on a model an error is thrown
TypeError: Cannot read property 'id' of undefined
because the context is unknown.In my test the stack looks like this:
So the
request-context
is stored in entry 371.The debug log from
node-cls
shows:It seems that the post hooks are called from a separate async chain. Is there a way to fix that?
The text was updated successfully, but these errors were encountered: