Serverless-express: Cached Mongodb connection - Global variable

Created on 17 Apr 2018  路  6Comments  路  Source: vendia/serverless-express

Hi guys,

I'm using aws-serverless-express repository for a project, and I'm trying to optimize Mongodb connection when Lambda function is awakened.

Currently, for each call, my function create a new Mongodb connection (too bad...), and I found this solution: keep in global variable, the Mongodb connection.

image
(Full article: https://blog.cloudboost.io/i-wish-i-knew-how-to-use-mongodb-connection-in-aws-lambda-f91cd2694ae5)

But I don't know how to do that with my current architecture code:

"use strict";
const awsServerlessExpress = require("aws-serverless-express");
const app = require("./app");

const binaryMimeTypes = [
  "application/javascript",
  "..."
];
const server = awsServerlessExpress.createServer(app, null, binaryMimeTypes);

exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context);

Currently my Mongodb connection is done in app.js file. So each call reload app.js file...
I'm looking for a solution to send my Mongodb variable to my file app and keep alive Mongodb connection while Lambda function is awakened.

Thanks for your help.

Most helpful comment

I used lodash-memoize to cache the function. This solved my problem. Here is my code example, if someone is interested.

// index.js

const containerId = Date.now().toString().slice(-6);

exports.handler = async (event) => {
    const generate = require('./generator.js');
    console.log(`ContainerId: ${containerId}`);
    generate('clientId1');
    generate('clientId2');
    generate('clientId1');
    generate('clientId2');
};


// generate.js
const memoize = require('./memoize');

const getClientSecret = (clientId) => {
    console.log('hello');
    return clientId;
};

const cachedSecret = memoize(getClientSecret);

const generate = (clientId) => {
    const cs = cachedSecret(clientId);
    console.log(cs);
    return cs;
};

module.exports = generate ;

//memoize.js is lodash-memoize source code. I just use the source code here. 

The result looks like this:

// First trigger lambda
ContainerId: 071040
hello
clientId1
hello
clientId2
clientId1
clientId2

// Second time to trigger lambda
ContainerId: 071040
clientId1
clientId2
clientId1
clientId2

What I found:

  • Lambda will run in same container if you run it continuously.
  • No need to use NodeJS global variable. Lodash-memoize will cache getClientSecret function.

All 6 comments

You should be fine. app.js will only be loaded once per Lambda container (or "cold start")

I have similar requirement. Then how do you pass the global variable dbConnection to app.js?

// Following code is in index.js which is used for Lambda
"use strict";
const dbConnection = MongoClient.connect('...');
const awsServerlessExpress = require("aws-serverless-express");
const app = require("./app");  // app.js is used to create application.

const binaryMimeTypes = [
  "application/javascript",
  "..."
];
const server = awsServerlessExpress.createServer(app, null, binaryMimeTypes);

exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context); 
// how to pass dbConnection to app.js? or just use it in app.js?

// how to pass dbConnection to app.js? or just use it in app.js?

Don't. Instantiate the dbConnection in the app.js module.

I used lodash-memoize to cache the function. This solved my problem. Here is my code example, if someone is interested.

// index.js

const containerId = Date.now().toString().slice(-6);

exports.handler = async (event) => {
    const generate = require('./generator.js');
    console.log(`ContainerId: ${containerId}`);
    generate('clientId1');
    generate('clientId2');
    generate('clientId1');
    generate('clientId2');
};


// generate.js
const memoize = require('./memoize');

const getClientSecret = (clientId) => {
    console.log('hello');
    return clientId;
};

const cachedSecret = memoize(getClientSecret);

const generate = (clientId) => {
    const cs = cachedSecret(clientId);
    console.log(cs);
    return cs;
};

module.exports = generate ;

//memoize.js is lodash-memoize source code. I just use the source code here. 

The result looks like this:

// First trigger lambda
ContainerId: 071040
hello
clientId1
hello
clientId2
clientId1
clientId2

// Second time to trigger lambda
ContainerId: 071040
clientId1
clientId2
clientId1
clientId2

What I found:

  • Lambda will run in same container if you run it continuously.
  • No need to use NodeJS global variable. Lodash-memoize will cache getClientSecret function.

I'm sharing my MongoDB client with app.locals in app.js like this:
app.js:

new MongoClient.connect(config.mongo.url, config.mongo.options).then((client) => {
    logger.info('Connected to MongoDB')
    app.locals.client = client
})

// routes
app.use('/', routes)

In a route i do:

router.route('/foo').get(async (req, res) => {
    const { client } = req.app.locals
    ... some db logic
})

In my localhost this works, but when i deploy to the lamdba, the client becomes undefined, how should i share the mongo client between routes?

I've written here how we are doing it for both https://confy.app and https://angoralabs.com.

Was this page helpful?
0 / 5 - 0 ratings