简体   繁体   中英

Need help identifying memory leak using express in node

The problem

I have a Node application running in ECS that seems to be leaking memory, as the memory is constantly growing and dropping at every deploy.

I made a heapdump and imported it in Chrome DevTools, trying to make some sense of it.

Heapdump

There seems to be several 100 000 strings looking like this. The syntax looks like the express res.cookie syntax I have in my middleware, and also of course in the login route. The middleware I have that is checking if the user has a valid cookie, and refreshing it if valid. Since it would be running more often I think it makes sense if the middleware is the culprit, but I still can't make any sense of why it is leaking. 对象

The possible culprit? Middleware.ts

import * as jwt from 'jsonwebtoken';
import issueJWT from 'utils/functions/IssueToken';
import { getRepository, getConnection } from 'typeorm';
import User from 'entity/organization/User';
import { ApiResult } from 'utils/classes/ApiResult';

interface ICookie {
    name: string;
    payload: string;
}

interface ICookieRouterConnections {
    routeName: string;
    cookieName: string;
}

const authenticate = async (req, res, next) => {
    const userRepository = getRepository(User);
    const { authorization, cookie } = req.headers;
    let requestToken: ICookie = { name: '', payload: '' };

    // These are the tokens that can be issued with their corresponding routes
    const portalCookieNames: ICookieRouterConnections[] = [
        {
            routeName: 'admin',
            cookieName: process.env.JWT_ADMIN,
        },
        {
            routeName: 'customer',
            cookieName: process.env.JWT_CUSTOMER,
        },
    ];

    // Find the router the user is trying to access
    const requestedRouterName = req.path
        .split('/')
        .splice(1, 1)
        .shift();

    function throwError(statusObj: { message: string; code: string; statusCode: number }) {
        const apiResult = new ApiResult();
        apiResult.addError({
            message: statusObj.message,
            code: statusObj.code,
            request: req,
        });
        res.status(statusObj.statusCode);
        res.send(apiResult);
    }

    // Gets the value of a cookie by the cookie name
    const getCookie = (cookieName: string): ICookie => {
        const returnObject = {
            name: cookieName,
            payload: cookie
                .split(`${cookieName}=`)
                .pop()
                .split(';')
                .shift(),
        };
        return returnObject;
    };

    const routerCookieName = portalCookieNames.find(obj => obj.routeName === requestedRouterName).cookieName;

    if (!authorization && !cookie) {
        throwError({ message: 'Token not provided', statusCode: 401, code: 'tokenNotProvided' });
        return;
    }

    // If there is authorization header, use that
    if (authorization) {
        requestToken.name = routerCookieName;
        requestToken.payload = authorization.replace('Bearer ', '');
    } else {
        // If not use the JWT from the cookie based on which router the user is trying to access
        requestToken = getCookie(routerCookieName);
    }

    if (requestToken) {
        // Validate the token
        jwt.verify(requestToken.payload, process.env.APP_SECRET, async (err, decoded) => {
            if (err) {
                throwError({ message: err, statusCode: 401, code: 'internalError' });
            } else {
                const user = await userRepository.findOne({ where: { id: decoded.userId } });

                if (!user.isDeactivated) {
                    req.user = user;
                    // Issue a new token if the cookie provided was valid
                    res.cookie(requestToken.name, issueJWT(user), {
                        httpOnly: false,
                        expires: new Date(Date.now() + 24 * 3600000),
                        domain: process.env.COOKIE_DOMAIN,
                    });
                    next();
                }
            }
        });
    } else {
        throwError({ message: 'Invalid token', statusCode: 401, code: 'invalidTokenProvided' });
    }
};

export default authenticate;


I think Bergi pointed me in the right direction.

This was a package "express-session" that was loaded in the router. Seems like the default memory store is leaky and shouldn't be used in production.

From the npmjs repository

Warning The default server-side session storage, MemoryStore, is purposely not designed for a production environment. It will leak memory under most conditions, does not scale past a single process, and is meant for debugging and developing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM