/AWS Lambda Deep Dive
Concept
Hard

AWS Lambda Deep Dive

15 min read·LambdaServerlessConcurrencyDVA-C02

A comprehensive deep dive into AWS Lambda — execution environments, cold starts, invocation types, concurrency, VPC integration, layers, destinations, and deployment patterns for the DVA-C02 exam.


What is AWS Lambda?

AWS Lambda is AWS's serverless compute service — it runs your code in response to events without requiring you to provision or manage servers. You supply code as a ZIP archive or container image, configure triggers, and Lambda handles everything: server provisioning, OS patching, capacity scaling, and high availability.

Lambda is billed in 1 ms increments, only while your code is running — zero charge during idle time.

Core mental model: Lambda inverts traditional compute. Instead of "How many servers do I need?" you ask "What events trigger my code and for how long does it run?"


Execution Environment Lifecycle

Every Lambda invocation runs inside an execution environment — an isolated micro-VM managed entirely by AWS. Understanding this lifecycle is the single most important concept for writing performant Lambda functions.

Phase 1 — INIT (Cold Start)

When Lambda creates a new execution environment, the INIT phase runs. This is what we call a "cold start." It has three sequential sub-phases:

Sub-phaseWhat happensTime budget
Extension initLoad Lambda extensions10 seconds
Runtime initBootstrap the language runtime (Node.js, Python, JVM…)10 seconds
Function initExecute module-scope code outside your handler function10 seconds

The Function init sub-phase is where YOUR initialization code runs — SDK clients, database connections, config loading. This is the primary optimization opportunity.

javascript
1// ✅ INIT PHASE — runs ONCE per execution environment lifecycle
2const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
3const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');
4
5// Initialize expensive resources here — reused across warm invocations
6const ddbClient = new DynamoDBClient({ region: process.env.AWS_REGION });
7const docClient = DynamoDBDocumentClient.from(ddbClient);
8const CONFIG = JSON.parse(process.env.APP_CONFIG ?? '{}');
9
10// ✅ INVOKE PHASE — runs on EVERY invocation
11exports.handler = async (event) => {
12  // ddbClient and CONFIG are already initialized — zero overhead on warm starts
13  const result = await docClient.send(new GetCommand({
14    TableName: 'Users',
15    Key: { userId: event.pathParameters.userId },
16  }));
17  return { statusCode: 200, body: JSON.stringify(result.Item) };
18};

Phase 2 — INVOKE

Lambda calls your handler function with the incoming event. After the handler returns, the execution environment stays alive and warm, ready for the next invocation. Module-scope state persists across warm invocations on the same environment.

javascript
1// ⚠️ Module-scope state persists between warm invocations on the same environment
2let requestCount = 0;
3
4exports.handler = async (event) => {
5  requestCount += 1;
6  console.log(`Warm invocation #${requestCount} on this environment`);
7  // Never rely on this for business logic — any invocation may hit a fresh environment
8};

Phase 3 — SHUTDOWN

When Lambda terminates an idle execution environment, extensions receive a SHUTDOWN event and have 2 seconds to flush telemetry and clean up connections.

Rendering diagram…

Lambda Handler Anatomy

Node.js Handler

javascript
1exports.handler = async function(event, context) {
2  console.log('Event:', JSON.stringify(event));
3  console.log('Request ID:', context.awsRequestId);
4  console.log('Remaining time (ms):', context.getRemainingTimeInMillis());
5  console.log('Memory limit (MB):', context.memoryLimitInMB);
6
7  return {
8    statusCode: 200,
9    body: JSON.stringify({ message: 'Hello from Lambda!' }),
10  };
11};

Python Handler

python
1import json
2
3def handler(event, context):
4    print(f"Request ID: {context.aws_request_id}")
5    print(f"Remaining time: {context.get_remaining_time_in_millis()}ms")
6    print(f"Memory limit: {context.memory_limit_in_mb}MB")
7
8    return {
9        'statusCode': 200,
10        'body': json.dumps({'message': 'Hello from Lambda!'})
11    }

The Context Object — Key Properties

