2

So I'm following the example here https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs, to optimize my lambda functions.

I've tried two approaches and tested them locally using serverless-offline and both don't seem to work.

First Approach

// endpoint file

import {connectToDatabase} from "lib/dbUtils.js";

let cachedDb = null;

export function post(event, context, callback) {
  let response;
  context.callbackWaitsForEmptyEventLoop = false;
  connectToDatabase()
    .then(//do other stuff

// lib/dbUtils.js

export async function connectToDatabase() {
  if (cachedDb && cachedDb.serverConfig.isConnected()) {
    console.log(" using cached db instance");
    return cachedDb;
  }
  cachedDb = await mongoose.createConnection(
    process.env.DB_URL,
    async err => {
      if (err) {
        throw err;
      }
    }
  );
  return cachedDb;
}

Second Approach

global.cachedDb = null;

export function post(event, context, callback) {
  let response;
  context.callbackWaitsForEmptyEventLoop = false;
  connectToDatabase()
    .then(connection => createUser(event.body, connection))


// lib/dbUtils.js

export async function connectToDatabase() {
  // eslint-disable-next-line
  if (global.cachedDb && global.cachedDb.serverConfig.isConnected()) {
    // eslint-disable-next-line
    console.log(" using cached db instance");
    // eslint-disable-next-line
    return global.cachedDb;
  }
  // eslint-disable-next-line
  global.cachedDb = await mongoose.createConnection(
    process.env.DB_URL,
    async err => {
      if (err) {
        throw err;
      }
    }
  );
  // eslint-disable-next-line
  return global.cachedDb;
}

In both cases the using cached db instance console log does not run.

Why does this not work? Is this because of serverless-offline?

2
  • 2
    Try on AWS first and then comment Commented Aug 16, 2017 at 10:01
  • What you are attempting is perfectly legitimate -- there is no guarantee that the container will be reused, but it's highly likely that it will be. What I don't understand is why you seem to be making this test case much mode complicated than it needs to be. Could it be a scoping issue? Put all of this code in one single file, make it work, then split it up into different files. You are testing two conditions, if (cachedDb && cachedDb.serverConfig.isConnected()) yet you don't log the first condition to see if it's true. Commented Aug 16, 2017 at 20:42

1 Answer 1

2

The answer is simple: serverless-offline doesn't simulate the full AWS. Use the AWS console to to make a real Lambda

The MongoDB Atlas guide is OK, but it's also worth checking the official AWS Lambda documentation describing the context option in each lambda:

callbackWaitsForEmptyEventLoop – Set to false to send the response right away when the callback executes, instead of waiting for the Node.js event loop to be empty. If false, any outstanding events will continue to run during the next invocation.

It's possible to run your code on a real Lambda and see using cached db instance on the console. Since MongoDB's JavaScript code is fairly poor, I've written out my own version below:

var MongoClient = require("mongodb").MongoClient

let db = null

var log = console.log.bind(console)

var print = function(object) {
    return JSON.stringify(object, null, 2)
}

// Use your own credentials (and better yet, put them in environment variables)
const password = `notactuallyapassword`
const uri = `mongodb+srv://lambdauser:${password}@fakedomain.mongodb.net/test?retryWrites=true`

exports.handler = function(event, context, callback) {
    log(`Calling MongoDB Atlas from AWS Lambda with event: ${print(event)}`)
    var document = JSON.parse(JSON.stringify(event))

    const databaseName = "myDatabase",
        collectionName = "documents"

    // See https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs
    // and https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html#nodejs-prog-model-context-properties
    context.callbackWaitsForEmptyEventLoop = false

    return createDoc(databaseName, collectionName, document)
}

async function createDoc(databaseName, collectionName, document) {
    var isConnected = db && db.serverConfig.isConnected()
    if (isConnected) {
        log(`Already connected to database, warm start!`)
    } else {
        log(`Connecting to database (cold start)`)
        var client = await MongoClient.connect(uri)
        db = client.db(databaseName)
    }

    var result = await db.collection(collectionName).insertOne(document)
    log(`just created an entry into the ${collectionName} collection with id: ${result.insertedId}`)
    // Don't close the connection thanks to context.callbackWaitsForEmptyEventLoop = false - this will re-use the connection on the next called (if it can re-use the same Lambda container)
    return result
}

Use the Test button to run the lambda above twice in the AWS Lambda console.

  • The first time you run it you'll see Connecting to database (cold start)

  • The second time you'll see Already connected to database, warm start!

See the log output section in screenshot below:

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Is it possible to share a single database connection between multiple AWS Lambda functions? For example, suppose I have two Lambda functions. When the first Lambda function is invoked, it establishes a connection to the database. If this function is called again, it should ideally resume the existing connection rather than establishing a new one. However, when the second Lambda function is invoked, it currently creates a new database connection independently......
Ideally, I would like the second Lambda function to use the database connection that was established by the first Lambda function. Is this feasible, and if so, how can it be achieved?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.