Course outline

15.2

Lambda Limits, Concurrency and Cold Starts

Execution limits, reserved and provisioned concurrency, throttling and retries, cold starts and SnapStart.

26 min Reading + animations

Tip: tap or hover any dotted-underlined term to see what it means.

By the end of this lesson you will be able to

  • Recite the Lambda limits that change a design (memory 128 MB to 10,240 MB, timeout 15 minutes, 6 MB synchronous and 1 MB asynchronous payload, 50 MB zipped / 250 MB unzipped package, 10 GB container image, /tmp 512 MB to 10,240 MB, 4 KB of environment variables, 5 layers) and the standard way round each one.
  • Explain why CPU comes with memory (one vCPU at 1,769 MB, up to six at 10,240 MB) and why a bigger function can cost the same and run faster.
  • Calculate the concurrency a workload needs (requests per second × average duration in seconds), and check it against the account quota, the 10 requests per second per unit rule and the scaling rate of 1,000 new execution environments every 10 seconds per function.
  • Predict what happens to a throttled invocation: 429 for synchronous callers, up to 6 hours of retries and then a destination or DLQ for asynchronous events, the message staying in the queue for SQS.
  • Use reserved concurrency as a guarantee, a cap that protects a downstream database, and a kill switch (0), and know that 100 units always stay unreserved.
  • Explain cold starts (Init, Invoke, Shutdown), shrink them, and choose between provisioned concurrency (with Application Auto Scaling) and SnapStart, including SnapStart’s uniqueness and network-connection caveats.

#Why limits and concurrency matter

Lesson 15.1 sold Lambda as “no servers to manage, scales on its own, pay per request”. All true, but Lambda scales inside a fence. A function may run for at most 15 minutes, accept at most 6 MB from a synchronous caller, and the whole account shares one pool of concurrent executions. Most production incidents with Lambda are not bugs in the code: they are a function hitting one of these fences, or one function eating the pool that another one needed.

This lesson covers the fences (the limits), the pool (concurrency, reserved concurrency, throttling) and the delay of starting new copies (cold starts, provisioned concurrency, SnapStart). The exam asks about all three, usually as a scenario: “payments are throttled during a marketing campaign”, “the first request after deployment is slow”, “a Java function takes seconds to start”.

#The limits that shape a Lambda design

All quotas are per Region (per account). The ones in the first block can never be raised; concurrency can.

LimitValueCan it be raised?What you do when you hit it
Memory128 MB to 10,240 MB, in 1 MB stepsNoSplit the work, or move to Fargate / EC2
Timeout900 seconds (15 minutes) per invocationNoStep Functions to split the job (lesson 15.7), or Fargate (lesson 14.2)
Synchronous payload6 MB request and 6 MB response (200 MB for a streamed response)NoUpload to S3 with a presigned URL and pass the key
Asynchronous payload1 MB per event (256 KB until 2025)NoPut the data in S3, send a pointer
Environment variables4 KB in totalNoParameter Store or Secrets Manager (chapter 21)
.zip package50 MB zipped for a direct upload, 250 MB unzipped including layersNoContainer image, or download big files into /tmp during init
Container image10 GB uncompressedNoFargate for anything bigger
Layers5 per functionNoMerge layers or use a container image
/tmp (ephemeral storage)512 MB default, up to 10,240 MBNoEFS or S3 for shared or permanent data
File descriptors, processes and threads1,024 eachNoClose what you open; fewer threads
Code storage for .zip functions and layers300 GB per RegionNoDelete old versions, or store code in your own S3 bucket
Concurrent executions1,000 per account per RegionYes, to tens of thousandsRequest an increase in Service Quotas, or use reserved concurrency to share it fairly
Scaling rate1,000 new execution environments every 10 seconds, per functionNoProvisioned concurrency for known spikes; buffer with SQS

