Skip to content

Serverless / FaaS#

Serverless FaaS Lambda style platforms run a function in response to an event, charge per millisecond of execution, and scale concurrency in and out automatically. You give them code and an event source; the platform handles servers, scaling, patching, and capacity planning.

Problem statement (interviewer prompt)

Design an image-thumbnail pipeline using AWS Lambda triggered by S3 uploads. Explain cold start vs warm execution context, concurrency limits, billing per invocation, provisioned concurrency, layers, and when you would refuse and pick containers or VMs instead.

flowchart LR
  S3([S3 bucket])
  EV[Event Source]
  LAMBDA[Lambda Function]
  CTX[Warm Execution Context]
  DEST[(Output / DynamoDB)]

  S3 --> EV --> LAMBDA --> CTX --> DEST

  classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
  classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
  classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
  classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
  classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
  class S3 client;
  class EV edge;
  class LAMBDA service;
  class CTX compute;
  class DEST datastore;

Two latency regimes matter. A cold start spins up a fresh micro-VM (Firecracker on AWS), downloads the deployment package, starts the runtime, and runs your init code. A warm invocation re-uses the same execution context, so module-level state (DB pools, cached secrets) survives across calls. Provisioned concurrency keeps a pool of pre-warmed contexts ready, trading idle cost for predictable latency.

Lambda shines for spiky workloads (image processing, webhooks, ETL glue), but is a poor fit for long-running jobs, GPU work, or anything that needs a TCP connection held open for minutes.

Problem statement (interviewer prompt)

Design an image-thumbnail pipeline using AWS Lambda triggered by S3 uploads. Explain the execution model, cold start anatomy, concurrency limits, event source mapping, provisioned concurrency, layers, billing math, and the workloads where serverless is the wrong tool.

Execution model#

A Lambda function is packaged code (zip or container image up to 10 GB) plus a runtime, handler name, memory size (128 MB to 10 GB), and timeout (up to 15 min). The platform owns the fleet of host machines and spawns one micro-VM (Firecracker on AWS) per concurrent invocation. CPU scales linearly with memory, so doubling memory often shortens execution and halves cost.

flowchart TB
  EV[Event Source]
  INV[Invoke]
  COLD[Cold Start: download + init]
  WARM[Warm Path: handler only]
  CTX[Execution Context: /tmp, env, init state]
  DESTRUCT[Idle Timeout: 5-15 min]

  EV --> INV
  INV -->|new VM needed| COLD --> CTX
  INV -->|reuse| WARM --> CTX
  CTX --> DESTRUCT

  classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
  classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
  classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
  classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
  class EV client;
  class INV,COLD,WARM service;
  class CTX compute;
  class DESTRUCT obs;

Cold start anatomy#

A cold start has four phases. The first three are platform overhead; the fourth is yours to shrink.

  1. Sandbox provisioning (10-100 ms): allocate a Firecracker micro-VM, attach networking.
  2. Code download + extract (50-500 ms): pull the zip or container layer to the sandbox.
  3. Runtime init (50-300 ms): start the language runtime (Node, Python, JVM, .NET).
  4. Function init / module load (varies): your top-level imports, env reads, secret hydration.

Heavy languages (JVM, .NET) and fat dependency trees (numpy, gRPC) often push function init into the seconds. The fix:

  • Slim the deployment package; use Lambda layers for shared SDK code.
  • Lazy-import heavy modules inside the handler when they are not always needed.
  • Cache DB connection pools and JWKS keys at module scope so warm calls skip them.
  • Use SnapStart (Java, Python) to checkpoint a pre-initialised VM.
  • Use provisioned concurrency to pin N warm contexts ready.
# warm-friendly Lambda handler
import os, json, boto3

# module scope: runs once per execution context
DDB = boto3.resource("dynamodb")
TABLE = DDB.Table(os.environ["TABLE"])

