zLib memory retain due to references to Request Object
Hi Team,
I am currently tracking a memory leak where zLib references are being retained and never released. We see this via memory snapshot taken by chrome. Deep inspection of these zlib references show they are request / response releated.
We are injecting CLS_REQ to get access to the Request object and we are not saving the request anywhere. The more we do this the more zLib increases.
zLib is involved here because we use the compression middleware for express.
I recently updated the code to do the following -
ClsModule.forRoot({
global: true,
middleware: {
saveReq: false,
mount: true,
setup(cls, req, res) {
cls.set(CLS_REQ, {
user: req.user,
role: req.role,
headers: {
tenant: req.headers?.tenant,
parentid: req.headers?.parentid,
authorization: req.headers?.authorization,
host: req.headers?.host,
nonce: req.headers?.nonce,
'x-correlationid': req.headers?.['x-correlationid'],
'x-forwarded-for': req.headers?.['x-forwarded-for'],
'x-real-ip': req.headers?.['x-real-ip'],
'cf-connecting-ip': req.headers?.['cf-connecting-ip'],
},
ip: req.ip ?? req.socket?.remoteAddress ?? req.connection?.remoteAddress,
connectOptions: req.connectOptions,
});
},
},
}),
Is this expected behaviour of saveReq: true?
Kind Regards, Tarek
saveReq: true casues the Req object to be stored in the CLS store object (AsyncLocalStorage), so it can be later retrieved through cls.get(CLS_REQ) or via the corresponding Proxy provider (which internally calls the former at access-time)
A memory leak could happen, if you bind any listeners to the request object and never remove them (e.g. via req.on('finish'), but that's not related to nestjs-cls), or if you reference the CLS store in any persistent variable that outlives the request lifetime.
Does the leak disappear if you don't use saveReq: true? If so, that could indicate a problem with this library, otherwise it's likely caused by your setup.
Hi,
I am using the data in my example above.
When I do that is the zLib leaks disappear.
Hm, that is interesting to hear.
I see that you're only saving a subset of the Request object to CLS instead of the entire thing.
I have no idea what could cause this inside nestjs-cls though. If you look into the source code of ClsMiddleware, you'll see that the only difference is that the line cls.set(CLS_REQ, request) is skipped, but instead your code runs in the setup method.
Did you find any property on the request object (that you explicitly omit here) which might cause it?
Could you perhaps share a minimal reproduction where I could test the behavior locally?