Three of these numbers trip people up:

  • Synchronous vs asynchronous payload. API Gateway accepts a 10 MB body, but the Lambda behind it accepts only 6 MB. For uploads the answer is always the same: the client puts the file straight into S3 (lesson 10.1, presigned URL) and Lambda gets only the bucket and key. The asynchronous limit was raised from 256 KB to 1 MB in 2025; older questions and slides still say 256 KB.
  • The 15-minute timeout. It is per invocation. Longer work is either split into steps (a Step Functions Map state calls the function once per item) or does not belong in Lambda at all.
  • Package size. The 250 MB unzipped limit includes all layers. A container image raises this to 10 GB. Large files that are only data (a model, a font set) can also be downloaded into /tmp during init, which survives as long as the execution environment is reused.

#Memory buys CPU

Lambda has no CPU setting. You choose memory, and Lambda gives CPU (and network bandwidth) in proportion: at 1,769 MB a function has the equivalent of one full vCPU, and at 10,240 MB up to six vCPUs. At 128 MB it gets a small slice of one.

So “the function uses only 90 MB, give it 128 MB” is often a false saving. Price is per GB-second: if doubling the memory halves the duration, the cost stays the same and the user waits half as long. Past the point where the code can use the extra CPU (a single-threaded program cannot use six vCPUs), more memory only costs more. Measure a few sizes (the open-source AWS Lambda Power Tuning tool does it for you) and pick the knee of the curve.

When a Lambda limit decides the design

A Dhaka e-commerce shop runs its product-photo backend on Lambda. Four everyday requests hit four hard limits: the 6 MB synchronous payload, the 15-minute timeout, the 250 MB unzipped package, and CPU that comes only with memory. Each one has a standard way round it.

Scenario

Synchronous payloads stop at 6 MB: send big files to S3 and pass only the key.

Seller app
API Gateway
photo function
S3 bucketphotos-example
POST 8 MB
Step 1 of 4
1

A seller uploads an 8 MB product video through the API. API Gateway accepts bodies up to 10 MB, so it passes the request on.

#Concurrency: how many at once

Concurrency is the number of requests a function is handling at the same moment. Each in-flight request occupies one execution environment (a small Firecracker microVM with your code loaded). One environment handles one request at a time; when it finishes, it can take the next one. If a new request arrives and every environment is busy, Lambda creates another one, up to the limits.

The rule of thumb from the AWS documentation:

Concurrency = average requests per second × average duration in seconds

  • 100 requests per second, 0.5 s each → 50 concurrent executions.
  • 400 requests per second, 0.25 s each → 100.
  • 2,000 requests per second, 3 s each → 6,000: far above the default 1,000, so most requests would be throttled until the quota is raised.

Notice what moves the result: duration counts as much as traffic. If the database behind a function slows from 100 ms to 1 s, the same traffic needs ten times the concurrency. Many “Lambda throttled us” incidents start with a slow downstream, not a traffic spike.

#Two more ceilings: requests per second and scaling speed

  • 10 requests per second per unit of concurrency (synchronous). Lambda limits each execution environment to 10 invocations per second, so very short functions hit this before they hit concurrency. A 20 ms function at 5,000 requests per second needs only 5,000 × 0.02 = 100 concurrency by the formula, but 100 units allow only 1,000 requests per second: it actually needs 500.
  • Scaling rate. Each function can add up to 1,000 execution environments every 10 seconds (since November 2023; before that there was an account-wide burst of 500 to 3,000 followed by 500 per minute, which older material still quotes). AWS does not publish a time for a given level, but from that rate you can estimate it: zero to 5,000 concurrent takes somewhere around 40 to 50 seconds (the first 1,000 are available at once, and AWS refills the rate continuously rather than in steps), and requests above the current level are throttled meanwhile. Every one of those new environments is also a cold start.

Lambda publishes the metrics you need in CloudWatch (chapter 19): ConcurrentExecutions, Throttles, Duration, Invocations, and for provisioned concurrency ProvisionedConcurrencyUtilization.

