AWS Interview Questions

Lambda Cold Starts and Execution Model

questions
Scroll to track progress

Your Lambda function has P99 latency at 8 seconds, but P95 is sub-100ms. When you check CloudWatch, you see cold starts every ~15 minutes. The function is moderately used (50 invocations/minute average), and concurrency is set to 100. Why are you seeing so many cold starts?

Lambda recycles execution environments when they're idle for ~15 minutes. At 50 invocations/minute, if traffic is bursty or distributed across execution environments, environments go idle and are recycled, causing cold starts. The steps to diagnose: (1) Check CloudWatch X-Ray or custom cold start detection — add a cold start flag to your logs: `is_cold_start = "AWS_LAMBDA_INITIALIZATION_TYPE" env var in Python 3.11+ or compare timestamps. (2) Analyze cold start pattern — if cold starts spike every 15 minutes and don't correlate with traffic spikes, it's idle recycling. (3) Check concurrency — at 100 max concurrency and 50 inv/min, you're using ~0.8 concurrent environments on average. If traffic is spiky (e.g., 1000 inv/min for 10 seconds, then quiet), you could spin up 30-50 environments, then idle most of them. (4) Solutions: (a) Increase reserved concurrency to keep environments warm — `aws lambda put-function-concurrency --function-name my-func --reserved-concurrent-executions 20`, which keeps ~20 environments alive even when idle. (b) Add a CloudWatch scheduled rule to invoke the function every 5 minutes to prevent idle recycling: `aws events put-rule --name warm-lambda --schedule-expression "rate(5 minutes)" && aws events put-targets --rule warm-lambda --targets "Id"="1","Arn"="arn:aws:lambda:..."`. (c) Switch to Provisioned Concurrency (on Lambda@Edge or inside a VPC) for more predictable latency. (d) Use Lambda SnapStart (Java) to eliminate initialization time. For most use cases, reserved concurrency is overkill; a warmup scheduled rule is cost-effective.

Follow-up: You add a CloudWatch rule to warm the function every 5 minutes. P99 drops from 8s to 200ms, but P50 is now 300ms higher than before. Why?

Your Node.js Lambda function cold starts take 3 seconds. 90% of that is importing dependencies (Node modules total 50MB after bundling). You can't remove dependencies. How do you reduce cold start time in production?

Cold start time breaks down into: (1) Environment bootstrap (fixed, ~100ms), (2) Code download and unzip (depends on package size), (3) Code initialization (imports, module loading). At 50MB with 3s cold start, you're dominated by code initialization. Strategies: (1) Lambda Layers — split dependencies into layers to enable better caching. Upload dependencies to a layer, use it in the function. Layers are cached separately, so re-deploying function code doesn't re-download dependencies. `aws lambda publish-layer-version --layer-name node-deps --zip-file fileb://layer.zip && aws lambda update-function-configuration --function-name my-func --layers arn:aws:lambda:region:account:layer:node-deps:1`. (2) Code splitting — use dynamic imports to load only necessary dependencies on first use. In Node.js, replace `const lib = require('heavy-lib')` with `const lib = await import('heavy-lib')` and wrap in lazy loading. This defers module loading to when it's actually needed. (3) Tree-shaking and minification — use webpack or esbuild to remove dead code. `npx esbuild handler.js --bundle --minify --outfile=dist/handler.js`. (4) Use a lighter runtime — if using Node.js 14, upgrade to 20; newer runtimes are faster. (5) Lambda@Edge + CloudFront for static assets to avoid loading them on cold start. (6) Consider Python (faster imports than Node.js for some libs) or Go (fastest cold starts, 20-50ms). Testing cold starts: disable provisioned concurrency, update function, invoke with `--invocation-type RequestResponse`, measure end-to-end time. `aws lambda invoke --function-name my-func --payload '{}' response.json && cat response.json`.

Follow-up: You split dependencies into layers and remove 1.5s of cold start time. But P99 latency is still 2s on cold starts, 500ms higher than expected. What's the remaining overhead?

You're comparing cold start times between two Lambda functions: Function A (Python, 10MB code) and Function B (Go, 15MB code). Function A's cold start is 600ms, Function B's is 150ms. Explain the difference and why you'd choose one over the other.

Cold start time is dominated by runtime initialization and code loading, not package size. Go cold starts are faster because: (1) Go is compiled to a single binary with no runtime overhead. The entire 15MB is just the executable and libraries, no interpreter needed. (2) Python requires the Python runtime to initialize, load the interpreter, and execute the imports. At 10MB of Python code, module initialization is slower. (3) Go's startup is ~50-100ms for the runtime, Python is ~300-400ms. Lambda uses AWS Nitro hardware, which is optimized for container isolation, but Go's lean runtime shines. Benchmarks typically show: Go 50-200ms, Node.js 100-500ms, Python 300-600ms, Java 1000ms+ (due to JVM startup). For your use case: (1) If cold start is critical and you're hitting it frequently, Go is better. A 450ms savings per cold start adds up. (2) If the function is rarely cold-started (high concurrency, always warm), the language matters less. (3) Python is easier to maintain; Go requires more upfront work. (4) If the function does heavy compute, Go's performance in warm execution (faster than Python) adds benefit. Decision: For a high-traffic API gateway → Lambda scenario where bursty traffic causes cold starts, use Go. For a scheduled task (once/hour), Python is fine. To optimize Python: use compiled extensions (Cython) for hot code paths, or switch to PyPy runtime (if available via custom runtimes). Testing: `aws lambda invoke --function-name func-a --payload '{}' response.json && aws logs tail /aws/lambda/func-a --follow` and parse the "Duration" from the log line showing initialization time.

