Suggestions for detecting memory leak in express application?

I have an application built in Express 3.20. I maintain it and work with PM2. Observing memory usage in PM2, I can see that it only takes a few hours for usage to become> 500MB for each process, after which it has problems.

About 90% of the requests my application receives are for thumbnails. So I am guessing that the memory leak is from the part of my code that serves as thumbnail images to the user.

The way the code works is the user requests an image of a specific size. The script checks if an image of this size exists in the cache / folder. If so, it sends this file to the client. If not, it uses graphicsMagick to create a new image in cache / folder from the original image and then uploads it.

I am including the relevant code below. Can anyone tell me that I am doing something clearly wrong here that is causing things to hang in memory?

Thanks (in advance) for your help.

var fs = require( "graceful-fs" ),
    gm = require( "gm" );

function isOriginalNewer( orig, cache ) {
    return orig.ctime > cache.ctime ? true : false;
}

function checkIfCachedImageUpToDate( originalPath, cachePath, cb ) {
    fs.stat( originalPath, function( err, stat1 ) {
        if ( err ) return cb( err );
        fs.stat( cachePath, function( err, stat2 ) {
            if ( err ) return cb( err );
            if ( isOriginalNewer( stat1, stat2 ) ) {
                cb ( null, false );
            } else {
                cb( null, true );
            }
        });
    });
}

function createNewCachedImage( originalPath, cachePath, cacheSubDir, size, cb ) {
    fs.exists( "cache/" + cacheSubDir, function ( exists ) {
        if ( exists ) {
            gm( originalPath ).resize( size, null, "^" ).quality( 95 ).write( cachePath, cb );
        } else {
            fs.mkdir( "resources/cache/" + cacheSubDir, 0750, function() {
                gm( originalPath ).resize( size, null, "^" ).quality( 95 ).write( cachePath, cb );
            });
        }
    });
}

function checkIfCached( originalPath, cachePath, cb ) {
    fs.exists( cachePath, function( exists ){
        if ( exists ) {
            checkIfCachedImageUpToDate( originalPath, cachePath, function( err, isUpToDate ) {
                if ( err ) return cb( err );
                if ( isUpToDate ) cb( null, true );
                else cb( null, false );
            });
        } else cb( null, false );
    });
}

function getImagePath( filename, type, size, cb ) {
    var originalPath = "originals/" + filename,
        cacheFilename = hash.md5( originalPath ) + "." + size + ".jpg",
        cacheSubDir = cacheFilename.substr( 0,2 ),
        cachePath = "cache/" + cacheSubDir + "/" + cacheFilename;

    checkIfCached( originalPath, cachePath, function( err, isCached ) {
        if ( err ) return cb( err );
        if ( isCached ) {
            cb( null, cachePath );
        } else {
            fs.exists( originalPath, function( exists ) {
                if ( !exists ) return cb( "File Not Found " + originalPath );
                createNewCachedImage( originalPath, cachePath, cacheSubDir, size, function( err ) {
                    if ( err ) return cb( err );
                    cb( null, cachePath );
                });
            });
        }
    });
}

module.exports = function( app ){
    app.get( "/image/:filename/:size", isLoggedIn, function( req, res, next ){
        var p = sanitizeParams( req );
        getImagePath( p.filename, p.size, function( err, imagePath ){
            if ( err ) next( err );
            else res.sendfile( imagePath );
        });
    });
};

      

0


source to share





All Articles