DEV Community

Cover image for Why a DynamoDB GSI throttles base-table writes
DynoTable
DynoTable

Posted on • Edited on • Originally published at dynotable.com

Why a DynamoDB GSI throttles base-table writes

You write to your table. The write fails with a throughput exception — but the
exception names a Global Secondary Index, not the table. The table has spare
capacity.

Coming from SQL, that's nonsense: a secondary index can't block an INSERT. In
DynamoDB it can, and the mechanism is called GSI back-pressure.

Why does a DynamoDB GSI throttle base-table writes?

DynamoDB throttles the base-table write because every write also replicates to each GSI, and if a GSI partition can't absorb its share, DynamoDB applies back-pressure to stop the index falling permanently behind. So an under-provisioned or low-cardinality GSI key becomes a hard ceiling on your base-table write rate.

  • A write to the base table also writes to every GSI. If a GSI can't absorb its share, DynamoDB throttles the base-table write to keep the index from falling permanently behind. (AWS docs)
  • An even base table doesn't save you. The GSI is partitioned by its own key. A low-cardinality GSI key (like status) creates a hot index partition even when base-table writes are perfectly spread.
  • The exception lies about the victim. The ResourceArn points at the GSI; the operation actually being throttled is your write to the table.
  • The fix is capacity or key design, not retry loops — raise GSI throughput, or pick a GSI partition key that spreads.

How a single write touches the index

A PutItem on the base table is not one write. DynamoDB replicates the item's
projected attributes into each GSI asynchronously, on an eventually consistent
model. One logical write fans out to N physical writes — table plus every index.

That replication is not free and not optional. The GSI must keep up, or the
index drifts further from the table on every operation.

To stop that drift, DynamoDB applies back-pressure: it throttles the source
write so the index never gets unboundedly stale.

So the GSI's write capacity is a hard ceiling on your base-table write rate —
even though you never write to the GSI directly.

A worked example: an orders table

Say you run an orders table. The base item:

field value note
PK "CUST#8841" partition key
SK "ORD#2026-06-23#A7" sort key
order_state "PROCESSING"
warehouse "EU-MAD-2"
total_cents 4990

Base-table writes are healthy. CUST#... has high cardinality, so order writes
spread evenly across base partitions. No hot key, plenty of capacity.

Now you add a GSI to answer "show me every order in a given state":

GSI: orders-by-state

field value note
GSI-PK order_state "PENDING" | "PROCESSING" | "SHIPPED" | "CANCELED"
GSI-SK SK

Four possible partition-key values. During a flash sale, almost every new order
lands in order_state = "PENDING". Every one of those writes hits the same
GSI partition.

That partition has a per-partition throughput limit, and you've just aimed your
entire write storm at it.

The base table is fine. The PENDING GSI partition is on fire. DynamoDB
throttles the base-table PutItem to protect the index.

The flow that bites you

Here's the back-pressure path — base write balanced, index write concentrated:

Why a DynamoDB GSI throttles base-table writes

The throttle travels backward: a hot GSI partition rejects the base-table write
that fed it.

Read the exception, not your gut

The exception type tells you exactly which ceiling you hit. The ResourceArn
names the GSI; the throttled op is still the table write.

Mode Reason code What ran out
Provisioned IndexWriteProvisionedThroughputExceeded GSI's provisioned write capacity
Both IndexWriteKeyRangeThroughputExceeded A single hot GSI partition
On-demand IndexWriteMaxOnDemandThroughputExceeded GSI's configured max on-demand ceiling
On-demand IndexWriteAccountLimitExceeded Account/region throughput boundary

Source: Understanding GSI write throttling and back pressure.

The KeyRange reason is the giveaway for the hot-partition case above: overall
GSI capacity can look fine while one key range is saturated.

How to fix it

Give the GSI room. The simplest cause is under-provisioning. A GSI has its
own read and write capacity, fully separate from the table — see
GSI vs LSI.

If you provisioned the table generously and left the GSI thin, raise the GSI's
write capacity (or its on-demand max).

Fix the partition key. Capacity won't save a low-cardinality key — you can't
out-provision a single hot partition. Pick a GSI partition key that spreads.

Compose it: order_state#shard where shard is a small random suffix, or fold
the date in (PENDING#2026-06-23). Writes spread across partitions and you still
Query a state by querying the shards.

Project fewer attributes. Each GSI write copies the projected attributes. A
KEYS_ONLY or tight INCLUDE projection means smaller index writes and less
pressure than ALL. Don't project what you'll never read off the index.

Drop the GSI if it's only for reporting. If "orders by state" is an
occasional admin question, not a hot path, a periodic scan with a filter may beat
a permanently hot index — weigh it against
Query vs Scan.

When you do query that index, the
Expression Builder writes the
KeyConditionExpression for you — e.g. #s = :state AND begins_with(SK, :prefix)
with the names and values escaped correctly:

Example Notes
KeyConditionExpression "#s = :state AND begins_with(SK, :prefix)"
ExpressionAttributeNames { "#s": "order_state" }
ExpressionAttributeValues { ":state": { "S": "PENDING" }, ":prefix": { "S": "ORD#2026-06-23" } }

The trap to remember

The relational instinct — "indexes only slow writes a little" — does not
transfer. A DynamoDB GSI is a throughput dependency, not a passive structure.
Under-size it or pick a clumping key, and it back-pressures the table it serves.

Watch ConsumedWriteCapacityUnits and WriteThrottleEvents on the GSI
dimension, not just the table's, and use Contributor Insights to find the hot
keys.

Next steps

  • GSI vs LSI — why a GSI has its own capacity and a different partition key.
  • Single-table design — overloading one GSI to serve many patterns without multiplying hot indexes.
  • Query vs Scan — when an index isn't worth its write cost.

Try DynoTable to inspect every GSI on your tables — key schema and
item counts — and query your indexes before a sale turns them red.

Top comments (0)