Photo by Nathan Riley from Unsplash

3 Things I Wish I Knew About AWS Lambda Functions Early On

So I’ve been building serverless applications for quite a lot during the last year and a half. It’s now time to share a few very important takeaways I’ve learned about the AWS Lambda engine.

Retaining of the execution context

It came as a surprise to me and my team when we realized that two absolutely unrelated executions of the same Lambda function might end up sharing the global execution context. Take a look at this example snippet:

/* my-lambda-function.js */const Logger = require('...');
const logger = new Logger('...');
exports.handler = () => {
// ...
}

You might be doing something similar in your code where you create a global instance of an object (for example a reusable logger instance) that is then passed around or exported for use in other listings.

Since it has no modifiable state you treat it as a global constant and don’t consider it an anti-pattern. And that’s where the problems begin: this is an anti-pattern in the context of the Lambda engine.

When someone comes around and unknowingly updates the Logger class to hold some modifiable state, it infests your code with one of the evilest bugs that you could ever encounter: implicit data mutations. You can’t understand the source of the mutations and spend hours trying to fix it.

It’s not immediately obvious because intuitively you treat each Lambda function as a separate application and, again intuitively, you expect it to start each code execution from a blank slate, with a brand new global context. It’s so intuitive that you don’t even think about it.

Well the truth is that Lambda can actually run the next function invocation request on an existing, running engine image, on the same Node.js process by simply invoking your Lambda function handler method with a new request payload. If you modified your global context in the previous execution, effects of that will propagate into the future executions that occur afterwards.

It’s worth noting that this “request bundling” happens only when two requests occur within a certain time threshold, that in my experience have been about 2–3 seconds.

Abandoned network connections

Another unobvious thing that you can encounter with is unexplainable Lambda function timeouts. This issue is also quite hard to debug simply because you don’t expect this behavior to occur.

When you write your Lambda function code, you use callbacks or Promises to signal the Lambda engine that your function is done doing what it’s been doing and a response can be returned back to the invocation request.

Intuitively you expect that when your handler’s returned Promise resolves or rejects, or when you call a handler callback() function, your function will stop executing and pass back the execution result. But it’s not that simple.

If your Node.js event loop contains something at the time of callback() invocation or Promise resolution Lambda engine won’t act on your signal until the event loop is cleared. Typically things that are found in the event loop once your script flow is done executing are open database connections. If you don’t terminate them and leave them hanging your function will timeout.

You can disable this behavior by setting the callbackWaitsForEmptyEventLoop flag to false on the context object passed to your Lambda function handler.

/* my-lambda-function.js */exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
}

Lambda layers limitations

Another thing I wish I knew earlier is the limitations of Lambda layers. You can attach only 5 layers to each Lambda function and the maximum size of each Lambda function’s bundle is 250mb. That includes the function’s code, dependencies, and all attached layers.

I wrongfully assumed that layer count is unlimited and that function size is not a thing. I jumped straight into it and ended up designing several serverless applications incorrectly which led me to have to redo my work.

So remember these limitations and plan your application architecture ahead, taking this information into consideration. Don’t repeat my mistakes!

If you have found this article valuable please share it! Thank you for reading!

--

--

--

Writing full-stack React.js applications and building cloud solutions on AWS. Find me on GitHub and LinkedIn @iamarkadyt or at www.rkd.fyi

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

A simplified guide to create an app with ASP.NET CORE and Docker

Debug Kubernetes Operator-sdk v1.0.0 locally using Vscode

Use of comparator in HashSet and TreeSet

Top Reason for Project Scope Creep, And How To Avoid It

RealReview — AI model based Blazor .Net

Not the Shortest Path: Convert GPX Files for 3D Animation

From Hackfest to Production, Integrating with Elasticsearch

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Arkady Titenko

Arkady Titenko

Writing full-stack React.js applications and building cloud solutions on AWS. Find me on GitHub and LinkedIn @iamarkadyt or at www.rkd.fyi

More from Medium

Serverless Web Application with Docker, AWS Fargate and ELB

How to deploy a Go application with Postgres on Patr

Move Dead Letter Queue Messages From SQS to DynamoDB using Pulumi

AWS, Developer Journey