def handler(event, context):
    item = json.loads(event["Records"][0]["body"])
    TABLE.put_item(Item=item)
    return {"statusCode": 200}

Concurrency model#

There is no "instance" concept. Each in-flight invocation owns one execution context. If 500 events arrive simultaneously, the platform creates up to 500 contexts (subject to account quota), then reuses them as load drops.

sequenceDiagram
  participant EV as Event Source
  participant LM as Lambda
  participant C1 as Ctx 1
  participant C2 as Ctx 2
  participant C3 as Ctx 3 (cold)

  EV->>LM: 3 events arrive at once
  LM->>C1: invoke (warm)
  LM->>C2: invoke (warm)
  LM->>C3: cold start, then invoke
  C1-->>LM: 200 ms
  C2-->>LM: 220 ms
  C3-->>LM: 1.2 s (cold)

Quotas to remember:

  • Account-level concurrency limit (default 1000, raisable).
  • Reserved concurrency: cap a function's max concurrent contexts (e.g. to protect a downstream DB).
  • Provisioned concurrency: pre-warmed contexts billed per second.
  • Burst limit: 500-3000 (region-dependent) immediate cold starts, then +500/min linear ramp.

Event sources#

Source Invocation style Notes
API Gateway, ALB Sync request/response 29 s gateway cap, 15 min Lambda max. Cold start adds tail latency.
S3, SNS Async push Lambda retries twice, then DLQ.
SQS Poll-based event source mapping Batch up to 10 messages, scales by visibility timeout.
Kinesis, DynamoDB Streams Poll-based ordered shards One concurrent batch per shard; checkpoint per batch.
EventBridge, Cron Schedule Replaces self-managed cron daemons.
Step Functions Workflow orchestrated Chains many short Lambdas with retry, fan-out.

Billing math#

You pay for GB-seconds plus per-invocation requests. For 1 GB memory at 1 M invocations of 200 ms:

GB-seconds = 1 GB * 0.2 s * 1_000_000 = 200_000
cost ~= 200_000 * $0.0000166667 + 1_000_000 * $0.20/1_000_000
     ~= $3.33 + $0.20 = $3.53

Plus data transfer, plus provisioned concurrency if used. Compared to a 24/7 t3.medium ($30/month) the Lambda is cheaper only if you average well under one concurrent request all month.

Containers and layers#

Lambda can run container images up to 10 GB pulled from ECR. This solves the 250 MB zip limit and lets teams reuse Dockerfiles, at the cost of slower cold starts (the platform fetches missing image layers). Lambda layers are zip-based bundles of shared libraries; multiple functions reference the same layer to deduplicate disk and speed up cold start.

When NOT to use serverless#

flowchart LR
  LONG[Jobs > 15 min]
  GPU[GPU training, inference]
  WS[Long-lived WebSockets]
  PRED[Predictable steady traffic]
  CHATTY[Chatty private-VPC apps]
  STATE[In-memory caches across requests]

  LONG --> NO[Not Lambda]
  GPU --> NO
  WS --> NO
  PRED --> NO
  CHATTY --> NO
  STATE --> NO

  classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
  classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
  class LONG,GPU,WS,PRED,CHATTY,STATE external;
  class NO datastore;
  • Steady high traffic: ECS/EKS or EC2 is cheaper because Lambda's per-second pricing exceeds reserved instance pricing.
  • GPU / ML inference: Lambda has no GPU. Use SageMaker, ECS+GPU, or Bedrock.
  • Long-running processes (> 15 min): use Step Functions, Batch, or Fargate.
  • WebSocket fan-out: API Gateway WebSockets work, but heavy connection pooling is awkward.
  • Latency-critical synchronous APIs: cold starts add a tail spike. Pin provisioned concurrency or pick Fargate.
  • VPC-heavy apps: Lambda inside a VPC adds ENI provisioning latency (now under 1 s after Hyperplane, but watch IP exhaustion).

