Intermediate8 min read

DynamoDB Throttling — Why It Happens and How to Fix It

Throttling is DynamoDB telling you a limit was hit — but there are four different limits, three different exceptions, and the fix for one cause makes another worse. Raising table capacity does nothing for a hot key; switching to on-demand does nothing for a hot key either and can still throttle on its own rules. This guide is the umbrella: which limit you actually hit, how the metrics tell them apart, and the fix that matches each cause.

Why is DynamoDB throttling my requests?

One of four documented reasons: a single partition exceeded its fixed per-partition limit of 3,000 read units or 1,000 write units per second (a hot key — happens in both capacity modes); the table exceeded its provisioned RCU/WCU (provisioned mode); the account exceeded its region-level throughput quota; or an on-demand table grew faster than double its previous peak within 30 minutes. The fix depends on which one it was, so diagnose before you resize anything.

The four throttling scenarios

AWS's own troubleshooting page splits throttling into exactly four cases:

  1. Key-range (partition) throughput exceeded — both modes. Every partition is designed for a maximum of 3,000 read units and 1,000 write units per second (partition-key docs), and item size counts against it. No table-level setting raises this; only key design spreads it. This is the hot partition case, and the table can look massively under-utilized while it throttles.
  2. Provisioned throughput exceeded — provisioned mode. Consumption beat the table's (or a GSI's) provisioned RCU/WCU, and the ~5-minute burst-capacity cushion was spent. The fix ladder is capacity-side: auto scaling, a higher provision, or a mode switch.
  3. Account-level quota exceeded. Regional account quotas cap total throughput — by default 40,000 read and 40,000 write units per table, and for provisioned mode 80,000 RCU and 80,000 WCU per account (quotas); these are initial defaults, adjustable via Service Quotas, and on-demand tables have no account-level throughput quota.
  4. On-demand maximum throughput exceeded. On-demand instantly accommodates up to double the previous peak; grow past double within 30 minutes and it can throttle (on-demand docs). New on-demand tables sustain 4,000 writes/s and 12,000 reads/s out of the box. For a planned step-spike (launch, sale, migration), pre-warm the table with warm throughput instead of hoping the ramp is gradual.

The three exceptions, and the field that names the cause

  • ProvisionedThroughputExceededException — provisioned-mode capacity throttling: "you exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes". Details on the dedicated error page.
  • ThrottlingException — control-plane operations issued too fast, and, on on-demand tables, any data-plane operation whose rate is too high (that's the exception behind the double-peak rule — see the on-demand error page and ThrottlingException).
  • RequestLimitExceeded — account-level throughput limits: "contact AWS Support" territory, covered on its error page.

All three are marked retryable, and all three now carry structured ThrottlingReason values of the form resource + operation + limit — TableReadProvisionedThroughputExceeded, IndexWriteKeyRangeThroughputExceeded, TableWriteAccountLimitExceeded, and so on (error reference). Read the reason, not just the exception class: it names the resource (table or index), the operation direction, and which of the four limits you hit — which is exactly the diagnosis. One hedge the docs themselves force: AWS pages differ on whether account-limit throttling surfaces as RequestLimitExceeded or a ThrottlingException with an AccountLimitExceeded reason, so key your handling on the reason string.

What absorbs load before you're throttled

Two built-ins soften limits, and knowing their edges explains "it worked yesterday":

  • Burst capacity retains up to five minutes (300 seconds) of unused read and write capacity for spikes — but DynamoDB may also consume it for background maintenance "without prior notice", and AWS explicitly notes the details may change. Don't design for burst; treat it as luck.
  • Adaptive capacity automatically and instantly shifts throughput toward hot partitions and can isolate a frequently-accessed item onto its own partition — but only "provided that traffic does not exceed your table's total provisioned capacity or the partition maximum capacity". It rebalances skew; it never lifts the 3,000/1,000 per-partition ceiling, and it won't split item collections when the table has an LSI. The current AWS troubleshooting pages lean on split-for-heat — partitions splitting under sustained heat — which takes time and does not help a single hot key.

Diagnose it from the metrics

CloudWatch separates requests from events, and the distinction does the diagnosis (metrics reference):

  • ThrottledRequests counts a request once if any event inside it was throttled — a PutItem on a table with three GSIs is one request but four write events. In a batch, it increments only if every item throttled.
  • ReadThrottleEvents / WriteThrottleEvents count each throttled event — a BatchGetItem of 10 items is 10 GetItem events. To see a GSI's write throttles you must query the metric with both TableName and GlobalSecondaryIndexName — this is how GSI back-pressure hides from table-level dashboards.
  • The newer reason-specific event metrics (WriteProvisionedThroughputThrottleEvents, ReadKeyRangeThroughputThrottleEvents, …AccountLimitThrottleEvents, …MaxOnDemandThroughputThrottleEvents) split the counts by the same four causes — if your region shows them, they answer the "which limit" question directly.

One trap: SDKs retry throttled requests automatically — the standard retry mode makes 3 total attempts by default (the 2026 opt-in retry rollout moves DynamoDB clients to 4 attempts with tighter delays). Light throttling therefore shows up as latency, not errors; watch the throttle metrics, not just your exception logs.

yesnoyesnoprovisionedon-demandThrottling observedThrottleEvents on a GSI(TableName + IndexName)?GSI back-pressure:scale the indexTable utilization far belowprovisioned / expected?Hot key: fix key design,split-for-heat needs timeCapacity mode?Raise capacity /auto scaling / switch modeGrew past 2x previous peak:pre-warm or spread the ramp

GSI back-pressure: the throttle that points at the wrong table

If any GSI can't absorb the write amplification, "DynamoDB throttles writes to the base table to maintain data consistency" (GSI throttling docs) — even when the base table has capacity to spare. The exception's ResourceArn points at the index, but the operation that failed is your base-table write. Every index needs its own capacity plan (and its own auto-scaling policy); why a GSI throttles base-table writes walks through the mechanics.

Match the fix to the cause

CauseWhat fixes itWhat doesn't
Hot key / partitionKey design that spreads load (hot partitions); time for split-for-heatRaising table capacity, switching to on-demand
Provisioned capacityAuto scaling, higher min, or on-demandRetries alone — they add load
GSI back-pressureScale the index; sparse-index or projection changesScaling the base table
Account quotaService Quotas increaseTable-level settings
On-demand step spikePre-warm (warm throughput); spread the ramp over 30+ minWaiting — double-peak resets slowly

Do it in DynoTable

Most self-inflicted throttling starts with reads that cost more than they look: a filtered Scan consumes the full read either way. DynoTable's pre-run cost preview shows whether a statement becomes a Query or a Scan, the index it hits, and an estimated read cost before you spend it — the cheapest throttling fix is the expensive read you didn't run. The Scan vs Query guide covers the difference; the free item size calculator turns a real item into the RCU/WCU numbers the limits above are measured in.

Pitfalls and next steps

  • Retries amplify overload. Backoff is built into the SDKs, but a tight application-level retry loop on top of SDK retries multiplies pressure on exactly the partition that's struggling.
  • Batches hide partial throttling. A BatchWriteItem returns unprocessed items rather than throwing while any item succeeds — check UnprocessedItems, not just exceptions.
  • The table-level view lies about GSIs. Always chart throttle events per-index; base-table dashboards look clean during back-pressure.
  • Capacity fixes take minutes; key design is forever. Auto scaling reacts in ~5 minutes, quota increases take a support ticket, but a hot key follows you to every capacity mode — spend the effort where it compounds: how partition keys work.

Download DynoTable to see each query's Scan-vs-Query plan and read cost before it runs against your capacity.

Updated