Concurrency and throttling calculator

The wallet’s pay-merchant function on Eid eve. Choose the traffic, how long one request takes, the account limit, an optional reserved concurrency, and how much the other functions in the account are already using. Then calculate who gets throttled.

Requests per second

Average duration

Account concurrency limit

Reserved concurrency for this function

Concurrency other functions are using

Press “Calculate” to see the result.

#Throttling: what happens when concurrency runs out

When a request needs a new execution environment and the function may not have one (the account pool is empty, or the function is at its reserved concurrency), Lambda throttles it. What happens next depends entirely on how the function was invoked (invocation types, lesson 15.1).

#Synchronous: 429 straight back to the caller

API Gateway, an ALB, a function URL, Cognito triggers or your own SDK call wait for the answer. A throttle is returned at once as HTTP 429 (TooManyRequestsException, “Rate Exceeded”). Lambda stores nothing: the caller must retry, ideally with exponential backoff and jitter (the AWS SDKs do it for throttles automatically). Through API Gateway the user sees a 429 or 5xx error page. Retries only smooth short spikes; steady overload needs more concurrency.

#Asynchronous: Lambda retries for you, for up to 6 hours

S3 events, SNS, EventBridge and --invocation-type Event calls are handed to Lambda’s internal queue, and the caller gets 202 Accepted immediately. If the attempt is throttled (429) or hits a system error (5xx), Lambda puts the event back and tries again, with gaps that grow from 1 second to at most 5 minutes, for up to 6 hours by default (the maximum event age, configurable from 60 seconds to 6 hours).

A function error (an exception or a timeout in your code) is different: Lambda retries it twice by default (after about 1 minute, then about 2 more), and you can set 0, 1 or 2 retries. An event that runs out of retries or of age goes to the on-failure destination (SQS, SNS, EventBridge, another Lambda function or S3, with the error details) or the older dead-letter queue (SQS or SNS, the event only). Without either, it is dropped silently.

#Event source mappings: the queue is the buffer

For SQS, Kinesis, DynamoDB Streams, MSK and MQ, Lambda’s own pollers read the source and invoke the function synchronously with a batch. When that invoke is throttled, the poller simply backs off:

  • SQS: the messages are not deleted; they become visible again after the visibility timeout and are read again. Set the visibility timeout to at least 6 times the function timeout. A message that keeps failing past the queue’s maxReceiveCount goes to the queue’s own DLQ (lesson 13.2), which is configured on the queue, not on the function.
  • Kinesis and DynamoDB Streams: the shard waits and retries the same batch until it succeeds or the record expires, so a throttled function delays everything behind it in that shard (lesson 13.5).

To cap how many copies one SQS queue may start, use the event source mapping’s maximum concurrency (2 to 1,000) rather than reserved concurrency alone: with maximum concurrency the poller stops reading instead of invoking and being throttled, so messages are not pushed towards the DLQ by throttles. Keep the function’s reserved concurrency at least as high as the total maximum concurrency of its queues.

Throttled: what happens to the request?

When a function has no concurrency left, Lambda does not queue a synchronous call for you. What happens next depends on how the function was invoked: synchronously, asynchronously, or by an event source mapping that polls a queue.

Scenario

The caller gets the error at once and must retry itself.

Wallet app
S3 / SQS
Lambda service
function
on-failuredestination / DLQ
Invoke (sync)
Step 1 of 7
1

The app calls the balance function synchronously and waits for the answer, as API Gateway, an ALB or the SDK do.

#One pool for the whole account

The 1,000 concurrent executions belong to the account in that Region, not to each function. By default every function draws from the same unreserved pool, first come, first served.

#The noisy neighbour

That is the concurrency issue the slides warn about. Suppose a function behind an ALB gets many users and scales to 1,000. Now a function behind API Gateway with few users, and a script calling a third function through the SDK, both get 429, although they barely ran at all. One function’s burst starves all the others: a marketing campaign can take down payments, a runaway retry loop can take down the login.

