DVA-C03 Lambda Development: Configuration, VPC Access, Error Handling and Event Sources
Developing code for AWS Lambda means writing a handler and configuring how Lambda runs it: its memory, timeout, concurrency, environment variables, layers and extensions, its network path to private resources, and how it is invoked and recovers from errors. This lesson covers DVA-C03 Task 1.2 end to end: the execution environment and handler, each configuration setting and the symptom it fixes, VPC access with NAT, VPC endpoints and RDS Proxy, the three invocation types, asynchronous retries with destinations and dead-letter queues, event source mappings for SQS, Kinesis and DynamoDB Streams, and API Gateway proxy integration.
On this page10 sections
- How Lambda runs your code: handler, runtime and the execution environment
- Memory, CPU, timeout and ephemeral storage
- Environment variables, versions and aliases
- Concurrency: reserved versus provisioned
- Layers and extensions
- Reaching private resources in a VPC
- Invoking functions: synchronous, asynchronous and polling
- Asynchronous invocations: retries, destinations and dead-letter queues
- Event source mappings: SQS, Kinesis and DynamoDB Streams
- API Gateway Lambda proxy integration
- Set the handler for each runtime and use initialisation code, warm environments and /tmp correctly
- Match memory, timeout, ephemeral storage, reserved concurrency and provisioned concurrency to the problem each one solves
- Manage configuration with environment variables, versions and aliases, and move large or changing settings to Parameter Store or AppConfig
- Package shared code as layers with the right folder structure and add extensions such as the AWS Parameters and Secrets Lambda Extension
- Give a function access to private VPC resources, the internet (NAT) and AWS services (gateway and interface endpoints), and pool database connections with RDS Proxy
- Handle failures for asynchronous invocations with retries, maximum event age, destinations and dead-letter queues
- Configure event source mappings for SQS, Kinesis and DynamoDB Streams, including partial batch responses and stream failure controls, and integrate with API Gateway
How Lambda runs your code: handler, runtime and the execution environment
Lambda runs your code by calling one entry point, the handler, inside an execution environment that it creates, reuses for later invocations and eventually discards. The runtime setting chooses the language environment (a managed runtime such as Python, Node.js, Java or .NET, or an OS-only runtime such as provided.al2023 for Go, Rust or a custom runtime), and the handler setting tells that runtime which function to call.
| Runtime | Handler format | Example |
|---|---|---|
| Python | file.function (file name without .py) | billing.handle for def handle(event, context) in billing.py |
| Node.js | file.export | index.handler |
| Java | package.Class::method | com.example.Billing::handleRequest |
The lifecycle: init, invoke, reuse
A new environment first runs the init phase: it starts the runtime, starts any extensions and runs all code outside the handler (imports, module-level variables, SDK clients). That one-off cost is the cold start. Each invocation then runs only the handler. After an invocation, Lambda keeps the environment warm and may send it the next request, so:
- Objects created outside the handler (SDK clients, database connections, loaded configuration) are reused by later invocations in the same environment. Moving expensive setup out of the handler is the standard way to cut warm-invocation duration.
- Module-level variables keep their values between invocations in that environment, so a global list that you append to keeps growing. Per-request state belongs inside the handler.
- Files written to
/tmpstay there for the life of the environment./tmpis not shared between concurrent environments, and Lambda never saves memory state to it for you.
Lambda never promises reuse: each concurrent request gets its own environment, and environments are recycled at any time. Treat reuse as an optimisation (a cache), never as storage. For Java, Python and .NET, Lambda SnapStart is another cold-start option that resumes new environments from a snapshot of an initialised one.
Memory, CPU, timeout and ephemeral storage
Memory is the one performance dial in Lambda: you set memory from 128 MB to 10,240 MB, and Lambda allocates CPU in proportion to memory. At 1,769 MB a function gets the equivalent of one full vCPU, and more memory gives more vCPUs. A CPU-bound function that uses little of its memory still speeds up when you raise the memory setting, often for a similar or lower cost because it finishes faster.
| Setting | What it controls | Range | What it does not fix |
|---|---|---|---|
| Memory | RAM and proportional CPU | 128 MB – 10,240 MB | Disk space in /tmp; jobs longer than the timeout ceiling |
| Timeout | How long one invocation may run before Lambda stops it (Task timed out) | 1 s – 900 s (15 minutes); default 3 s | Slow CPU-bound code (that needs memory) |
| Ephemeral storage | Size of /tmp | 512 MB (default) – 10,240 MB | Memory or CPU; it is a separate setting |
Match the symptom to the setting. A No space left on device error while writing a file means /tmp is too small: raise ephemeral storage; raising memory does not enlarge /tmp, and a layer cannot add disk. A Task timed out after 900.00 seconds error means the job hits the hard 15-minute ceiling, which no setting raises. Split the work into smaller invocations (for example, an AWS Step Functions workflow that runs batches) or move it to a container on Amazon ECS with AWS Fargate. More memory only helps if the time is spent on CPU, not waiting on an API.
Environment variables, versions and aliases
Environment variables are key-value settings stored in the function's configuration and read by your code at runtime (for example os.environ['TABLE_NAME']). They let one deployment package run in development, test and production with a different bucket name, table name or log level in each, with no rebuild. Lambda also sets reserved variables of its own, such as AWS_REGION and AWS_LAMBDA_FUNCTION_NAME.
- Size: all of a function's environment variables together are limited to 4 KB. The limit cannot be raised, and splitting a large document across several variables does not help because the total is what counts.
- Encryption: Lambda encrypts environment variables at rest with an AWS managed KMS key by default; you can choose a customer managed key instead to control its key policy and rotation. Encryption helpers in the console can also encrypt values client-side so they are hidden in the console, and the code then decrypts them. Secrets are still better kept in Secrets Manager than in variables (see the next sections).
Larger or frequently changed configuration
When configuration is too large for 4 KB, or must change without touching the function at all, keep it outside the function and read it at runtime: an AWS Systems Manager Parameter Store parameter (standard parameters hold 4 KB, advanced parameters 8 KB), AWS AppConfig read through the AppConfig Agent Lambda extension, or AWS Secrets Manager for secrets. A file inside a layer or the package is not changeable without a new deployment.
Versions freeze configuration
Publishing a version takes an immutable snapshot of the code and the configuration, including environment variables, of $LATEST. You cannot edit a published version. An alias (such as prod) is a named pointer to a version; clients invoke the alias ARN, so you promote or roll back by moving the alias. Editing a variable in the console changes only $LATEST. To get it into production safely, publish a new version and point the alias at it; pointing the alias at the old version again is the rollback. Keep production aliases on published versions so every release has a fixed, known rollback point.
Concurrency: reserved versus provisioned
Concurrency is the number of requests a function is handling at the same moment, and therefore the number of execution environments it is using. All functions in a Region share the account's concurrency quota. Two controls look alike but solve different problems:
| Reserved concurrency | Provisioned concurrency | |
|---|---|---|
| Purpose | Guarantee capacity for a function and cap its maximum | Keep environments initialised in advance to remove cold starts |
| Set on | The function (all versions) | A published version or alias, never $LATEST |
| Effect of the value | The function can never exceed it; other functions cannot use it. 0 throttles every invocation (an emergency stop) | That many environments are kept warm; traffic above it still scales with normal (cold) environments |
| Cost | No extra charge | Charged for the configured environments while they are provisioned |
| Scheduling | — | Application Auto Scaling (scheduled or target tracking) |
Use reserved concurrency to protect a downstream system that can take only so many simultaneous connections, while every other function keeps scaling, or set it to 0 to stop a runaway function at once. Use provisioned concurrency on an alias for latency-sensitive APIs, and schedule it with Application Auto Scaling for predictable peaks such as the start of the working day.
One trap: when the trigger is an SQS event source mapping, a low reserved concurrency makes Lambda throttle the batches the poller has already received. Those messages return to the queue, each receive counts toward the redrive policy, and valid messages can land in the dead-letter queue unprocessed. To cap an SQS-triggered function, set maximum concurrency on the event source mapping instead.
Layers and extensions
A Lambda layer is a .zip archive of libraries, a custom runtime or other files that you publish once and attach to many functions, so shared code is maintained in one place and each function's package stays small. A function can use up to five layers. Each layer version is immutable: publishing a change creates a new version number, and functions keep the version they reference until you update them. Layer versions can be shared with other accounts through layer permissions.
Lambda extracts layer contents into /opt, and each runtime only searches specific folders there. The folder structure inside the .zip therefore decides whether imports work:
| Layer .zip folder | Extracted to | Used by |
|---|---|---|
python/ (or python/lib/python3.x/site-packages/) | /opt/python | Python import path |
nodejs/node_modules/ | /opt/nodejs/node_modules | Node.js module path |
java/lib/ | /opt/java/lib | Java classpath |
lib/ | /opt/lib | Native libraries (LD_LIBRARY_PATH) |
bin/ | /opt/bin | Executables (PATH) |
A Python library placed at the root of the .zip lands directly in /opt, which is not on the Python path, so imports fail with ModuleNotFoundError. Rebuild the layer with the package under python/.
Container images take no layers. A function deployed as a container image cannot attach layers at all. When migrating, copy what the layers held (shared code and any extension) into the image during the Docker build.
Extensions
A Lambda extension is a companion process that plugs into the execution environment's lifecycle to add monitoring, security or configuration tooling without changing handler code. External extensions run as separate processes (delivered as layers, under /opt/extensions), start during init, can keep working after the function returns its response (for example, flushing telemetry), and are typical for observability vendors' agents. Internal extensions run inside the runtime process (through language-specific environment variables or wrapper scripts).
The AWS Parameters and Secrets Lambda Extension is the AWS-supplied extension for caching Parameter Store parameters and Secrets Manager secrets inside the environment. The code calls a local HTTP endpoint (port 2773 by default) and passes the function's session token in the X-Aws-Parameters-Secrets-Token header; the extension fetches the value on a miss and serves it from cache until its time-to-live expires, which cuts API cost, latency and throttling. Key points:
- It calls Secrets Manager and Parameter Store with the function's execution role, so the role needs
secretsmanager:GetSecretValue(orssm:GetParameter, pluskms:Decryptfor a customer managed key). An access-denied answer from the local endpoint means the role lacks that permission; a resource policy forlambda.amazonaws.comdoes not help. - Cache freshness is set with environment variables:
SECRETS_MANAGER_TTLandSSM_PARAMETER_STORE_TTL(seconds; default 300). Use a short TTL for a secret that rotates often.
Reaching private resources in a VPC
To reach resources that exist only inside your VPC (an RDS database, an ElastiCache cluster, an internal API), you attach the function to that VPC by giving it subnets and security groups. By default a function runs in a Lambda-managed network with internet access but no route into your VPC, so connections to private endpoints time out. Adding IAM permissions or a resource-based policy never fixes a network path.
- Execution role permissions. Lambda creates elastic network interfaces (Hyperplane ENIs) in your subnets, using the function's execution role. The role needs
ec2:CreateNetworkInterface,ec2:DescribeNetworkInterfacesandec2:DeleteNetworkInterface, which the AWS managed policyAWSLambdaVPCAccessExecutionRolegrants among others (it also covers subnet description, private IP assignment and CloudWatch Logs). Without them, saving the VPC configuration fails.AWSLambdaBasicExecutionRolecovers only logging. - Security groups. Allow the function into the target by adding an inbound rule on the target's security group (on the database or service port) whose source is the function's security group. A security-group reference follows the function's network interfaces wherever they are created, so the rule never needs updating as addresses change. Security groups are stateful, so no extra outbound rule is needed on the database for replies.
- Subnets. Choose private subnets in at least two Availability Zones for resilience.
Outbound access from a VPC-attached function
Once attached to a VPC, the function's traffic follows that VPC's routing. Lambda's ENIs never receive public IP addresses, so placing the function in a public subnet with an internet gateway route does not give it internet access, and enabling auto-assign public IPv4 on the subnet does not change that.
| Destination | What the function needs |
|---|---|
| Public internet (a SaaS or payment API) | Run in private subnets whose route table sends 0.0.0.0/0 to a NAT gateway in a public subnet |
| Amazon S3 or DynamoDB, without internet | A gateway VPC endpoint added to the private subnets' route tables (gateway endpoints exist only for these two services) |
| Other AWS services (Secrets Manager, SQS, SNS, KMS, Systems Manager…), without internet | An interface VPC endpoint (AWS PrivateLink) in the subnets with private DNS enabled, so the SDK's normal hostname resolves to the endpoint |
An interface endpoint is for AWS (or PrivateLink partner) services; it cannot reach an arbitrary public API. When a security team forbids any internet egress, endpoints are the answer and NAT is ruled out.
Relational databases: RDS Proxy
Each concurrent execution environment opens its own database connections, so a function that scales to hundreds of environments can exhaust a database's connection limit. Amazon RDS Proxy sits between the function and the RDS or Aurora database, pools and shares connections, and absorbs bursts without changing the database engine or instance size; the code change is only the endpoint it connects to. Reusing a connection opened outside the handler helps per environment but does not cap the total across hundreds of environments; reserved concurrency caps it, but at the cost of throughput.
By default, RDS Proxy reads the database credentials from AWS Secrets Manager for its own connections to the database; since 2025 it can instead use end-to-end IAM authentication, connecting to the database with IAM and needing no secret. Either way, the function can authenticate to the proxy with IAM authentication: enable IAM auth on the proxy, grant the execution role rds-db:connect, and connect with a short-lived token generated by the SDK (for example generate_db_auth_token), so the function never handles the password. (The RDS Data API, an HTTPS alternative to connections, works with Aurora only.)
Invoking functions: synchronous, asynchronous and polling
Every Lambda invocation is one of three kinds, and the kind decides who waits, who retries and which error-handling features apply.
| Synchronous | Asynchronous | Event source mapping (polling) | |
|---|---|---|---|
| How | Caller waits for the result (InvocationType=RequestResponse, the default) | Lambda queues the event and returns 202 at once (InvocationType=Event) | Lambda polls the source and invokes the function synchronously with batches |
| Typical sources | API Gateway, Application Load Balancer, function URLs, Amazon Cognito triggers, SDK default | Amazon S3 event notifications, Amazon SNS, Amazon EventBridge, CloudWatch Logs subscriptions, SDK with Event | Amazon SQS, Kinesis Data Streams, DynamoDB Streams, Amazon MSK/Kafka, Amazon MQ |
| Who retries | The caller (or its SDK) | Lambda (built-in retries) | The mapping, by its own settings |
| Failure handling | The caller's code | Retry attempts, maximum event age, destinations, DLQ | Settings on the mapping (and, for SQS, the queue's redrive policy) |
| Permission to invoke | The function's resource-based policy must allow the calling service (for example apigateway.amazonaws.com) | The function's resource-based policy must allow the calling service (for example s3.amazonaws.com, sns.amazonaws.com) | The function's execution role must be allowed to read the source |
The third InvocationType, DryRun, only checks that the caller is allowed to invoke and runs nothing. InvokeWithResponseStream streams a response back progressively and is still synchronous.
Calling a function from code
Choose Event when the caller should continue as soon as Lambda accepts the request (fire-and-forget): the caller stops paying for idle waiting, and Lambda takes over the retries. Choose RequestResponse when the caller needs the result.
A long synchronous call needs the SDK client configured for it. AWS SDKs have their own read timeouts (boto3's default is 60 seconds) and automatic retries. If the function runs longer than the client's read timeout, the client gives up and retries the call, starting a second run while the first is still going. Set the client's read timeout above the function's longest run and turn off retries for that call:
from botocore.config import Config
import boto3
cfg = Config(read_timeout=310, retries={"total_max_attempts": 1})
client = boto3.client("lambda", config=cfg)
client.invoke(FunctionName="invoice-batch", Payload=b"{}")
(Here the function's own timeout is 300 seconds, so the client waits slightly longer.)
Raising the function's own timeout does not help here: the function was not timing out; the client was.
Asynchronous invocations: retries, destinations and dead-letter queues
For an asynchronous invocation, Lambda puts the event in an internal queue, invokes the function, and on an error retries it up to two more times by default; an event that waits too long is discarded. You tune both on the function's asynchronous invocation configuration: maximum retry attempts (0–2) and maximum event age (60 seconds to 6 hours, default 6 hours). To learn about a failure quickly, lower both: fewer retries and a shorter maximum event age move an undeliverable event to the failure target sooner, so the worst-case delay is bounded by the event age you set. Because events can be delivered more than once, asynchronous handlers should be idempotent.
What happens to an event that finally fails depends on two features that overlap:
| Destinations | Dead-letter queue (DLQ) | |
|---|---|---|
| Triggers on | Success (on-success) and/or failure (on-failure) | Failure only |
| Content sent | An invocation record: the original event, the response or error details, and request context | The original event payload only (with the error code and message as attributes) |
| Targets | SQS queue, SNS topic, EventBridge event bus, another Lambda function; S3 bucket for on-failure | SQS queue or SNS topic |
| Configured | Asynchronous invocation configuration of the function, a version or an alias (and on stream/Kafka event source mappings, failure only) | Function level only; published versions inherit the DLQ of $LATEST |
| FIFO targets | Not supported: FIFO SQS queues and FIFO SNS topics cannot be destinations | Not supported: use a standard queue or topic |
AWS recommends destinations for new work because they carry more context and more targets. Three rules decide most scenarios:
- Function destinations and DLQs apply only to asynchronous invocations. A synchronous
RequestResponsecall never triggers them, so a caller that waits must switch toInvocationType=Eventbefore an on-failure destination will fire. They are configured on the function that fails, not on its caller. - Lambda writes to the destination or DLQ with the function's execution role. The role needs
sqs:SendMessage,sns:Publish,events:PutEventsorlambda:InvokeFunctionon the target. When a DLQ delivery fails, the function'sDeadLetterErrorsmetric rises; the usual cause is a missing permission on the execution role (or KMS access for an encrypted queue). - Polled sources do not use these settings. SQS, Kinesis and DynamoDB Streams handle failures on the event source mapping (next section), and the queue's own visibility timeout or redrive policy has nothing to do with an asynchronous invocation.
Event source mappings: SQS, Kinesis and DynamoDB Streams
An event source mapping is a Lambda resource that polls a queue or stream, gathers records into batches and invokes the function synchronously with each batch. It is how SQS, Kinesis and DynamoDB Streams trigger a function; no EventBridge rule or resource-based policy is involved. The execution role needs permission to read the source; AWS managed policies such as AWSLambdaSQSQueueExecutionRole and AWSLambdaDynamoDBExecutionRole (GetRecords, GetShardIterator, DescribeStream, ListStreams) supply it. Without it, creating the mapping fails.
Amazon SQS
- Deletion is automatic. When the function returns success, the mapping deletes the batch's messages. If the function throws, the whole batch becomes visible again after the visibility timeout and is reprocessed.
- Partial batch responses. Add
ReportBatchItemFailuresto the mapping's function response types and return{"batchItemFailures": [{"itemIdentifier": "<messageId>"}]}. Only the listed messages return to the queue; the rest are deleted. Batch size stays as large as you want. - Visibility timeout. Set the queue's visibility timeout to at least six times the function timeout (plus any batching window). A shorter value lets a message reappear and be processed a second time while the first run is still working.
- Failed messages. Isolate poison messages with a redrive policy on the source queue that moves a message to a dead-letter queue after
maxReceiveCountreceives. The function's asynchronous DLQ and destinations do not apply to SQS mappings. - Throttling. Cap concurrency with the mapping's maximum concurrency (see the concurrency section), not with reserved concurrency.
Kinesis Data Streams and DynamoDB Streams
Stream mappings read each shard in order. If a batch fails, Lambda retries that same batch, and by default it keeps retrying until the records expire from the stream, so the shard is blocked while newer records pile up (the IteratorAge metric climbs). The mapping offers these controls:
| Mapping setting | What it does |
|---|---|
BisectBatchOnFunctionError | Splits a failed batch in two and retries each half, isolating the bad record |
MaximumRetryAttempts | Caps retries (default -1 = retry until the record expires) |
MaximumRecordAgeInSeconds | Drops records older than this (default -1 = no limit) |
| On-failure destination | Where a record that ran out of retries is reported: SQS queue, SNS topic or S3 bucket |
ReportBatchItemFailures | Return the failed record's sequence number as itemIdentifier; Lambda checkpoints and retries from that record onward |
ParallelizationFactor, batch size | Throughput tuning; they do not unblock a poison record |
These settings live on the event source mapping. Configuring retries or an on-failure destination in the function's asynchronous invocation settings has no effect on a stream, which is why a shard can stay blocked even though "a destination is configured". Note what the failure record contains: an SQS or SNS on-failure destination receives metadata only (the stream ARN, shard ID and start and end sequence numbers), not the changed data. To reprocess, read the records from the stream with those positions before they expire (DynamoDB Streams keep records for 24 hours). An S3 on-failure destination also stores the batch contents.
Wiring DynamoDB to Lambda
To run code when items change, enable DynamoDB Streams on the table, then create an event source mapping from the stream to the function. The stream's view type decides what each record carries:
| StreamViewType | Record contains |
|---|---|
KEYS_ONLY | Only the key attributes of the changed item |
NEW_IMAGE | The whole item as it is after the change |
OLD_IMAGE | The whole item as it was before the change |
NEW_AND_OLD_IMAGES | Both the before and after images |
The view type cannot be changed on an existing stream. To change it, disable the stream and enable a new one with the new view type; the new stream has a new stream ARN, so the mapping must be recreated against it. Choose the view type up front from what the consumer needs, before or after images, or both.
Choose the starting position when creating a stream mapping: TRIM_HORIZON (oldest available record) or LATEST (new records only); Kinesis also supports AT_TIMESTAMP. Add filter criteria to invoke the function only for matching records, for example inserts only:
{ "Filters": [ { "Pattern": "{\"eventName\": [\"INSERT\"]}" } ] }
Filtering on the mapping means the function is never invoked for non-matching records, unlike checking eventName inside the handler, which still pays for every invocation.
API Gateway Lambda proxy integration
With Lambda proxy integration, API Gateway passes the whole HTTP request (path, query string, headers, body) to the function as the event and turns the function's return value directly into the HTTP response. The function is therefore responsible for the response format. For a REST API it must return an object like this:
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json",
"Access-Control-Allow-Origin": "https://shop.example.net"},
"body": json.dumps({"message": "ok"}),
}
bodymust be a string (serialise JSON yourself);isBase64Encodedis set for binary bodies. Returning a bare dictionary, a plain string or other keys produces a 502 Bad Gateway with a "malformed Lambda proxy response" error.- CORS: enabling CORS in the console configures the
OPTIONSpreflight (a mock integration). With proxy integration there is no integration response to map headers on, so theAccess-Control-Allow-Originheader on realGET/POSTresponses must come from the function's returned headers. - REST API integrations time out at 29 seconds by default, so long work belongs behind an asynchronous pattern.
Permission to invoke
API Gateway invokes the function under the function's resource-based policy, which needs a statement allowing lambda:InvokeFunction for the principal apigateway.amazonaws.com, normally scoped with a source ARN for the API. The console adds this automatically when you pick a function; the AWS CLI, SDKs and some templates do not, and calls then fail with a 500 and "Invalid permissions on Lambda function" in the execution logs. The fix is aws lambda add-permission (or the template's permission resource). The execution role is the opposite direction: it is what the function itself may call, and it never authorises callers.
Tip. Questions on this task are scenarios about a developer whose function misbehaves or needs a new capability, and you choose the configuration or code change that fixes it, often with a constraint such as least privilege, no code change, no internet egress or keeping throughput. Expect to tell apart near-twin settings: memory versus timeout versus ephemeral storage, reserved versus provisioned concurrency, destinations versus dead-letter queues, function asynchronous settings versus event source mapping settings, execution role versus resource-based policy, and gateway versus interface endpoints versus NAT. Error messages are common clues (Task timed out, No space left on device, ModuleNotFoundError, 502 malformed proxy response, Invalid permissions on Lambda function, CreateNetworkInterface denied), as are CloudWatch metrics such as IteratorAge and DeadLetterErrors. Some items are multiple response, and short SDK, policy or configuration snippets may appear.
- Code outside the handler runs once per execution environment; reuse SDK clients and connections there, but keep per-request state inside the handler.
- Memory also sets CPU; ephemeral storage sizes /tmp; the 15-minute timeout is a hard ceiling, so longer jobs move to Step Functions or containers.
- Environment variables are limited to 4 KB in total and are frozen into each published version; promote changes by publishing a version and moving the alias.
- Reserved concurrency caps and guarantees (0 stops the function); provisioned concurrency pre-initialises environments on a version or alias.
- Layers extract to /opt in runtime-specific folders (python/, nodejs/node_modules/); container-image functions take no layers.
- A VPC-attached function needs AWSLambdaVPCAccessExecutionRole, security-group references, NAT for the internet and VPC endpoints for AWS services; it never gets a public IP.
- RDS Proxy pools connections for highly concurrent functions and lets them authenticate with IAM tokens instead of passwords.
- Destinations and DLQs apply only to asynchronous invocations and are written with the execution role; destinations send a richer record to more targets.
- SQS, Kinesis and DynamoDB Streams failures are handled on the event source mapping: partial batch responses, the queue's redrive policy, bisect, retry limits and on-failure destinations.
- With proxy integration the function returns statusCode, headers and a string body, and API Gateway needs a resource-based policy statement to invoke it.
Frequently asked questions
What is the difference between reserved and provisioned concurrency in AWS Lambda?
Reserved concurrency sets aside part of the account's concurrency for one function and also caps that function at the same number, at no extra cost; setting it to 0 stops all invocations. Provisioned concurrency keeps a number of execution environments initialised on a published version or alias so requests avoid cold starts, and it is billed while configured.
Should I use a Lambda destination or a dead-letter queue?
Use a destination for new work. An on-failure destination receives an invocation record with the original event, the error details and context, and can target SQS, SNS, EventBridge, another function or S3. A dead-letter queue receives only the event payload, only on failure, and only to SQS or SNS. Both apply only to asynchronous invocations.
How does a Lambda function in a VPC access the internet?
A VPC-attached Lambda function reaches the internet only through a NAT gateway: place the function in private subnets whose route table sends 0.0.0.0/0 to a NAT gateway in a public subnet. Putting the function in a public subnet does not work because Lambda's network interfaces never get public IP addresses. For AWS services without internet access, use VPC endpoints instead.
Why does an SQS-triggered Lambda function reprocess messages that already succeeded?
When the function throws an error, the whole batch returns to the queue, including messages that were processed successfully. Enable ReportBatchItemFailures on the event source mapping and return the failed message IDs in batchItemFailures so only those messages are retried. Also set the queue's visibility timeout to at least six times the function timeout to avoid redelivery during processing.
Why does API Gateway return a 502 error from a Lambda proxy integration?
A 502 Bad Gateway with a malformed Lambda proxy response error means the function returned the wrong shape. With proxy integration the function must return an object with statusCode, optional headers and a body that is a string, for example JSON serialised with json.dumps.
How do I stop a bad record from blocking a Kinesis or DynamoDB stream in Lambda?
Configure the event source mapping, not the function's asynchronous settings: turn on bisect batch on function error, set maximum retry attempts or maximum record age, and add an on-failure destination. Optionally use ReportBatchItemFailures and return the failed record's sequence number so Lambda retries from that record onward.
Where should large configuration go if Lambda environment variables are too small?
Lambda environment variables are limited to 4 KB in total and the limit cannot be raised. Store larger configuration in AWS Systems Manager Parameter Store (advanced parameters hold up to 8 KB) or in AWS AppConfig, and read it at runtime so it can change without redeploying the function.
Can a container image Lambda function use layers?
No. Functions deployed as container images cannot use Lambda layers. Copy the libraries and any extensions the layers provided into the image during the Docker build instead.
Source
This lesson covers the "Development with AWS Services" domain of the official DVA-C03 exam guide. Vendors revise their guides — check the source for the current version.
- AWS Certified Developer – Associate (DVA-C03) exam guide — Amazon Web Services
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.