Optimizing AWS Lambda by In-memory Caches using Closures in JavaScript

Optimizing AWS Lambda by In-memory Caches using Closures in JavaScript

Takahiro Iwasa
Takahiro Iwasa
3 min read
Lambda In-memory Caches Closures in JavaScript

AWS Lambda users can utilize in-memory caches to improve its performance and reduce the cost.

For example, a secret value stored in your Secrets Manager can be cached outside handler unless you need the latest value. This is called static initialization.

This post shows you how to optimize your AWS Lambda functions by in-memory caches using closures in JavaScript.

Non-optimized Lambda Function

First of all, take a look at the following code.

The key points are:

  • It always instantiates SecretsManagerClient every invocation.
  • It always gets a secret value from a Secrets Manager every invocation.
import {
  SecretsManagerClient,
  GetSecretValueCommand,
} from '@aws-sdk/client-secrets-manager';

export async function handler(event) {
  const client = new SecretsManagerClient();
  const command = new GetSecretValueCommand({ SecretId: '<YOUR_SECRET_ID>' });
  const { SecretString } = await client.send(command);

  return {
    statusCode: 200,
    body: SecretString,
  };
}

Run the function a few times, and you will see responses like the following. In this example, the Duration is 153.66 ms.

Test Event Name
TestEvent

Response
{
  "statusCode": 200,
  "body": "{\"message\":\"Hello World\"}"
}

Function Logs
START RequestId: 55f3596f-b4c1-4fcd-bc34-2c63674bd3cd Version: $LATEST
END RequestId: 55f3596f-b4c1-4fcd-bc34-2c63674bd3cd
REPORT RequestId: 55f3596f-b4c1-4fcd-bc34-2c63674bd3cd	Duration: 153.66 ms	Billed Duration: 154 ms	Memory Size: 128 MB	Max Memory Used: 89 MB

Request ID
55f3596f-b4c1-4fcd-bc34-2c63674bd3cd

Optimized Lambda Function

Take a look at the following code.

The key points are:

  • A function to get a secret value is defined as closure in useGetSecret. This function name is inspired by React Hooks.
  • The secret value is cached at the first call, and always return it until the Lambda runtime is terminated.
  • Callers of getSecret do not need to be conscious of the cache, leading to separation of concerns.
import {
  SecretsManagerClient,
  GetSecretValueCommand,
} from '@aws-sdk/client-secrets-manager';

const getSecret = useGetSecret('<YOUR_SECRET_ID>');

export const handler = async (event) => {
  const secret = await getSecret();
  return {
    statusCode: 200,
    body: secret,
  };
};

function useGetSecret(id) {
  const client = new SecretsManagerClient();
  const command = new GetSecretValueCommand({ SecretId: id });
  let secret = '';

  return async () => {
    if (secret) {
      return secret;
    }
    const { SecretString } = await client.send(command);
    secret = SecretString;
    return secret;
  };
}

Run the function a few times, and you will see the response like the following. In this example, the Duration is 1.28 ms.

It is clear that the cache drastically improve the function performance.

Test Event Name
TestEvent

Response
{
  "statusCode": 200,
  "body": "{\"message\":\"Hello World\"}"
}

Function Logs
START RequestId: aaba6d16-19ee-437a-9592-f708b3ed7c8b Version: $LATEST
END RequestId: aaba6d16-19ee-437a-9592-f708b3ed7c8b
REPORT RequestId: aaba6d16-19ee-437a-9592-f708b3ed7c8b	Duration: 1.28 ms	Billed Duration: 2 ms	Memory Size: 128 MB	Max Memory Used: 88 MB

Request ID
aaba6d16-19ee-437a-9592-f708b3ed7c8b

Conclusion

AWS Lambda users can actively take advantage of in-memory caches to improve its performance and reduce the cost.

I hope you will find this post useful.

Takahiro Iwasa

Takahiro Iwasa

Software Developer at KAKEHASHI Inc.
Involved in the requirements definition, design, and development of cloud-native applications using AWS. Now, building a new prescription data collection platform at KAKEHASHI Inc. Japan AWS Top Engineers 2020-2023.