#Reserved concurrency: a guarantee and a cap

Reserved concurrency sets aside a number of concurrent executions for one function:

  • Guarantee (floor): no other function can use those units. If pay-merchant has 300 reserved, it can always run 300 copies, whatever the campaign does.
  • Cap (ceiling): the function can never use more than its reservation, even when the rest of the pool sits idle. The slides write it as “reserved concurrency (= limit)”; it is both.
  • Cost: free.
  • Side effect: the reservation leaves the shared pool even when the function is idle. With 300 reserved, every other function shares 700.
  • Rule: Lambda always keeps 100 units unreserved for functions without a reservation. With a 1,000 quota you can reserve at most 900 in total across all functions.

The cap side is just as useful as the guarantee:

  • Protect a fragile downstream. A function that opens one database connection per copy, writing to a database that accepts 100 connections, can be capped at, say, 50. (For many functions sharing a database, RDS Proxy is the better tool, lesson 15.3.)
  • Kill switch. Reserved concurrency 0 throttles every new invocation of the function at once, without deleting or redeploying anything. Running invocations finish; remove the setting to switch it back on.
  • Cost control: a cap limits how much a bug or an attack can spend.

If one function needs more than the account allows, no reservation helps: request a quota increase (the slides: “open a support ticket”; today the Service Quotas console).

One concurrency pool for the whole account

A bKash-style wallet runs three functions in one account and Region: payments, a cashback campaign and a nightly report. By default they all draw from the same 1,000 concurrent executions. Watch what one runaway function does to the others, and how reserved concurrency stops it.

Scenario

Three functions share 1,000 concurrent executions on demand.

Customers
API Gateway
Schedule 2 a.m.
Reports DBmax 100 conns
pay-merchant120 in use
cashback-promo40 in use
nightly-report
paymentscashback

Concurrency in use (account limit 1,000)

pay-merchantcashback-promonightly-reportfree in pool
120400840
Step 1 of 2
1

Concurrency is the number of requests a function is handling at the same moment; each one occupies an execution environment. The default quota is 1,000 per account per Region, for all functions together.

#Cold starts

#The execution environment lifecycle

Every execution environment goes through three phases:

  1. Init: Lambda creates the microVM, downloads your code, starts the runtime (Node.js, Python, Java…) and runs your code outside the handler once: imports, reading configuration, creating SDK clients and database connections. On demand, the Init phase may take at most 10 seconds.
  2. Invoke: the handler runs for the request. After it returns, the environment is frozen and kept for reuse; the next request to it skips Init entirely (a warm start).
  3. Shutdown: an environment that stays idle for a while is removed; Lambda also recycles environments every few hours even under load.

A cold start is a request that has to wait for Init. It happens to the first request to every new environment: after a deployment, after a quiet period, and for every extra concurrent request during a spike. Overall, cold starts hit under 1% of invocations, but they cluster at the worst moments (the start of a spike) and they show up in p99 latency rather than the average.

What makes Init slow, roughly from most to least common: a big package and many dependencies, a heavy framework (Spring Boot on Java, large .NET apps), creating many clients or connections, downloading files, and the language itself (Java and .NET start slower than Node.js and Python). Since August 2025, Lambda bills the Init phase for all function types, so a slow init now costs money as well as latency.

#Making cold starts shorter

  • Put expensive setup outside the handler (clients, connections, config): it runs once per environment, not once per request. Never keep data there that must survive; environments disappear.
  • Trim the package: only the dependencies you use, tree-shaking, lighter frameworks.
  • More memory also speeds Init, because CPU comes with memory.
  • Lambda in a VPC used to add seconds for creating an ENI. Since 2019, VPC functions use shared Hyperplane ENIs created when the function is configured, so the VPC no longer adds a noticeable cold start (lesson 15.3). Older material still says “avoid VPC because of cold starts”.
  • When that is not enough: provisioned concurrency or SnapStart, below.

