Boosting AWS Lambda Performance with In-memory Cache

Boosting AWS Lambda Performance with In-memory Cache

Takahiro Iwasa
Takahiro Iwasa
2 min read
Lambda In-memory Cache Closures in JavaScript

Introduction

AWS Lambda users can utilize in-memory cache to improve performance and reduce costs.

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

This post demonstrates how to optimize AWS Lambda functions with in-memory cache using JavaScript closures.

Non-optimized Lambda Function

Key Issues

  • Instantiates SecretsManagerClient on every invocation.
  • Fetches a secret value from Secrets Manager every time the function is called.
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,
  };
}

Execution Results

Running this function multiple times reveals inefficiencies. For instance, the following logs show a Duration of 153.66 ms per invocation:

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

Optimized Lambda Function

Key Improvements

  • Implements caching with closures in useGetSecret.
  • Caches the secret value on the first call, minimizing subsequent fetches until the Lambda runtime is terminated.
  • Ensures separation of concerns, where callers of getSecret remain unaware of the caching logic.
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;
  };
}

Execution Results

The optimized function significantly reduces execution time. For example, the following logs show a Duration of only 1.28 ms per invocation:

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

Conclusion

AWS Lambda users can greatly improve function performance and reduce costs by adopting in-memory caching techniques like closures. This simple adjustment minimizes redundant processes and enhances execution efficiency.

Happy Coding! 🚀

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.