Where it is used#

  • Image processing pipelines triggered by S3 PutObject.
  • Webhook receivers with API Gateway in front.
  • ETL glue moving rows between SQS, Kinesis, and DynamoDB.
  • Cron-style schedulers replacing self-hosted Celery beat.
  • Edge / origin shielding with Lambda@Edge or CloudFront Functions.

Quick reference#

Execution model#

  • One execution context = one concurrent invocation, running in a Firecracker micro-VM (AWS).
  • Memory 128 MB to 10 GB, CPU scales linearly with memory.
  • Timeout up to 15 minutes, zip up to 250 MB, container image up to 10 GB.
  • Module-level state survives across invocations in the same context; /tmp (up to 10 GB) is per-context scratch.

Cold start vs warm#

  • Cold: VM provisioning + code download + runtime init + your init code.
  • Warm: handler only; reuse pooled DB connections, hydrated secrets, cached JWKS.
  • Reduce cold starts: slim deps, use layers, lazy imports, SnapStart, provisioned concurrency.

Concurrency#

  • Account quota (default 1000), raisable.
  • Reserved concurrency: cap to protect a downstream.
  • Provisioned concurrency: pre-warmed contexts, billed per second.
  • Burst limit: 500-3000 immediate cold starts, then linear ramp +500/min.

Event sources#

Source Style Notes
API Gateway / ALB sync 29 s gateway cap
S3 / SNS async push retries + DLQ
SQS poll mapping batch up to 10
Kinesis / DDB Streams poll, ordered per shard one concurrent batch per shard
EventBridge schedule + bus cron replacement

Billing#

  • GB-seconds + per-request fee.
  • Doubling memory often halves duration: tune via Lambda Power Tuner.
  • Provisioned concurrency adds idle cost.

When NOT serverless#

  • Jobs longer than 15 minutes.
  • GPU workloads.
  • Steady high-throughput traffic (containers cheaper).
  • Long-lived WebSockets at scale.
  • Apps that need large warm in-memory caches per process.

Production gotchas#

  • Watch VPC ENI IP exhaustion when many functions share a subnet.
  • API Gateway 29 s timeout caps synchronous requests.
  • Retries can cause duplicate side effects; design idempotently.
  • Cold starts amplify p99 latency; consider provisioned concurrency or pre-warming.
  • Container image cold start beats zip when image is small and cached.

Refs#

  • AWS Lambda Operator Guide.
  • Agache et al.: "Firecracker - Lightweight Virtualization for Serverless Applications" (NSDI 2020).
  • Berkeley View on Serverless Computing (2019).
  • Knative Serving docs.

FAQ#

What causes a Lambda cold start?#

When no warm execution environment is available, the platform downloads code, starts the runtime, and runs initialization. Cold starts add tens to hundreds of milliseconds depending on language and package size.

When should I avoid serverless?#

Avoid it for long-running jobs, latency-critical APIs at very high traffic, workloads needing persistent in-memory state, and anything that requires custom kernel or GPU configuration.

What is provisioned concurrency?#

A paid setting that keeps a fixed number of execution environments warm so requests never pay cold start. It is useful for predictable latency on consumer-facing endpoints.

How does Lambda pricing work?#

You pay for the number of invocations plus GB-seconds of memory and execution time. Idle time costs nothing, which is why bursty or low-traffic workloads are cheap on serverless.

How is concurrency controlled in Lambda?#

Each function has an account or reserved concurrency limit. Beyond that limit, invocations are throttled or queued depending on the event source like SQS, Kinesis, or API Gateway.

  • Containers vs Virtual Machines: serverless platforms run code in micro-VMs (Firecracker), so the container vs VM trade-off informs cold start cost.
  • Container Orchestration: Kubernetes-based FaaS (Knative, OpenFaaS) gives serverless ergonomics on existing clusters.
  • Auto Scaling: FaaS is the limit case of autoscaling, scale-to-zero with per-request billing.

Further reading#