Cold start, warm start

A newspaper’s “latest headlines” function, invoked through API Gateway. The first request to a new execution environment pays for creating it and running the init code; later requests reuse it.

Scenario

New environment: download code, start the runtime, run init, then the handler.

Reader
Lambda service
execution env
DynamoDB
GET /headlines
Step 1 of 6
1

The first request of the morning arrives. No execution environment exists for this function, or all of them are busy.

#Provisioned concurrency

Provisioned concurrency keeps a chosen number of execution environments initialized in advance, so requests that land on them never wait for Init (double-digit millisecond responses).

  • It is set on a published version or an alias, never on $LATEST. Callers must invoke that alias or version, or they do not use it.
  • Lambda runs the init code when it allocates the environments (it takes a minute or two) and again when it recycles them, so the code outside the handler should do as much as possible.
  • Spillover: requests beyond the provisioned number use normal on-demand environments, with normal cold starts, from the unreserved pool (or from the function’s reserved concurrency). They are not throttled unless that pool is empty too.
  • It counts against the account’s concurrency like a reservation: you can provision at most the unreserved quota minus 100, and if the function also has reserved concurrency, provisioned ≤ reserved.
  • It costs money while configured, busy or idle: a charge per GB-second for the provisioned environments (US East: about $0.0000041667 per GB-second) plus a lower duration price when they run ($0.0000097222 per GB-second instead of $0.0000166667). Example: 100 environments of 2 GB for one hour = 100 × 2 × 3,600 = 720,000 GB-seconds ≈ $3.00 per hour before any request.
  • Application Auto Scaling manages the number (register the alias as a scalable target): scheduled scaling for known peaks (raise it 15 minutes before the office rush, lower it after) and target tracking on ProvisionedConcurrencyUtilization (for example 0.7) for gradual changes. Target tracking reacts in minutes, so sudden spikes still spill over meanwhile. The AWS docs suggest provisioning about 10% above the usual peak.

Reserved concurrencyProvisioned concurrency

  • A number of concurrent executions set aside for one function: guaranteed to it, and its ceiling.
  • Environments are still created on demand: cold starts still happen.
  • Protects against noisy neighbours; caps a function to protect a downstream; 0 = kill switch.
  • Set on the function (all versions together).
  • Free.
  • A number of pre-initialized environments, ready before any request.
  • Removes cold starts for requests up to that number; beyond it, spillover to on-demand.
  • Solves latency, not starvation (though it does consume pool units).
  • Set on a version or alias, not $LATEST; scheduled or target-tracking scaling with Application Auto Scaling.
  • Paid for every second it is configured.

Provisioned concurrency: warm before the first request

A Pathao-style ride app’s fare quote API must answer in under 100 ms. Provisioned concurrency keeps a set number of environments initialized on a published version or alias; Application Auto Scaling can change that number on a schedule or by utilization.

Scenario

Init runs before traffic arrives, so no request waits for it.

Riders
API Gatewayalias live
App Auto Scaling
CloudWatchutilization
warm env 1provisionedready
warm env 2provisionedready
Step 1 of 3
1

The team publishes version 7, points the alias live at it and sets provisioned concurrency 2 on the alias. It works only on a version or an alias, never on the unpublished version. After a minute or two, Lambda has created two environments and run their init code.

#SnapStart

SnapStart attacks the other side of the problem: instead of keeping environments warm, it makes starting a new one fast. When you publish a version with SnapStart on, Lambda runs Init once, takes a Firecracker snapshot of the initialized memory and disk, encrypts it and caches several copies. New environments for that version are restored from the snapshot instead of running Init again, often in well under a second instead of several seconds.

  • Runtimes: Java 11 and later, Python 3.12 and later, .NET 8 and later (managed runtimes and their base images). Not Node.js, Ruby or OS-only runtimes.
  • Price: no extra charge for Java. For Python and .NET you pay for caching the snapshot (per GB-second, minimum 3 hours, for as long as the version is active) and for each restore. The slides say “no extra cost for Java, Python and .NET”; only Java is free.
  • Only on published versions (and aliases pointing to them), not $LATEST.
  • Not compatible with provisioned concurrency, EFS, or /tmp larger than 512 MB.

