8. What is a cold start, and how often does it actually happen?
A cold start is an invocation that has to wait for the Init phase: a new environment, code download, runtime start and your initialization code. It happens on the first request, whenever traffic rises beyond the warm environments available, after a deploy (new version, new environments), and after environments are recycled.
AWS's own documentation says cold starts typically occur in under 1% of invocations and range from under 100 ms to over a second. That is why they show up at the p99, not the median, and why dev and test functions feel much worse than production - they are invoked rarely, so nearly every call is cold. Init time is also billed, so a heavy Init costs money as well as latency.
9. How do you reduce cold-start latency without paying for provisioned concurrency?
- Shrink the package. Fewer dependencies, tree-shaking and bundling for Node.js, no unused SDKs. Less to download and less to import.
- Lazy-load rarely used modules inside the code path that needs them.
- Raise memory. CPU is allocated in proportion to memory, so Init code that is CPU-bound (class loading, JIT) runs faster.
- Avoid unnecessary work in Init. Do not fetch every secret and warm every cache if most invocations need one.
- SnapStart for supported runtimes (next question).
"Ping it every five minutes to keep it warm" is the answer interviewers listen for so they can push back: it keeps one environment warm, and does nothing for the burst that needs fifty.
10. What is SnapStart and what are its gotchas?
SnapStart initializes your function when you publish a version, takes a Firecracker snapshot of the memory and disk state, and resumes new environments from that snapshot instead of running Init from scratch. It supports Java 11 and later, Python 3.12 and later, and .NET 8 and later. It works only on published versions and aliases, not $LATEST.
The gotchas are what the interviewer wants:
- Uniqueness. Anything generated during Init - random seeds, UUIDs, secrets - is in the snapshot and therefore shared by every environment resumed from it. Generate unique values after restore or in the handler.
- Network connections opened during Init are not guaranteed to be valid after restore. Validate and reconnect.
- Stale data. Temporary credentials or cached timestamps from Init may be old.
- Incompatibilities. SnapStart cannot be combined with provisioned concurrency, EFS, or ephemeral storage above 512 MB.
Runtime hooks let you run code before the snapshot and after restore to handle the first three.
11. What is provisioned concurrency, and when is it worth paying for?
Provisioned concurrency keeps a set number of execution environments initialized ahead of time, so requests that land on them skip Init entirely and respond in double-digit milliseconds. It is configured on a version or alias and billed for as long as it is configured, used or not. You can scale it on a schedule or with Application Auto Scaling on utilization.
It is worth it for interactive paths with a cold-start problem you cannot fix in code (checkout, login), rarely for async pipelines. Requests beyond the provisioned amount still get on-demand, possibly cold, environments.
The follow-up: SnapStart or provisioned concurrency? SnapStart is cheaper and needs no capacity guess, but it still restores on demand. Provisioned concurrency gives the most predictable latency. They cannot be used on the same function version.
12. How do memory and CPU relate in Lambda, and how do you tune memory?
You set only memory; CPU scales with it. At 1,769 MB a function has the equivalent of one full vCPU. Below that you get a fraction; above it, more than one.
Because you are billed in GB-seconds, more memory is not automatically more expensive: a CPU-bound function that takes 4 seconds at 512 MB and 1 second at 2,048 MB costs the same, with lower latency. An I/O-bound function waiting on a database gains nothing.
The answer interviewers want is "measure it": run the function at several memory sizes (the open-source Lambda Power Tuning tool automates this) and pick the point on the cost-versus-duration curve that fits.
13. Should you run Lambda on arm64 (Graviton)?
Usually, yes, for new functions. AWS documents better price-performance for arm64 than for equivalent x86_64 functions, and for interpreted runtimes the switch is often a one-line configuration change.
The catch is compatibility: native dependencies (compiled wheels, Node.js native modules, binaries in a layer) and container images must be built for arm64. Roll it out behind a canary alias and compare duration and errors.
Part 3: Concurrency and throttling (14-19)
14. What is concurrency in Lambda, and how do you estimate it?
Concurrency is the number of requests your function is handling at the same moment. For standard Lambda, each execution environment handles one request at a time, so concurrency equals the number of busy environments.
The estimate is Little's law:
concurrency = requests per second x average duration in seconds
100 requests per second at 200 ms needs about 20 concurrent environments. The same traffic at 2 seconds needs 200. That is why a slow downstream dependency is a concurrency problem: when the database slows from 50 ms to 2 seconds, your concurrency rises 40x with no change in traffic and you hit the account limit.
15. How fast does Lambda scale, and what is the account limit?
The default concurrency quota is 1,000 per Region, shared by all functions in the account, and it can be raised to tens of thousands. Separately, each function can add up to 1,000 execution environments every 10 seconds. New accounts may start with lower quotas that AWS raises with usage.
Two consequences: a spike from zero to 5,000 concurrent requests on one function takes tens of seconds to absorb, with the excess throttled; and because the pool is shared, one runaway function can starve every other function in the account - the case for reserved concurrency and separate accounts for critical workloads.
16. What is reserved concurrency, and what are its two jobs?
Reserved concurrency sets aside a number of concurrent executions for one function and also caps it at that number. It costs nothing.
- Guarantee: a critical function always has that capacity, no matter what other functions do.
- Limit: a function cannot scale past it, which protects a downstream system - a database that accepts 100 connections, a partner API with a rate limit.
You cannot reserve everything: the account keeps at least 100 units unreserved for functions without a reservation. And reservations are taken from the shared pool whether used or not - reserving 100 for function-a leaves 900 for everyone else even when function-a is idle.
aws lambda put-function-concurrency \
--function-name orders-consumer \
--reserved-concurrent-executions 50
17. What happens when a function is throttled? Does it depend on the invocation type?
Yes, completely - this is the question where the invocation-model table pays off.
- Synchronous: the caller gets a 429
TooManyRequestsException immediately. Lambda does not retry. API Gateway passes an error to the client; your SDK client may retry with backoff.
- Asynchronous: Lambda puts the event back in its internal queue and keeps retrying with exponential backoff for up to 6 hours by default (the maximum event age). Events that age out are discarded unless you configured a DLQ or an on-failure destination.
- Event source mapping: for SQS, Lambda backs off and the message stays in (or returns to) the queue; for streams, the shard stops advancing and
IteratorAge grows until processing resumes.
So a throttle on an async or queue-backed path is a delay, not a loss - until the retention window runs out.
18. How do you deliberately stop a function from running, in an emergency?
Set its reserved concurrency to 0. Every invocation is then throttled: synchronous callers get 429s, event source mappings stop delivering, and async events go to the DLQ or on-failure destination without retries.
It is the kill switch for a function writing bad data or looping. For an SQS consumer, disabling the event source mapping is gentler: messages wait in the queue instead of burning receives toward the DLQ.
19. What is the difference between reserved concurrency and SQS maximum concurrency?
Reserved concurrency is a function-level cap. Maximum concurrency is an event-source-mapping-level cap on how many concurrent invocations one SQS queue can drive (2 to 1,000 in the console).
The difference matters. If you cap an SQS consumer with reserved concurrency alone, Lambda's pollers can still pull messages faster than the function can accept them, invocations get throttled, and messages bounce back to the queue - and with a low maxReceiveCount they land in the DLQ without ever failing. Maximum concurrency stops the poller from over-fetching in the first place. If you use both, keep reserved concurrency at or above the sum of maximum concurrency across all queues mapped to the function.
Part 4: Errors, retries and idempotency (20-25)
20. How does Lambda retry a failed asynchronous invocation?
When the function returns an error (including a timeout), Lambda retries twice by default: about one minute after the first failure, then about two minutes after the second. You can set retry attempts from 0 to 2 and the maximum event age up to 6 hours.
Throttles and Lambda-side 500-series errors are different: they are retried for up to the maximum event age with exponential backoff, up to 5 minutes between attempts.
aws lambda put-function-event-invoke-config \
--function-name thumbnailer \
--maximum-retry-attempts 1 \
--maximum-event-age-in-seconds 3600
After the last attempt, the event is discarded unless you configured a DLQ or an on-failure destination. Lambda's async queue is also eventually consistent, so even a successful function can receive the same event twice.
21. What is the difference between a dead-letter queue and a Lambda destination?
Both catch async events that failed all attempts, but:
| DLQ | On-failure destination |
|---|
| Targets | SQS standard queue or SNS standard topic | SQS, SNS, Lambda, EventBridge, S3 (failure only) |
| Content | Only the original event | The event plus invocation record: error, stack, attempt count, request context |
| Success path | None | On-success destination available too |
Destinations are the modern choice because the failure record tells you why it failed, not just what failed. The same idea exists for event source mappings: Kinesis, DynamoDB Streams and Kafka mappings support an on-failure destination for discarded batches. SQS mappings do not - for SQS, the DLQ belongs on the source queue via its redrive policy.
FIFO queues and topics are not supported as async destinations; the DestinationDeliveryFailures metric is how you notice that configuration error.
22. Who handles retries for a synchronous invocation?
The caller. Lambda runs the function once and returns the error. If API Gateway is in front, the client sees a 5xx and decides whether to retry; if your service calls Invoke through the SDK, the SDK's retry policy applies (and it will retry throttles and some network errors, which means your function can run twice even on a synchronous path).
The implication: a synchronous chain has no durable retry anywhere. If losing the request matters, persist it to SQS and process it asynchronously, returning 202.
23. Why must Lambda functions be idempotent, and how do you make them so?
Because every delivery path is at least once. Async invocations can be delivered twice, SQS standard queues redeliver after visibility timeouts, stream batches are retried from a checkpoint, and SDK clients retry. AWS's own documentation tells you to make event-source-mapping consumers idempotent.
The standard pattern is an idempotency key with a conditional write:
import boto3
from botocore.exceptions import ClientError
ddb = boto3.client("dynamodb")
def handler(event, context):
key = event["orderId"]
try:
ddb.put_item(
TableName="processed",
Item={"pk": {"S": key}},
ConditionExpression="attribute_not_exists(pk)",
)
except ClientError as e:
if e.response["Error"]["Code"] == "ConditionalCheckFailedException":
return {"status": "duplicate-skipped"}
raise
charge_customer(event)
The follow-up: "what if the function crashes after the put and before the charge?" Then the key is recorded but the work never happened. Production versions store a status (IN_PROGRESS with an expiry, then COMPLETED with the result) so a retry after a crash can take over. The Powertools for AWS Lambda idempotency utility implements exactly that pattern.
24. What is the right idempotency key?
Something derived from the business event, stable across retries: an order ID, a payment intent ID, a client-supplied Idempotency-Key header, or a hash of the meaningful payload fields.
Wrong answers: a timestamp, or a UUID generated in the handler. The SQS message ID catches SQS redeliveries but not a producer sending the same event twice - only a business key does.
Give the record a TTL so the table does not grow forever, sized to cover the longest redelivery window you have.
25. What is Lambda recursive loop detection?
Lambda tracks invocation chains and, if a function is invoked about 16 times in the same chain of requests, stops the next invocation and notifies you. It detects loops between Lambda, SQS, SNS, S3 and EventBridge custom event buses, and requires a recent enough AWS SDK in the function.
The classic case is an S3-triggered function writing back to the same bucket. Loops through untracked services, such as DynamoDB Streams, are not caught. The real fix is design (a different bucket or prefix), with invocation and billing alarms as the backstop.
Part 5: SQS, Kinesis and DynamoDB Streams (26-32)
26. How does an SQS event source mapping work?
Lambda runs pollers that long-poll the queue, gather messages into batches and invoke your function synchronously with each batch. If the function succeeds, Lambda deletes the messages. If it fails, it does nothing; the messages become visible again when the visibility timeout expires and are retried, and after maxReceiveCount receives SQS moves them to the queue's DLQ.
Key settings:
- Batch size: up to 10,000 for standard queues, 10 for FIFO. Above 10 you must set a batching window of at least 1 second.
- Batching window: how long to wait to fill a batch (standard queues).
- Scaling: Lambda starts with five concurrent batches and adds up to 300 more concurrent invokes per minute, up to 1,250 by default. Provisioned mode adds dedicated pollers for faster scaling and much higher throughput.
27. How should you set the SQS visibility timeout for a Lambda consumer?
At least six times the function timeout, plus the batching window. Lambda refuses to create the mapping if the function timeout exceeds the visibility timeout.
Why six times: Lambda may receive a batch and then be throttled before it can invoke the function, and it needs time to retry within the same visibility window. If the visibility timeout is too short, a message becomes visible again while the first invocation is still processing it, and a second invocation picks it up - double processing that looks like a bug in your code.
Also set maxReceiveCount on the redrive policy to at least 5, so a few throttles do not send healthy messages to the DLQ.
28. One bad message keeps failing the whole SQS batch. How do you fix it?
By default, any error fails the whole batch, and all of it - including the messages that succeeded - returns to the queue. The fix is partial batch responses: enable ReportBatchItemFailures on the mapping, catch errors per message, and return only the failed message IDs.
def handler(event, context):
failures = []
for record in event["Records"]:
try:
process(record)
except Exception:
failures.append({"itemIdentifier": record["messageId"]})
return {"batchItemFailures": failures}
aws lambda update-event-source-mapping \
--uuid "<mapping-uuid>" \
--function-response-types ReportBatchItemFailures
Gotchas: if the handler throws instead of returning, the whole batch fails anyway; and the response is ignored unless the setting is enabled on the mapping. A message that fails every time still ends up in the DLQ after maxReceiveCount receives, which is what you want.
29. How does Lambda process SQS FIFO queues?
In order within each message group ID. A batch may contain several groups, but order within each group is preserved, and if the function fails, Lambda retries the affected messages before it delivers more from that group.
Consequences: concurrency is capped at the number of active message groups (or the maximum concurrency setting, whichever is lower), so one group ID for everything means one consumer. With partial batch responses on FIFO, stop at the first failure and report it plus every unprocessed message after it, or you break ordering.
30. How do Kinesis and DynamoDB Streams event source mappings differ from SQS?
Streams are ordered logs, not queues. Lambda reads each shard in order and processes one batch per shard at a time by default. Nothing is deleted on success; Lambda checkpoints its position in the shard.
So the failure mode is different. If a batch fails, Lambda retries that same batch and the shard stops advancing - by default until the records expire from the stream (24 hours for DynamoDB Streams; the Kinesis retention period, 24 hours by default and configurable much longer). One poison record can block a shard for a day while IteratorAge climbs. In SQS, a poison message only blocks itself.
31. How do you stop one bad record blocking a stream shard?
Configure the mapping's error handling, which defaults to retry-forever:
MaximumRetryAttempts and MaximumRecordAgeInSeconds - give up on a batch after N retries or once records are old enough.
BisectBatchOnFunctionError - split a failing batch in two and retry each half, homing in on the bad record.
ReportBatchItemFailures - return the sequence number of the first failed record; Lambda checkpoints before it and retries from there. If you return several, the lowest sequence number wins.
- On-failure destination (SQS, SNS, S3 or Kafka) - receives the details of discarded batches so you can replay them.
Combine them, and alarm on IteratorAge.
32. Your Kinesis consumer has a high IteratorAge. How do you speed it up?
IteratorAge is how far behind the newest record your consumer is. Options, in rough order:
- Make the function faster - batch writes downstream, raise memory if CPU-bound, remove per-record network calls.
- Raise
ParallelizationFactor (1 to 10) - Lambda processes up to that many batches per shard concurrently while preserving order per partition key. 100 shards at factor 2 can drive 200 concurrent invocations.
- Tune batch size and batching window - larger batches amortize per-invoke overhead.
- Add shards - more shards, more parallel batches.
- Enhanced fan-out - a dedicated read pipe per consumer, if several consumers are competing for the shard's shared read throughput.
Check for throttling first: a capped consumer looks exactly like a slow one.
Part 6: Front doors, orchestration and events (33-38)
33. API Gateway, function URLs or an Application Load Balancer - how do you choose?
| API Gateway (REST / HTTP API) | Function URL | ALB |
|---|
| Auth | IAM, Cognito, JWT, Lambda authorizers, API keys (REST) | AWS_IAM or NONE | OIDC/Cognito on listener rules |
| Extras | Throttling, usage plans, request validation, WAF, custom domains, caching (REST) | Response streaming, little else | Path/host routing to mixed targets (Lambda, ECS, EC2) |
| Best for | Public APIs that need management features | Webhooks, internal tools, a single-function backend | Mixing Lambda into an existing ALB-fronted estate |
A function URL with NONE is a public endpoint governed only by the function's resource policy, so it needs its own auth in code. The follow-up is often "put CloudFront in front" - it adds caching, a custom domain and WAF to any of the three.
34. What is the API Gateway timeout problem?
API Gateway REST APIs have a default integration timeout of 29 seconds. For Regional and private REST APIs you can request a higher value through Service Quotas (AWS notes it may require lowering your Region-level throttle quota); for edge-optimized APIs it cannot be raised. A Lambda function behind it may have a 15-minute timeout, but the client gets a 504 after the integration timeout regardless, while the function keeps running and billing.
The answer interviewers want is to not make long work synchronous: accept the request, enqueue it (SQS, or start a Step Functions execution), return 202 with a job ID, and let the client poll or receive a webhook or WebSocket notification. Set the function timeout below the integration timeout for genuinely synchronous handlers so they fail fast.
35. When would you use Step Functions instead of chaining Lambdas?
Whenever a workflow has more than one step and you care about retries, branching, waiting, or visibility. Step Functions gives you per-step retry and catch policies, parallel and map states, waits (up to a year in Standard), human approval via task tokens, and a visual execution history - all of which you would otherwise build badly in code.
| Standard | Express |
|---|
| Max duration | One year | Five minutes |
| Semantics | Exactly-once workflow execution | Async: at-least-once; Sync: at-most-once |
| Billing | Per state transition | Per execution, duration and memory |
| Use for | Long-running, auditable, non-idempotent steps (payments) | High-volume, short, idempotent event processing |
Express does not support .sync job runs or task-token callbacks. The type cannot be changed after you create the state machine.
36. Why is Lambda calling Lambda synchronously considered an anti-pattern?
Because you pay twice for the waiting: the caller is billed for its duration while it blocks on the callee. Errors and timeouts compound - the caller must handle the callee's throttles, timeouts and retries, and nested timeouts must shrink at each level. Coupling is tight, and a spike doubles concurrency usage.
Alternatives: share the logic as a library and call it in-process; orchestrate with Step Functions; or go through SQS or EventBridge when the caller does not need the result. An occasional synchronous call to a deliberately isolated function is fine - as a decision, not a habit.
37. What is EventBridge and when do you pick it over SNS or SQS?
EventBridge is an event bus: producers put events, rules match on event content (source, detail-type, any field in the payload) and route to targets. It also receives AWS service events and SaaS partner events, supports archive and replay, and has a schema registry.
Pick SQS for buffering one consumer's work, SNS for simple push fan-out, and EventBridge for content-based routing across many producers, consumers and accounts. Common pairing: EventBridge routes to an SQS queue per consumer, and Lambda reads from the queue - you get routing plus buffering. For delivery failures, a classic EventBridge rule target retries for 24 hours and up to 185 times by default; configure a DLQ on the target or failed events are dropped.
38. What are Lambda layers versus container images?
Layers are zip archives of libraries, a custom runtime or shared code, extracted into /opt and attached to a zip-packaged function (up to five). They share dependencies across functions and keep deployment packages small, but the 250 MB unzipped limit includes layers, versioning gets awkward across many consumers, and a layer is not a substitute for a real package manager.
Container images package the function as an OCI image up to 10 GB, stored in ECR. Use them for large dependencies (ML libraries), when your team's build pipeline is already Docker-based, or when you need specific system packages. Layers do not apply to container images - you put everything in the image.
An image is not a long-running container: it is the same event-driven function in a different package format.
Part 7: Networking, security, observability and cost (39-45)
39. What happens when you attach a Lambda function to a VPC?
Lambda creates a Hyperplane ENI in your subnet for each unique subnet and security group combination, and functions sharing that combination share the ENI (each supports up to 65,000 connections). The old one-ENI-per-environment model, with its slow VPC cold starts, is long gone.
What changes for you:
- The function can reach private resources as its security group permits.
- It loses default internet access. Only VPC routes apply.
- Creating the ENI puts the function in
Pending for a short time at create or update, and a VPC function idle for 14 days goes Inactive and has to re-create its ENI on the next invoke.
- The execution role needs the EC2 network-interface permissions (the
AWSLambdaVPCAccessExecutionRole managed policy).
40. A VPC-attached function times out calling DynamoDB or an external API. Why?
Because it has no route to the internet. Lambda functions in a VPC never get public IP addresses, so putting them in a public subnet does not help.
Fixes:
- For AWS services: VPC endpoints - a gateway endpoint for DynamoDB and S3 (no hourly charge), interface endpoints for most other services.
- For the internet: put the function in private subnets routed through a NAT gateway in a public subnet.
The deeper question is whether it needs to be in the VPC at all. A function that only talks to DynamoDB, S3 and public APIs should not be VPC-attached.
41. What is the difference between the execution role and the resource-based policy?
They answer two different questions:
- Execution role (identity policy): what can this function do? It is assumed when the function runs and grants it, for example,
dynamodb:PutItem on one table and sqs:ReceiveMessage on its source queue. Event source mappings use it - Lambda polls SQS as the function's role.
- Resource-based policy on the function: who can invoke this function? It grants
lambda:InvokeFunction to a service principal (S3, SNS, EventBridge, API Gateway) or another account, ideally with SourceArn or SourceAccount conditions to prevent confused-deputy access.
aws lambda add-permission \
--function-name thumbnailer \
--statement-id s3-invoke \
--action lambda:InvokeFunction \
--principal s3.amazonaws.com \
--source-arn arn:aws:s3:::uploads-bucket \
--source-account 111122223333
Push sources (S3, SNS, EventBridge) need the resource policy; pull sources (SQS, Kinesis) need the execution role.
42. How should a Lambda function handle secrets?
Not in plaintext environment variables, and never in code. Store them in Secrets Manager or SSM Parameter Store (SecureString), grant the execution role read access to that one secret, and fetch at Init with a cache and a TTL so a rotated secret is picked up without a redeploy. The AWS Parameters and Secrets Lambda extension does the caching for you.
Environment variables are fine for non-sensitive configuration, but anyone with lambda:GetFunctionConfiguration can read them. The bigger security lever is least privilege: one execution role per function, scoped to its resources.
43. How do you observe Lambda functions in production?
Three pillars plus Lambda-specific metrics:
- Metrics:
Invocations, Errors, Throttles, Duration (watch p99, not average), ConcurrentExecutions, and for consumers IteratorAge and async DeadLetterErrors / DestinationDeliveryFailures. Also the source side - SQS ApproximateAgeOfOldestMessage.
- Logs: structured JSON to CloudWatch Logs with a correlation ID, queried with Logs Insights. The
REPORT line per invocation includes duration, billed duration, memory used and init duration on cold starts.
- Traces: X-Ray or OpenTelemetry to see where the time went across API Gateway, Lambda and downstream calls.
Alarm on throttles and queue age, not just errors - a throttled async function produces no errors, only a growing backlog.
44. How is Lambda priced, and how do you reduce the bill?
You pay per request plus per GB-second of duration, metered in milliseconds, with the Init phase included in billed time. Provisioned concurrency and SnapStart (for non-Java runtimes) add their own charges. Data transfer, NAT gateway processing and CloudWatch Logs ingestion are billed separately, and are often the surprise line items.
Levers:
- Right-size memory using measurements (question 12).
- Move to arm64 where dependencies allow.
- Batch and filter at the event source mapping.
- Cut log volume and set log retention - CloudWatch Logs can cost more than the compute.
- For constant high-throughput load, compare against Fargate.
45. What is event filtering on an event source mapping and why does it matter?
You can attach filter criteria (JSON patterns on the message body or metadata) to SQS, Kinesis, DynamoDB Streams and other mappings, and Lambda only invokes the function for records that match.
{ "Filters": [ { "Pattern": "{\"body\": {\"type\": [\"order.created\"]}}" } ] }
Filtering in code means paying for an invocation only to return immediately. The gotcha: for SQS, messages that do not match the filter are deleted from the queue, not left for another consumer. If two consumers need different subsets of the same queue, give each its own queue (SNS or EventBridge fan-out) instead.
Part 8: Scenarios - design or debug this (46-50)
46. Design: an image upload must be resized into three sizes, indexed for search, and the user notified. How do you fan out?
- Client uploads directly to S3 with a presigned URL - never through Lambda, because of the 6 MB payload limit.
- The S3 event goes to EventBridge (or SNS), which fans out to one SQS queue per consumer: resize, index, notify.
- Each queue has its own Lambda consumer, its own DLQ, its own concurrency settings, and its own idempotency key (the object key plus version ID).
Why not one Lambda that does all three? A failure in indexing would retry the resizing, and you could not scale or throttle the steps independently. Why queues? Backpressure - if search is down, the index queue fills and drains later. Write resized images to a different prefix so the function cannot trigger itself.
47. Debug: customers are being charged twice. The charge happens in a Lambda that consumes an SQS queue. What do you check?
Work through the at-least-once causes in order:
- Visibility timeout shorter than processing time - the message reappears mid-processing and a second invocation takes it.
- Whole-batch failure - one bad message fails the batch and the successful charges are retried. Enable
ReportBatchItemFailures.
- Producer duplicates - the producer retried
SendMessage on a timeout, so two copies have different message IDs.
- Errors after the side effect - the charge succeeds, a later step throws, the message is retried.
The fix is idempotency on a business key (the payment ID), also passed to the payment provider as its idempotency key. Then refund the existing duplicates.
48. Debug: an SQS consumer's DLQ is filling up, but the function logs show no errors. What is happening?
Throttling. The pollers receive messages, the function is throttled (reserved concurrency too low, or the account limit is exhausted by another function), the receives count against maxReceiveCount without the function ever running, and after enough receives SQS moves the message to the DLQ. No invocation means no error log.
Confirm with the function's Throttles metric and ConcurrentExecutions against its reserved concurrency. Fixes:
- Use the mapping's maximum concurrency instead of (or below) reserved concurrency, so the pollers do not over-fetch.
- Raise
maxReceiveCount to at least 5 and set the visibility timeout to at least six times the function timeout.
- If another function is eating the account pool, give this one a reservation or raise the account quota.
Then redrive the DLQ to the source queue - the messages were never bad.
Almost certainly cold starts on the morning ramp: overnight traffic was low, warm environments were reclaimed, and the first wave of users lands on new environments that run Init. The REPORT log lines with an Init Duration field and the p99 Duration metric will confirm it.
Ranked fixes:
- Shrink Init - smaller package, lazy imports, no heavy work at startup.
- Enable SnapStart if the runtime supports it.
- Provisioned concurrency on a schedule (Application Auto Scaling scheduled actions) that ramps up before the morning peak and back down afterwards - you pay for warm capacity only around the spike.
Also check for a morning batch job consuming account concurrency - it produces the same graph.
50. Debug: after a traffic spike, your Lambda functions started failing with "too many connections" from the RDS database. What do you do?
Each execution environment holds its own database connection. A spike to 800 concurrent environments means 800 connections, which exceeds what the database allows, and new connections fail. Lambda scaled correctly; the database could not.
Fixes, usually in combination:
- RDS Proxy in front of the database - it pools and multiplexes connections, so hundreds of environments share a much smaller pool. The function connects to the proxy endpoint, ideally with IAM authentication.
- Reserved concurrency on the function as a hard cap matched to what the database can serve.
- If the traffic is a write path, put a queue in front and drain it at a controlled concurrency, rather than letting synchronous traffic hit the database directly.
- Make sure connections are created outside the handler and reused, not opened per invocation.
"Why not just raise max_connections?" Each connection costs database memory, and the next spike will be bigger. Lambda's scaling must be bounded by the slowest thing it depends on.
How to practice for an AWS Lambda interview
- Learn the invocation-model table cold. For every failure question, say which model you are in and who retries. That sentence answers half of Part 4.
- Build the three consumers yourself. An async function with an on-failure destination, an SQS consumer with partial batch responses, and a stream consumer with bisect and a bounded retry. Break each on purpose and watch where the event goes.
- Reproduce a double-process. Set a visibility timeout shorter than the function's duration, watch the duplicate appear in the logs, then fix it with a conditional write.
- Read your own
REPORT lines. Billed duration, memory used and init duration answer most cost and cold-start questions.
- Draw the scenarios before you speak. Producer, bus, queue, function, DLQ, downstream. Interviewers score the shape of the answer as much as the service names.
Keep going
- Drill it under pressure: the AWS and cloud architecture question banks, plus the free daily challenge with no account needed.
- Go deeper on the patterns: the message queue and API gateway design walkthroughs cover the infrastructure Lambda sits between.
- Study for the cert at the same time: the Developer Associate practice exam leans hard on Lambda, event sources and error handling.
- Round out the loop: Top 50 AWS interview questions, Top 50 microservices interview questions and Top 30 system design interview questions.
- Practise the follow-ups: the chat-based AI mock interview probes your reasoning with "and what if the message is delivered twice?" follow-ups.
- Related guides: Top 50 DynamoDB Interview Questions 2026, Top 40 AWS CDK Interview Questions 2026, Top 50 Distributed Systems Interview Questions 2026.
#aws #lambda #serverless #cloud #interviews #career