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)
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.
When the function is first invoked, Lambda is going to set up the entire context top to bottom. So
myVar
andmyDBConnection
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
andmyDBConnection
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.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?