Command Palette

Search for a command to run...

Hectal
PHASE 5Intermediate ~14 min· topic 3 of 4

Topic 5.3

Lambda: The Execution Model

In one line

Lambda runs your function in response to events inside short-lived execution environments that are reused between invocations — understanding cold starts, concurrency, timeouts, and what survives between calls is what makes Lambda code correct and fast.

0/4 · 0%

Think of it like this

A food truck that only opens when a customer walks up. The first customer waits while the grill heats (a COLD START). Customers right after are served instantly on the hot grill (WARM invocations). A queue of customers at once? More trucks appear, each needing its own warm-up (CONCURRENCY).

Key ideas

  1. 01

    Lambda runs a HANDLER per event (an HTTP request, an S3 upload, an SQS batch, a schedule). You configure memory (128 MB–10 GB — CPU scales proportionally with it), a timeout (max 15 minutes), and an execution role (its permissions). You pay per request and per GB-second of duration.

  2. 02

    An EXECUTION ENVIRONMENT is created on a cold start: download code, start the runtime, run your INIT code (everything outside the handler). It then serves many invocations, ONE AT A TIME, until it's idle long enough to be reclaimed. Put SDK clients, DB connections, and config loading OUTSIDE the handler so warm invocations reuse them.

  3. 03

    CONCURRENCY = number of environments running simultaneously. Each concurrent request needs its own environment, so a burst of 500 requests means ~500 environments (and cold starts). The account has a regional concurrency limit (1,000 by default); RESERVED concurrency caps one function (protecting downstream databases), and PROVISIONED concurrency keeps environments pre-initialized to eliminate cold starts for latency-critical paths.

  4. 04

    Invocation types matter for errors: SYNCHRONOUS (API Gateway) returns errors to the caller; ASYNCHRONOUS (S3, EventBridge) retries twice then sends to a failure destination/DLQ; POLL-BASED (SQS, Kinesis, DynamoDB Streams) retries per batch — use partial batch responses so one bad message doesn't reprocess the whole batch. Because retries happen, handlers must be IDEMPOTENT.

  5. 05

    Limits to design around: 15-minute max runtime, 6 MB synchronous payload, /tmp up to 10 GB (ephemeral), no long-lived background work after the handler returns. Package as a zip (with LAYERS for shared deps) or a container image up to 10 GB.

In your stack

  • →

    JVM cold starts are the slowest of the common runtimes — often 1–several seconds with frameworks like Spring because of class loading and dependency-injection setup. Enable SnapStart (restores from a snapshot of the initialized JVM), prefer lightweight frameworks or none, and raise memory to get more CPU during init.

  • →

    With SnapStart, anything unique captured during init (random seeds, cached credentials, open connections) is duplicated across restored environments — re-establish connections and re-seed in a restore hook.

Code & diagrams

handler.mjsjs

Client created once per execution environment, reused across warm invocations; partial batch failure for SQS.

import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";

const ddb = new DynamoDBClient({});

export const handler = async (event) => {
  const failures = [];
  for (const record of event.Records) {
    try {
      const order = JSON.parse(record.body);
      await ddb.send(new PutItemCommand({
        TableName: process.env.TABLE,
        Item: { pk: { S: `ORDER#${order.id}` }, total: { N: String(order.total) } },
        ConditionExpression: "attribute_not_exists(pk)",
      }));
    } catch (err) {
      if (err.name !== "ConditionalCheckFailedException") {
        failures.push({ itemIdentifier: record.messageId });
      }
    }
  }
  return { batchItemFailures: failures };
};
lambda-ops.shbash
aws lambda update-function-configuration --function-name orders-consumer \
  --memory-size 1024 --timeout 30

# Cap concurrency so the function can never overwhelm the database
aws lambda put-function-concurrency --function-name orders-consumer \
  --reserved-concurrent-executions 50

# Pre-warm 10 environments on the latency-critical alias
aws lambda put-provisioned-concurrency-config --function-name checkout-api \
  --qualifier live --provisioned-concurrent-executions 10

# Tail logs
aws logs tail /aws/lambda/orders-consumer --follow
LambdaLifecyclediagram
Rendering diagram…

Explain it without notes

01

Why should database connections and SDK clients be created outside the handler function?

02

An S3-triggered Lambda sometimes processes the same file twice. Why, and how should you handle it?

Practice

01

A Lambda behind API Gateway has p50 latency of 40 ms but p99 of 2.5 s. What's the likely cause and two ways to reduce it?

02

A queue-consuming Lambda scaled to 800 concurrent executions and took down the Postgres database. How do you prevent this?

Trade-offs

  • ↔

    Lambda scales to zero and to thousands with no capacity planning, but you accept cold starts, a 15-minute ceiling, per-invocation isolation that complicates connection reuse and caching, and costs that exceed a container once traffic is high and constant.

Done when you can

  • I put clients and connections in init code, outside the handler.

  • I understand cold starts and when to use provisioned concurrency or SnapStart.

  • I use reserved concurrency to protect downstream systems.

  • I write idempotent handlers and use partial batch responses for SQS.