Follow-up: You switch from Python to Go and cold starts drop to 150ms. But warm execution is now only 20ms faster than Python (Go: 50ms, Python: 70ms). Why is the warm-start speedup so small compared to cold-start?

Your Lambda function is configured with 3008 MB memory (the maximum). It's CPU-bound (heavy compute). You're seeing cold starts at 2s, and you want to optimize further. Would increasing memory help? What's the relationship between memory and cold start time?

Memory does NOT significantly affect cold start time (initialization); it affects warm execution performance. Here's the breakdown: (1) Cold start is dominated by environment bootstrap, code download/unzip, and module initialization. These are not memory-dependent. (2) CPU allocation is proportional to memory in Lambda — 3008 MB gives you ~2 vCPU equivalents. More CPU speeds up code initialization slightly (maybe 10-20% improvement), but it's minor. (3) Warm execution time is highly dependent on memory (CPU), so a CPU-bound function will run 3-4x faster at 3008 MB vs 128 MB. To optimize cold starts, focus on: (1) Code size — smaller code = faster unzip. (2) Module initialization — lazy load expensive imports. (3) Runtime choice — Go/Rust are faster than Python/Node.js. (4) Layers — better caching of static dependencies. Testing: measure cold start at different memory levels. You'll see minimal improvement in cold start, but warm execution time will change dramatically. `aws lambda update-function-configuration --function-name my-func --memory-size 1024 && aws lambda update-function-configuration --function-name my-func --memory-size 3008`. Invoke both and compare CloudWatch Logs' "Duration" and "Init Duration" fields. In CloudWatch Logs Insights, query: `fields @duration, @initDuration | filter @type = "REPORT"`. You'll notice `@initDuration` is nearly the same regardless of memory, but `@duration` scales inversely with memory.

Follow-up: You check the logs and cold starts show @initDuration of 2s at all memory levels, confirming memory doesn't help. But you notice @initDuration is sometimes 0. Why is that?

Your Lambda function is deployed inside a VPC (for RDS access). Cold starts are 4-5 seconds. The same function without VPC configured has 500ms cold starts. You can't remove the VPC config because the function needs RDS. How do you mitigate the VPC cold start penalty?

VPC cold starts are slow because Lambda must set up network interfaces (ENIs) in the VPC, which involves attaching an ENI, assigning IPs, and waiting for the network stack to initialize. This adds 1-2 seconds. Strategies: (1) VPC Endpoints — if your function only needs to reach specific AWS services (RDS, S3), use VPC endpoints instead of NAT. Create a VPC endpoint for the service, attach to your VPC, and Lambda can reach it without waiting for NAT setup. Example: `aws ec2 create-vpc-endpoint --vpc-id vpc-xxxxx --service-name com.amazonaws.region.rds --subnet-ids subnet-xxxxx`. But this only helps if RDS is accessed via private link, not applicable to RDS directly. (2) Provisioned Concurrency — keep environments warm so VPC setup happens during provisioning, not cold starts. `aws lambda put-provisioned-concurrency-config --function-name my-func --provisioned-concurrent-executions 10`. (3) RDS Proxy — use Amazon RDS Proxy instead of direct RDS connections. Proxy is outside the Lambda, so you can reach it via VPC endpoint or through a non-VPC path. (4) Connection pooling — reuse database connections across invocations using Lambda context. In warm execution, the connection remains open, so no re-initialization. (5) Ephemeral storage — move some initialization to Lambda Ephemeral Storage (/tmp) to avoid re-initialization on every invocation. The most effective fix: use RDS Proxy + Provisioned Concurrency. Provisioned Concurrency removes the VPC setup from cold starts by pre-initializing environments. Benchmark: measure before/after with VPC, and after Provisioned Concurrency at different levels. `aws lambda get-provisioned-concurrency-config --function-name my-func`.

Follow-up: You enable Provisioned Concurrency at 5 environments. Cold starts still happen (when exceeding 5 concurrent invocations). Those cold starts are still 4 seconds. Why doesn't Provisioned Concurrency help the 6th invocation?

Your Lambda function is Java-based and compiled to a JAR. Cold starts are 3-4 seconds (typical for Java). You read about Lambda SnapStart for Java. Should you use it? What's the tradeoff?

