DEV Community

Cristopher
Cristopher

Posted on

Help me understand how AWS Lambda reuses object

Good day to all of you,

I'm a complete newbie on AWS Lambda. Currently, I'm building an app which has an OO pattern with clean architecture on top of it for ease of testing on the handler function like so

async function setupBotHandler(): Promise<void> {
  const connection = await connectToDatabase();

  const serviceMap = new Map<string, BotService>();

  const announcementRepository = connection.getCustomRepository(
    AnnouncementRepositoryTypeORM,
  );
  const categoryRepository = connection.getCustomRepository(
    CategoryRepositoryTypeORM,
  );
  const stateRepository = connection.getCustomRepository(
    StateRepositoryTypeORM,
  );

  const announcementService = new BotAnnouncementService(
    announcementRepository,
    categoryRepository,
  );

  serviceMap.set(announcementService.identifier, announcementService);

  endpointHandler = new BotEndpointHandler(serviceMap, stateRepository);
}

export async function handler(
  event: APIGatewayEvent,
  context: Context,
): Promise<APIGatewayProxyResult | void> {
  if (!endpointHandler) {
    await setupBotHandler();
  }

  return await endpointHandler.handler(event, context);
}

My main concern is about reusing execution context. I've heard that lambda reuses any variables declared outside the handler functions if the container decides to reuse the execution context, which is why we must check if the object exist or not (from the if the statement in my case).

However, some tutorials on the net (example) doesn't seem to check if some variable which the handler function depends on already exist or not.

Should I stick to what I've done or should I do it like this:

const connection = await connectToDatabase();

const serviceMap = new Map<string, BotService>();

const announcementRepository = connection.getCustomRepository(
  AnnouncementRepositoryTypeORM,
);
const categoryRepository = connection.getCustomRepository(
  CategoryRepositoryTypeORM,
);
const stateRepository = connection.getCustomRepository(
  StateRepositoryTypeORM,
);

const announcementService = new BotAnnouncementService(
  announcementRepository,
  categoryRepository,
);

serviceMap.set(announcementService.identifier, announcementService);

const endpointHandler = new BotEndpointHandler(serviceMap, stateRepository);

export const handler = endpointHandler.handler;

Any explanation will be appreciated. Thank you!

Top comments (2)

Collapse
 
kylegalbraith profile image
Kyle Galbraith

Your current approach isn't wrong but it might not be the most "Lambda" way of doing this. Your second example is more in line with what I have seen a lot of other folks use (myself included). The advantage of the second approach is no if statement needed and everything remains initialized in memory across invocations.

The key bit is the function handler and what is outside of it, as shown below.

const myVar = 3;
const myDBConnection = new DbConnection();

export async function handler(
  event: APIGatewayEvent,
  context: Context,
): Promise<APIGatewayProxyResult | void> {
  if (!endpointHandler) {
    await setupBotHandler();
  }

  return await endpointHandler.handler(event, context);
}

When the function is first invoked, Lambda is going to set up the entire context top to bottom. So myVar and myDBConnection will get initialized and then the function handler will be invoked. This is what is commonly referred to as a "cold start", it's the first time the function has been invoked.

However, Lambda optimizes invocations by reusing contexts as long as they meet certain conditions, i.e. they remain "hot". In that scenario, myVar and myDBConnection do not get reinitialized, in fact, that code doesn't get executed again because it's already happened in that context. In this scenario, the function handler is invoked because everything outside of it is already in memory.

Collapse
 
namchee profile image
Cristopher

Could you please explain about certain conditions for remaining 'hot'?

And if lambda decides to not reuse the execution context, will lambda re-initialize everything outside the function handler before executing the function?