Subchapter 42.3
references/concurrency.mdMarkdown6 KBView on GitHub
Four concurrency controls operate at different levels with non-obvious interactions. The exact numbers and mutual-exclusivity rules below are the part that’s easy to get wrong.
Reserved Concurrency (scope: function) — sets the max concurrent instances AND reserves that capacity from the account pool. Setting to 0 fully throttles the function (emergency shutoff). Use to protect critical functions, cap to protect downstream, or kill-switch.
Provisioned Concurrency (scope: published version or alias — NOT $LATEST) — pre-initializes environments so they’re ready before requests arrive. Spills to on-demand (with cold starts) beyond the provisioned count. Combine with Application Auto Scaling (~70% target). Paid even when idle.
Maximum Concurrency (scope: per SQS ESM, range 2–1,000) — caps how many concurrent instances one SQS ESM can invoke. Does not reserve anything; other triggers can still consume function concurrency.
Provisioned Mode — ESM (SQS and Kafka/MSK) — allocates dedicated event pollers for an SQS/Kafka ESM with configurable min/max. Per-poller capacity is an OR envelope and differs by source: SQS = 1 MB/s or 10 concurrent invokes; Kafka/MSK = 5 MB/s or 5 concurrent invokes. Use for high-throughput or spiky traffic where standard ramp-up (5 → +300/min) is too slow.
ClaimedAccountConcurrency.| Combination | OK? | Notes |
|---|---|---|
| Reserved + Provisioned | Yes | Provisioned ≤ Reserved |
| Reserved + Maximum Concurrency (ESM) | Yes | Reserved ≥ Σ(Maximum Concurrency across ESMs) |
| Provisioned + Maximum Concurrency / Provisioned Mode (ESM) | Yes | Different layers |
| Maximum Concurrency + Provisioned Mode (same ESM) | No | Mutually exclusive |
| Provisioned Concurrency + SnapStart | No | Mutually exclusive |
At the limit: Sync → 429. Async → retries up to 6h then DLQ. Streams → polling throttled, messages stay in source.
RPS gotcha: a 50ms function at 20,000 RPS needs only 1,000 concurrency, but the RPS limit (10×1,000 = 10,000) throttles it. Request account concurrency = 2,000.
aws service-quotas request-service-quota-increase \
--service-code lambda --quota-code L-B99A9384 --desired-value 5000| Scenario | Reserved | Provisioned | Maximum Concurrency (ESM) | Prov Mode (ESM) |
|---|---|---|---|---|
| Protect critical API / cap downstream | Yes | — | — | — |
| Eliminate cold starts (user-facing API) | Optional | Yes | — | — |
| Multiple SQS queues, prevent hogging | Yes | — | Yes | — |
| High-throughput SQS, low-latency | Optional | Optional | — | Yes |
| Kafka/SQS ESM with spiky traffic | — | — | — | Yes |
| Predictable daily traffic | — | Yes + AutoScale | — | — |
| Emergency shutoff | Yes (=0) | — | — | — |
$LATEST — doesn’t work; publish a version, create an alias.Mutually exclusive on the same function.
Runtime Java 11+ / Python 3.12+ / .NET 8+
├─ No → Provisioned Concurrency
└─ Yes
├─ Need guaranteed <50ms on EVERY request? → Provisioned Concurrency
├─ Need EFS or >512MB ephemeral storage? → Provisioned Concurrency
└─ Otherwise → SnapStart first; if P99 still too high, switch (they cannot coexist)| Type | SAM | CDK |
|---|---|---|
| Reserved | ReservedConcurrentExecutions | reservedConcurrentExecutions |
| Provisioned | AutoPublishAlias + ProvisionedConcurrencyConfig.ProvisionedConcurrentExecutions | new lambda.Alias({ provisionedConcurrentExecutions }) (alias, not $LATEST) |
| Maximum Concurrency (ESM) | ScalingConfig.MaximumConcurrency | maxConcurrency on EventSourceMapping |
| Provisioned Mode (ESM) | ProvisionedPollerConfig.MinimumPollers / MaximumPollers | provisionedPollerConfig: { minimumPollers, maximumPollers } |
| SnapStart | SnapStart.ApplyOn: PublishedVersions + AutoPublishAlias | snapStart: lambda.SnapStartConf.ON_PUBLISHED_VERSIONS |
Auto scaling: alias.addAutoScaling({ minCapacity, maxCapacity }) then scaling.scaleOnUtilization({ utilizationTarget: 0.7 }).