Lambda SnapStart (available for Java 11, 17) snapshots the Lambda execution environment after function initialization, then restores it on subsequent cold starts. This can reduce cold start from 3-4 seconds to 100-500ms. Tradeoffs: (1) Pros: dramatic cold start reduction, no code changes needed, works with any Java library. (2) Cons: SnapStart is not available in all regions (check AWS docs for availability). It only works for provisioned functions (not on-demand scaling). It adds a small overhead to provisioning. The init snapshot is taken after your function's handler is loaded but before the first invocation, so any state initialized at module level is baked into the snapshot. (3) State issues — if your handler initialization creates a database connection, that connection is captured in the snapshot and reused. This can cause stale connections or issues if the connection pool expects fresh initialization. (4) Networking — VPC setup is still done, but only once per snapshot. If the snapshot exists, re-invocations skip VPC setup entirely. To use: (1) Ensure Java 11+ runtime, (2) In function configuration: `aws lambda update-function-configuration --function-name my-func --snapstart ApplyOn=PublishedVersions`. (3) Deploy a version: `aws lambda publish-version --function-name my-func`. (4) Invoke with `Qualifier=`. Important: SnapStart works only on published versions, not $LATEST. Testing: compare cold start latency before/after enabling SnapStart on a published version. CloudWatch Logs will show reduced Init Duration.

Follow-up: You enable SnapStart, but cold starts are still 2 seconds. You check logs and see Init Duration is 200ms (good), but Duration (actual execution) is still high. What's the issue?

Your organization runs thousands of Lambda functions across multiple AWS regions. You notice regional differences: us-east-1 cold starts are 800ms, eu-west-1 are 1200ms, ap-southeast-1 are 1500ms. All functions are identical (same code, memory, runtime). Why the difference, and can you fix it?

Cold start latency can vary by region due to: (1) Hardware differences — older regions might have older Nitro generation hardware, newer regions have newer, faster hardware. Newer = faster cold starts. (2) Data center provisioning — regions with higher demand are over-provisioned, leading to faster initialization. Regions with lower demand might have older infrastructure. (3) Container image caching — AWS caches container images across Lambda hosts in a region. If a region has fewer Lambda invocations, images are less likely to be cached, causing slower pulls and higher initialization. (4) Network latency — within-region latency is negligible, but regions with older infrastructure might have slower I/O subsystems. You can't directly fix regional hardware differences, but you can: (1) Use Provisioned Concurrency in regions with slow cold starts to keep environments pre-warmed. (2) Switch to Go or other compiled languages if the region's Python runtime is slow. (3) Deploy critical functions to multiple regions and use a global load balancer (e.g., Route 53, CloudFront) to route to the fastest region. (4) If the difference is significant, migrate to a newer region if your application allows. Testing: invoke the same function 100 times in each region, measure cold start times, and average. `aws lambda invoke --function-name my-func --region us-east-1 response.json && grep "Duration" response.json`. Repeat for other regions. If you see significant skew (e.g., 800ms vs 1500ms), evaluate whether to switch regions or add Provisioned Concurrency in slower regions.

Follow-up: You migrate the function to a newer region. Cold starts are now 500ms. But you realize this new region has higher data transfer costs. How do you balance latency vs. cost?

Your Lambda function processes SQS messages. Each invocation pulls a batch of 10 messages. You've observed P99 latency is 5 seconds, but P50 is 200ms. Analyzing cold starts, you notice they occur randomly throughout the day, not just on traffic spikes. You've set up a CloudWatch rule to warm the function every 10 minutes. Cold starts still happen. What's wrong?

The issue is likely: (1) Concurrent invocations exceeding your warmer's coverage. If SQS is delivering batches to 20 concurrent Lambda invocations, and your warmer only keeps 1 environment warm, 19 will be cold on each burst. (2) SQS batching behavior — Lambda polls SQS and invokes with a batch. If you have high concurrency (e.g., `MaximumConcurrency: 10` on the event source mapping), 10 environments must be ready. A single warmer can't keep 10 warm. (3) Idle recycling still happening — your warmer at every 10 minutes is barely sufficient to keep 1 environment from being recycled. If your actual workload has 2-3 concurrent environments running, the warmer only prevents 1 from being recycled. Debug: (1) Check current concurrency — `aws lambda get-provisioned-concurrency-config --function-name my-func` or look at `ConcurrentExecutions` metric in CloudWatch. (2) Calculate required warmed environments — if peak concurrency is 10, you need at least 10 reserved concurrent executions, or a warmer that continuously invokes 10 environments. (3) Check SQS event source mapping — `aws lambda list-event-source-mappings --function-name my-func` and look at `MaximumConcurrency`. (4) Increase warmer aggressiveness — instead of every 10 minutes, invoke every 1 minute, or use Provisioned Concurrency. Solution: Set reserved concurrency to match peak load: `aws lambda put-function-concurrency --function-name my-func --reserved-concurrent-executions 15`. This keeps 15 environments warm even when idle, eliminating cold starts. Trade-off: higher cost (~$14.67/month per reserved concurrent execution), but P99 latency drops from 5s to 200ms.

Follow-up: You set reserved concurrency to 15. P99 drops to 300ms, but your CloudWatch bill increased significantly. You notice the reserved concurrency cost is $220/month. How do you justify this to finance?

Want to go deeper?