Two caveats appear in exam questions and in real outages:

  • Uniqueness. Everything created during Init is copied into every restored environment. A random seed, a unique ID or a secret generated in Init would be the same in all copies. Generate them after the restore: in the handler, or in an after-restore runtime hook (the afterRestore hook on Java, @register_after_restore on Python).
  • Network connections. A connection opened during Init, at publish time, may be long dead when the snapshot is restored hours later; Lambda does not guarantee its state. Validate and reopen connections after restore. Connections the AWS SDK creates usually recover by themselves. Temporary credentials and cached timestamps fetched in Init should be refreshed too.

SnapStart: init once, restore many times

A Java (Spring Boot) payment-status function takes 6 seconds to initialize. With SnapStart, Lambda runs the init when you publish a version, saves a snapshot of the initialized memory and disk, and resumes new environments from it.

Scenario

The slow init happens at publish time, not on a customer’s request.

Developer
Lambda service
Snapshot cache
execution env
Customer
publish-version
Step 1 of 3
1

SnapStart is switched on for published versions (Java 11+, Python 3.12+, .NET 8+). The developer publishes version 12.

#Which cold-start fix?

SituationBest fitWhy
Java, Python or .NET function with a slow Init, spiky trafficSnapStartFaster starts for every new environment, no always-on cost (free on Java)
Strict latency for every request, predictable peaks, any runtimeProvisioned concurrency with scheduled scalingNo Init at all on the provisioned environments
Node.js function with a slow startProvisioned concurrency (SnapStart does not support Node.js), plus a smaller package
Background or asynchronous processingUsually nothingNobody waits for a few hundred extra milliseconds
Cold start caused by heavy code in the handlerMove the setup outside the handlerRuns once per environment

#What goes wrong

SymptomLikely causeFix
API returns 429 / 5xx during a campaign, other functions are idle-lookingAnother function took the account poolReserved concurrency on critical functions; quota increase
A function is throttled although the account has spare concurrencyIts own reserved concurrency is the capRaise or remove the reservation
Throttles right at the start of a sudden spike, then fineScaling rate (1,000 per 10 s) or cold startsProvisioned concurrency before known spikes; buffer with SQS
Throttles on a very fast function with low concurrency10 requests per second per unit of concurrencyRaise concurrency (reserved or quota)
Asynchronous events disappearNo on-failure destination or DLQ; event older than the maximum event ageConfigure a destination; alarm on DeadLetterErrors / AsyncEventsDropped
SQS messages land in the DLQ although the code is fineThrottling consumed receives; visibility timeout too shortESM maximum concurrency; visibility timeout ≥ 6 × function timeout; higher maxReceiveCount
“Task timed out after 900.00 seconds”Work longer than 15 minutesStep Functions, or Fargate
RequestEntityTooLargeExceptionPayload over 6 MB (sync) or 1 MB (async)Presigned S3 upload; pass a key
Database refuses connections when traffic risesEach copy opens its own connectionReserved concurrency as a cap; RDS Proxy (lesson 15.3)
Cold starts despite provisioned concurrencyCallers invoke $LATEST; or spillover beyond the provisioned numberPoint the trigger at the alias; raise provisioned concurrency or use target tracking
Provisioned concurrency bill with almost no trafficConfigured 24/7Scheduled scaling; minimum capacity low at night
Duplicate IDs or the same “random” numbers after enabling SnapStartUniqueness created in InitGenerate after restore (handler or after-restore hook)
First query after a SnapStart restore failsConnection opened in Init is staleReconnect after restore
A CPU-heavy function is slow at 128 MBCPU comes with memoryRaise memory; measure cost per request