Property (Node.js)Property (Python)Description
awsRequestIdaws_request_idUnique ID for this invocation
functionNamefunction_nameLambda function name
memoryLimitInMBmemory_limit_in_mbConfigured memory size
getRemainingTimeInMillis()get_remaining_time_in_millis()Milliseconds before timeout
logGroupNamelog_group_nameCloudWatch Logs group
invokedFunctionArninvoked_function_arnFull ARN including alias/version qualifier

Configuration Limits Reference

SettingDefaultMaximum
Execution timeout3 seconds15 minutes (900 s)
Memory128 MB10,240 MB
Ephemeral storage (/tmp)512 MB10,240 MB
Deployment package (ZIP)50 MB compressed / 250 MB uncompressed
Container image size10 GB
Concurrent executions1,000 / regionIncrease via support ticket
Environment variables4 KB total
Layers per function5 layers

CPU scales with memory, not configured directly. At 1,769 MB your function has exactly 1 full vCPU. At 3,538 MB it has 2 vCPUs. For CPU-bound workloads (image processing, ML inference), increasing memory is the only way to add CPU.


Invocation Types

Lambda has three distinct invocation models. Knowing which services use which model is a DVA-C02 exam staple.

Rendering diagram…

1. Synchronous — RequestResponse

The caller waits for Lambda to finish and receives the full response. On error, the exception is returned directly to the caller — no automatic retry.

Services that invoke synchronously: API Gateway, ALB, Cognito Lambda triggers, Step Functions Task states, CloudFront/Lambda@Edge.

json
1{
2  "httpMethod": "POST",
3  "path": "/orders",
4  "queryStringParameters": { "source": "web" },
5  "headers": { "Authorization": "Bearer eyJhbGci..." },
6  "body": "{\"product\": \"widget\", \"qty\": 3}",
7  "requestContext": { "requestId": "abc-123", "stage": "prod" }
8}
javascript
1// Lambda response format for API Gateway proxy integration
2exports.handler = async (event) => {
3  const body = JSON.parse(event.body ?? '{}');
4  return {
5    statusCode: 201,
6    headers: { 'Content-Type': 'application/json' },
7    body: JSON.stringify({ orderId: 'ORD-' + Date.now(), ...body }),
8  };
9};

2. Asynchronous — Event

The caller sends the event and immediately receives 202 Accepted — no waiting. Lambda queues the event internally and processes it in the background.

Services that invoke asynchronously: S3 event notifications, SNS, EventBridge rules, IoT rules.

Automatic retry behavior on function failure:

AttemptTiming
1stImmediately
2nd~1 minute after 1st failure
3rd~2 minutes after 2nd failure
After 3rd failureRouted to Destination/DLQ, or discarded

Events are retained in the Lambda internal queue for up to 6 hours before being discarded.

json
1{
2  "Records": [{
3    "eventSource": "aws:s3",
4    "eventName": "ObjectCreated:Put",
5    "s3": {
6      "bucket": { "name": "my-uploads-bucket" },
7      "object": { "key": "uploads/photo.jpg", "size": 2097152 }
8    }
9  }]
10}

3. Poll-Based — Event Source Mapping

Lambda polls stream and queue services on your behalf. You configure an event source mapping; Lambda manages the polling loop, batching, scaling, checkpointing, and retry logic.

Supported sources:

SourceTypeOrdering
Amazon SQS StandardQueueBest-effort
Amazon SQS FIFOQueueStrict per message group
Amazon Kinesis Data StreamsStreamPer shard
Amazon DynamoDB StreamsStreamPer shard
Amazon MSK / KafkaStreamPer partition
json
1{
2  "Records": [{
3    "messageId": "19dd0b57-1234-4f67-8a7b-ce3b2d0c5d2f",
4    "body": "{\"orderId\": \"ORD-001\", \"amount\": 49.99}",
5    "attributes": { "ApproximateReceiveCount": "1" },
6    "eventSource": "aws:sqs"
7  }]
8}

Partial batch failure — return only the IDs of failed messages so successful ones are not reprocessed:

javascript
1exports.handler = async (event) => {
2  const batchItemFailures = [];
3
4  for (const record of event.Records) {
5    try {
6      await processOrder(JSON.parse(record.body));
7    } catch (err) {
8      console.error(`Failed: ${record.messageId}`, err.message);
9      batchItemFailures.push({ itemIdentifier: record.messageId });
10    }
11  }
12
13  return { batchItemFailures };
14  // Requires FunctionResponseTypes: ['ReportBatchItemFailures'] on the event source mapping
15};