#Lambda concurrency in system design

  • Size it with the formula, then add headroom. Peak requests per second × p95 duration, plus room for a slower downstream. Compare with the account quota, and request increases weeks before a launch, not on the day.
  • Separate critical paths. Reserve concurrency for payments, login and order capture; leave campaigns and batch jobs in the shared pool. For hard isolation, use separate accounts (each has its own quota, chapter 20).
  • Protect what is behind Lambda. Lambda scales faster than databases and third-party APIs. Cap it with reserved concurrency or ESM maximum concurrency, put SQS in front to absorb bursts (lesson 13.1), and use RDS Proxy for relational databases.
  • Prefer asynchronous where nobody is waiting. An S3 or SQS-driven function survives throttling (retries, buffers); a synchronous API turns throttling straight into user errors.
  • Latency budget. User-facing and spiky: provisioned concurrency for the peak, or SnapStart for Java/Python/.NET. Background: accept cold starts.
  • Throttling at the front door. API Gateway has its own throttling (10,000 requests per second per account by default, usage plans per client, lesson 15.6); throttling there is cheaper and friendlier than letting Lambda throw 429s.

#Try it yourself

Reserve concurrency, throttle a function on purpose, then add provisioned concurrency to an alias and scale it on a schedule. Output is shortened; the function fare-quote-example already exists.

REGION=ap-southeast-1
FN=fare-quote-example

# 1. Account quota and what is left unreserved
aws lambda get-account-settings --region $REGION \
  --query '{limit:AccountLimit.ConcurrentExecutions,unreserved:AccountLimit.UnreservedConcurrentExecutions}'
# { "limit": 1000, "unreserved": 1000 }

# 2. Memory (and so CPU), timeout and /tmp size
aws lambda update-function-configuration --function-name $FN \
  --memory-size 1769 --timeout 30 --ephemeral-storage '{"Size": 1024}'
# "MemorySize": 1769, "Timeout": 30, "EphemeralStorage": { "Size": 1024 }

# 3. Reserve 300 for this function: a guarantee and a cap
aws lambda put-function-concurrency --function-name $FN --reserved-concurrent-executions 300
# { "ReservedConcurrentExecutions": 300 }
aws lambda get-account-settings --query 'AccountLimit.UnreservedConcurrentExecutions'
# 700

# 4. Kill switch: 0 throttles every new invocation
aws lambda put-function-concurrency --function-name $FN --reserved-concurrent-executions 0
aws lambda invoke --function-name $FN --payload '{}' --cli-binary-format raw-in-base64-out out.json
# An error occurred (TooManyRequestsException) when calling the Invoke operation (reached max retries: 2): Rate Exceeded.
aws lambda delete-function-concurrency --function-name $FN   # back to the shared pool

# 5. Asynchronous invocation: 202, with retries and a failure destination
aws lambda put-function-event-invoke-config --function-name $FN \
  --maximum-retry-attempts 1 --maximum-event-age-in-seconds 3600 \
  --destination-config '{"OnFailure":{"Destination":"arn:aws:sqs:ap-southeast-1:111122223333:fare-failures-example"}}'
aws lambda invoke --function-name $FN --invocation-type Event --payload '{"from":"Gulshan"}' \
  --cli-binary-format raw-in-base64-out out.json
# { "StatusCode": 202 }

# 6. Publish a version, point the alias live at it, provision 20 warm environments
aws lambda publish-version --function-name $FN --query Version
# "7"
aws lambda create-alias --function-name $FN --name live --function-version 7
aws lambda put-provisioned-concurrency-config --function-name $FN --qualifier live \
  --provisioned-concurrent-executions 20
# { "RequestedProvisionedConcurrentExecutions": 20, "AllocatedProvisionedConcurrentExecutions": 0, "Status": "IN_PROGRESS" }
aws lambda get-provisioned-concurrency-config --function-name $FN --qualifier live --query Status
# "READY"   (after a minute or two)