Concurrency Deep Dive

Concurrency = the number of Lambda execution environments processing requests simultaneously.

Concurrency = Requests per second × Average duration (seconds) Example: 200 req/sec × 0.3 sec = 60 concurrent executions

Account-Level Concurrency

Every AWS account has a default regional limit of 1,000 concurrent executions, shared across ALL Lambda functions in that region. When the limit is hit, synchronous callers receive TooManyRequestsException (HTTP 429) and asynchronous invocations are queued.

Reserved Concurrency

Reserved Concurrency guarantees AND caps concurrency for a specific function — it carves a dedicated slice from the regional pool:

Rendering diagram…

Setting reserved concurrency to 0 disables the function (all invocations are throttled). This is a useful emergency circuit-breaker.

Provisioned Concurrency

Provisioned Concurrency pre-initializes execution environments so they respond to invocations instantly, with zero cold start latency.

ModelCold Start?Cost
Default (unreserved)Yes, on scale-outPer invocation + duration
Reserved onlyYes, on scale-outPer invocation + duration
ProvisionedNoPer provisioned env-hour + invocation + duration
bash
1# Enable Provisioned Concurrency on the "prod" alias
2aws lambda put-provisioned-concurrency-config \
3  --function-name MyFunction \
4  --qualifier prod \
5  --provisioned-concurrent-executions 100

SnapStart (Java 11/17/21+): Lambda snapshots a fully initialized Java execution environment. Future cold starts restore from this cached snapshot — up to 90% cold start reduction without the ongoing cost of Provisioned Concurrency.


Cold Start Deep Dive

A cold start = INIT phase duration added on top of your handler execution time. For synchronous user-facing APIs, this is often the most impactful latency problem.

Root Causes and Mitigations

FactorTypical impactMitigation
Runtime bootstrapNode.js/Python: 100–300ms; JVM: 500ms–3sUse Node.js or Python for latency-sensitive paths
Package size+10–50ms per MB of extra dependenciesTree-shake; import only what you need
Initialization codeProportional to work doneLazy-load non-critical modules
VPC (old ENI model)Added 10+ secondsModern Hyperplane ENIs: negligible impact

Code Patterns That Reduce Cold Start Time

javascript
1// ❌ ANTI-PATTERN: Import entire AWS SDK v2 (loads all ~700 service clients)
2const AWS = require('aws-sdk');
3
4// ✅ BEST PRACTICE: AWS SDK v3 — modular, import only what you need
5const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
6const s3 = new S3Client({}); // Initialized ONCE in module scope
7
8// ❌ ANTI-PATTERN: Require heavy module inside handler (runs every invocation)
9exports.handler = async (event) => {
10  const sharp = require('sharp');
11  return await sharp(event.imageData).resize(800, 600).toBuffer();
12};
13
14// ✅ BEST PRACTICE: Lazy-load and cache heavy modules in module scope
15let sharp;
16exports.handler = async (event) => {
17  if (!sharp) sharp = require('sharp'); // Load once, cached for warm invocations
18  return await sharp(event.imageData).resize(800, 600).toBuffer();
19};

Lambda in VPC

By default, Lambda runs in an AWS-managed network with full internet access. To reach resources in your private VPC (RDS, ElastiCache, internal APIs), configure VPC settings on the function.

Rendering diagram…

Hyperplane ENIs are shared across functions using the same subnet + security group pair, eliminating the 10+ second ENI creation delay that affected older Lambda VPC configurations.

Required IAM Permissions

The Lambda execution role must have network interface permissions. Use the AWS managed policy AWSLambdaVPCAccessExecutionRole, which grants:

json
1{
2  "Effect": "Allow",
3  "Action": [
4    "ec2:CreateNetworkInterface",
5    "ec2:DescribeNetworkInterfaces",
6    "ec2:DescribeSubnets",
7    "ec2:DeleteNetworkInterface",
8    "ec2:AssignPrivateIpAddresses",
9    "ec2:UnassignPrivateIpAddresses"
10  ],
11  "Resource": "*"
12}

VPC + Internet: VPC-connected Lambda functions lose internet access by default. To reach both your private VPC resources AND the public internet (Stripe, Twilio, etc.), place the function in a private subnet and route outbound traffic through a NAT Gateway in a public subnet.

Security Group Setup

Lambda SG outbound: TCP 5432 → RDS Security Group RDS SG inbound: TCP 5432 from Lambda Security Group

Lambda Layers

Layers are ZIP archives that Lambda extracts to /opt before your function code runs. They enable sharing libraries, custom runtimes, and configuration across multiple functions.

Constraints:

  • Maximum 5 layers per function
  • Function code + all layers uncompressed ≤ 250 MB
  • Layers are versioned and immutable — publish a new version to update
bash
1# Build a Node.js dependency layer
2mkdir -p layer/nodejs && cd layer/nodejs
3npm install lodash axios
4cd ../.. && zip -r deps-layer.zip layer/
5
6# Publish the layer
7aws lambda publish-layer-version \
8  --layer-name common-nodejs-deps \
9  --description "Shared Node.js dependencies" \
10  --zip-file fileb://deps-layer.zip \
11  --compatible-runtimes nodejs18.x nodejs20.x
12
13# Attach the layer to a function
14aws lambda update-function-configuration \
15  --function-name MyFunction \
16  --layers arn:aws:lambda:us-east-1:123456789:layer:common-nodejs-deps:1

AWS Lambda Powertools is a free, production-ready layer published by AWS that provides structured logging, X-Ray tracing, and custom metrics:

python
1from aws_lambda_powertools import Logger, Tracer, Metrics
2from aws_lambda_powertools.metrics import MetricUnit
3
4logger = Logger()
5tracer = Tracer()
6metrics = Metrics(namespace="MyApp")
7
8@logger.inject_lambda_context
9@tracer.capture_lambda_handler
10@metrics.log_metrics
11def handler(event, context):
12    metrics.add_metric(name="OrderProcessed", unit=MetricUnit.Count, value=1)
13    logger.info("Processing order", order_id=event["orderId"])
14    return {"statusCode": 200}

Lambda Destinations

Destinations route the result of an asynchronous invocation to a target service after the function completes. They are strictly more powerful than DLQ.

Rendering diagram…

Supported targets for both success and failure: SQS queue, SNS topic, EventBridge event bus, another Lambda function.

Destinations vs DLQ

FeatureDLQDestinations
Applies toFailures onlySuccess and failures
PayloadOriginal event onlyEvent + function response + metadata
Supported targetsSQS, SNSSQS, SNS, EventBridge, Lambda
RecommendationLegacyPrefer Destinations
bash
1aws lambda put-function-event-invoke-config \
2  --function-name MyFunction \
3  --destination-config '{
4    "OnSuccess": { "Destination": "arn:aws:events:us-east-1:123:event-bus/my-bus" },
5    "OnFailure":  { "Destination": "arn:aws:sqs:us-east-1:123:my-dlq" }
6  }'

Versions and Aliases

Versions

A version is an immutable, point-in-time snapshot of your function's code AND configuration (memory, timeout, layers, environment variables). Once published, a version cannot be changed.

bash
1# Publish a version from $LATEST
2aws lambda publish-version \
3  --function-name MyFunction \
4  --description "v2.3.1 — improved error handling"
5
6# Versioned ARN:  arn:aws:lambda:us-east-1:123:function:MyFunction:7
7# $LATEST ARN:    arn:aws:lambda:us-east-1:123:function:MyFunction:$LATEST

Aliases and Canary Deployments

An alias is a named, mutable pointer to a specific version. Aliases support weighted routing for safe canary deployments — critical for zero-downtime releases.

bash
1# Create "prod" alias pointing to version 6
2aws lambda create-alias \
3  --function-name MyFunction --name prod --function-version 6
4
5# Canary: send 10% of traffic to v7, 90% stays on v6
6aws lambda update-alias \
7  --function-name MyFunction --name prod \
8  --function-version 6 \
9  --routing-config AdditionalVersionWeights={"7"=0.1}
10
11# After validation: promote v7 to 100% of traffic
12aws lambda update-alias \
13  --function-name MyFunction --name prod \
14  --function-version 7 \
15  --routing-config AdditionalVersionWeights={}

CodeDeploy automates alias-based deployments with automatic rollback on CloudWatch Alarm breach:

  • Linear10PercentEvery1Minute — shift 10% per minute over 10 minutes
  • Canary10Percent5Minutes — 10% first, wait 5 min, then 90%
  • AllAtOnce — immediate full shift