# 7. Let Application Auto Scaling manage it: 200 for the weekday rush, back to 20 after
aws application-autoscaling register-scalable-target --service-namespace lambda \
  --resource-id function:$FN:live --scalable-dimension lambda:function:ProvisionedConcurrency \
  --min-capacity 20 --max-capacity 300
aws application-autoscaling put-scheduled-action --service-namespace lambda \
  --resource-id function:$FN:live --scalable-dimension lambda:function:ProvisionedConcurrency \
  --scheduled-action-name morning-rush --schedule "cron(45 1 ? * SUN-THU *)" \
  --scalable-target-action MinCapacity=200,MaxCapacity=300
# (01:45 UTC = 7:45 in Dhaka; a second action lowers MinCapacity to 20 at 10:30)

# 8. Or track utilization at 70%
aws application-autoscaling put-scaling-policy --service-namespace lambda \
  --resource-id function:$FN:live --scalable-dimension lambda:function:ProvisionedConcurrency \
  --policy-name pc-70 --policy-type TargetTrackingScaling \
  --target-tracking-scaling-policy-configuration '{"TargetValue":0.7,"PredefinedMetricSpecification":{"PredefinedMetricType":"LambdaProvisionedConcurrencyUtilization"}}'

# 9. SnapStart on a Java or Python function (applies to versions published afterwards)
aws lambda update-function-configuration --function-name payment-status-example \
  --snap-start ApplyOn=PublishedVersions
aws lambda publish-version --function-name payment-status-example \
  --query '{v:Version,snap:SnapStart.OptimizationStatus}'
# { "v": "12", "snap": "On" }

# 10. Clean up the paid part
aws lambda delete-provisioned-concurrency-config --function-name $FN --qualifier live

Then look at the Throttles and ConcurrentExecutions metrics of the function in CloudWatch while you invoke it in a loop with reserved concurrency set to 1: the throttle count rises as soon as two invocations overlap.

#Summary

Key takeaways

  • Hard limits (per Region): memory 128 MB–10,240 MB, timeout 15 min, payload 6 MB sync / 1 MB async (256 KB before 2025), package 50 MB zipped / 250 MB unzipped (layers included), container image 10 GB, /tmp 512 MB–10 GB, env vars 4 KB, 5 layers. Workarounds: S3 + presigned URL, Step Functions or Fargate, container images, /tmp or EFS.
  • CPU comes with memory: 1 vCPU at 1,769 MB, up to 6 at 10,240 MB. More memory can be faster for the same price.
  • Concurrency = requests per second × duration (s). Default quota 1,000 per account per Region, shared by all functions, raisable. Also: 10 requests per second per unit, and scaling of 1,000 environments per 10 s per function.
  • Throttling: synchronous → 429, client retries; asynchronous → Lambda retries up to 6 h (1 s to 5 min backoff), then on-failure destination / DLQ or dropped; function errors get 2 retries; SQS → messages stay in the queue, DLQ on the queue; ESM maximum concurrency to cap a queue.
  • Reserved concurrency: free; guarantee and cap for one function; protects against noisy neighbours, limits load on a database, 0 = kill switch; 100 units always stay unreserved.
  • Cold start: Init (new environment + code outside the handler, max 10 s, billed since August 2025) before the first request of each environment. Put setup outside the handler, trim the package, add memory.
  • Provisioned concurrency: pre-initialized environments on a version or alias; no cold start up to that number, spillover beyond; paid while configured; Application Auto Scaling (scheduled, target tracking); ≤ reserved concurrency.
  • SnapStart: snapshot after Init at publish time, restore instead of init; Java 11+, Python 3.12+, .NET 8+; free for Java only; not with provisioned concurrency, EFS or /tmp > 512 MB; regenerate unique values and reconnect after restore.