Lambda Container Images

Package Lambda as a Docker container image (up to 10 GB) for large dependencies, custom runtimes, or reproducible builds.

dockerfile
1# Use the official AWS Lambda base image
2FROM public.ecr.aws/lambda/nodejs:20
3
4# Install production dependencies
5COPY package.json package-lock.json ${LAMBDA_TASK_ROOT}/
6RUN npm ci --omit=dev
7
8# Copy function code
9COPY index.mjs ${LAMBDA_TASK_ROOT}/
10
11# Set the handler (filename.exportedFunction)
12CMD ["index.handler"]
bash
1# Authenticate Docker to Amazon ECR
2aws ecr get-login-password --region us-east-1 \
3  | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
4
5# Build, tag, and push the image
6docker build -t my-lambda:latest .
7docker tag my-lambda:latest 123456789.dkr.ecr.us-east-1.amazonaws.com/my-lambda:latest
8docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/my-lambda:latest

Key CloudWatch Metrics

MetricWhat it measuresAlert guidance
InvocationsTotal calls including retriesTraffic baseline
ErrorsUnhandled exceptions and timeoutsAlert if error rate > 1%
Duration p9999th-percentile execution timeAlert if approaching timeout
ThrottlesInvocations rejected by concurrency limitAlert on any throttle in production
ConcurrentExecutionsSimultaneous active environmentsAlert when near reserved limit
InitDurationCold start init timeMonitor after deployments
REPORT RequestId: abc-123 Duration: 234.56 ms Billed: 235 ms Memory Size: 512 MB Max Memory Used: 87 MB Init Duration: 318.45 ms ↑ Cold start indicator

The presence of Init Duration in the REPORT log line is the definitive cold start indicator.


DVA-C02 Quick Reference

TopicKey Fact
Max timeout15 minutes
Max memory10,240 MB
Default regional concurrency limit1,000 per region
API Gateway / ALB invocationSynchronous (RequestResponse)
S3 / SNS / EventBridge invocationAsynchronous (Event)
SQS / Kinesis / DynamoDB StreamsPoll-based (Event Source Mapping)
Eliminate cold startsProvisioned Concurrency
Java cold start optimizationSnapStart
Reserved concurrency = 0Disables the function
Async retry count2 retries (3 total attempts)
Prefer over DLQLambda Destinations
Versions areImmutable
Aliases supportWeighted routing (canary deployments)
CPU scales withMemory (1,769 MB = 1 full vCPU)
Max container image10 GB

Practice Questions10

easy

Q1. What is the maximum execution timeout for an AWS Lambda function?


Select one answer before revealing.

easy

Q2. Which Lambda invocation type is used by Amazon API Gateway when calling a Lambda function?


Select one answer before revealing.

medium

Q3. A Lambda function needs to connect to a private Amazon RDS instance inside a VPC. What is required?


Select one answer before revealing.

medium

Q4. A Lambda function processes messages from an SQS queue. Occasionally, messages fail processing and reappear in the queue, causing infinite retries. Which TWO approaches resolve this? (Choose 2)


Select one answer before revealing.

hard

Q5. A Lambda function written in Java is experiencing high cold start latency affecting user-facing P99 response times. Which TWO options best address this? (Choose 2)


Select one answer before revealing.

medium

Q6. A developer wants to run different code for a Lambda function in development, staging, and production without changing the deployment package. What is the recommended approach?


Select one answer before revealing.

medium

Q7. Which Lambda concurrency setting guarantees that a specific function will always have execution capacity available and prevents it from being throttled by other functions in the account?


Select one answer before revealing.

hard

Q8. A developer needs to run code before a Lambda function is invoked to validate a JWT token, and cache the result for 5 minutes. What should they implement?


Select one answer before revealing.

hard

Q9. A Lambda function processes asynchronous events from S3. For failed invocations, the developer wants to capture the original event, error details, and function response for debugging. What should they configure?


Select one answer before revealing.

hard

Q10. A Lambda function is deployed in a VPC private subnet with no NAT Gateway. It needs to call the AWS DynamoDB API. What is the most cost-effective and secure solution?


Select one answer before revealing.