DynamoDB at Scale: Partition Internals, GSI Backpressure & ACID Transactions
DynamoDB's performance ceiling is determined at schema design time — a partition key with insufficient cardinality creates a hot partition that table-level throughput scaling cannot fix, because the limit is enforced per-partition at the storage node. This article covers partition key hashing, per-partition hard limits, hot partition detection and salt-key mitigation, GSI backpressure propagation, ACID transactions, and Global Tables conflict resolution.
AWS Serverless Engineering: Lambda to Production
DynamoDB at Scale: Partition Internals, GSI Backpressure & ACID Transactions
Every AWS primitive is a tradeoff surface, not a feature toggle. DynamoDB's "scale to any workload" promise has a hard asterisk: per-partition hard limits. A table provisioned at 10,000 WCU cannot write faster than 1,000 WCU to a single partition key, because that limit is enforced at the storage node — not at the table control plane. This is why provisioning more capacity does not fix hot-partition throttling. This article covers the physical storage model that produces this constraint, the GSI backpressure failure mode that causes base table write throttling from an under-provisioned index, and the ACID transaction semantics that enable financial-grade consistency in a non-relational system.
Prerequisite chain: Document & NoSQL Databases: MongoDB & DynamoDB → Part 7: Access-Pattern-First Modeling → this article.
1. Partition Internals — The Storage Model
DynamoDB stores data in partitions — independently managed storage and compute units. Understanding the partition model is the prerequisite for understanding every performance and cost constraint in DynamoDB.
1.1 Partition Key Hashing
DynamoDB uses an internal hashing algorithm to map partition key values to physical partitions. The hash distributes keys across partitions to balance load. The key insight: all items with the same partition key value land on the same partition, served by the same storage node.
If 100,000 orders all share STATUS#PENDING as their partition key, all 100,000 writes route to partition_node_A. Even if the table has 10,000 WCU provisioned, partition_node_A is hard-capped at 1,000 WCU/second.
1.2 Per-Partition Hard Limits
| Limit | Value | Nature |
|---|---|---|
| Storage per partition | 10 GB | Hard limit — DynamoDB automatically splits at 10 GB |
| Read Capacity Units per partition | 3,000 RCU/sec | Per storage node — cannot be increased (~12 MB/s strong, ~24 MB/s eventual) |
| Write Capacity Units per partition | 1,000 WCU/sec | Per storage node — cannot be increased (~1 MB/s writes) |
| Maximum item size | 400 KB | Per item — binary blobs belong in S3 |
The per-partition WCU and RCU limits are hard constraints enforced at the storage node level per second (1,000 WCU/sec and 3,000 RCU/sec). Increasing table-level provisioned capacity or switching to On-Demand mode does NOT increase these limits. Because capacity units scale with payload size, larger items directly reduce request throughput: 1,000 WCU/sec yields 1,000 writes/sec for 1 KB items, but only 500 writes/sec for 2 KB items, and just 2 writes/sec for 400 KB items. The only mitigation for a hot partition is to distribute writes across more partition key values.
1.3 Hot Partition Detection
Before mitigating a hot partition, you must isolate it. A hot partition exhibits a distinct signature: ConsumedWriteCapacityUnits or ConsumedReadCapacityUnits is well below the table-level provisioned or on-demand threshold, yet ThrottledRequests is non-zero and spikes continuously.
Native Isolation: CloudWatch Contributor Insights
While table-level CloudWatch metrics confirm that throttling is occurring, they cannot identify which partition key is causing it. CloudWatch Contributor Insights for DynamoDB continuously tracks item-level telemetry and plots the exact partition keys responsible for throttled requests:
TopHotKeys: Isolates the exact partition key values (e.g.,STATUS#PENDINGorPRODUCT#flash-sale-sku) absorbing disproportionate write or read traffic.TopThrottledKeys: Pinpoints which specific keys are exceeding the 1,000 WCU/sec or 3,000 RCU/sec storage node limits.
1.4 Hot Partition Mitigations
Depending on whether the hotspot stems from write concentration, un-sharded bulk ingestion, or high-concurrency read demand, different architectural patterns are required:
Pattern 1: Deterministic Suffix Sharding (High Writes with Direct Point Reads)
Failure Modes & Limitations:
- Input Skew Bottleneck: Deterministic sharding relies on high cardinality in the hash input (
orderId). If you hash an attribute with skewed distribution (e.g., hashingtenantIdwhere one enterprise customer generates 90% of traffic), writes will still concentrate on a single shard.- Scatter-Gather for Range Queries: If you need to list all pending orders, you must issue 10 parallel queries across all shards (
Promise.all) and merge the results in memory, multiplying RCU consumption by the shard count.
Pattern 2: Random Suffix Sharding (Bulk Ingestion & Queue Draining)
Failure Modes & Limitations:
- Unindexed Point Lookups: Because items are assigned to random shards, single-item lookups by entity ID become impossible without querying every shard or maintaining a separate index.
- Uneven Shard Draining: In scatter-gather pagination, different shards drain at different speeds; tracking pagination cursors (
ExclusiveStartKey) across 20 asynchronous shard streams requires complex state coordination.
Pattern 3: Upstream Write Buffering (Smoothing Flash Surges Over 1,000 WCU/sec)
Failure Modes & Limitations:
- Asynchronous User Experience: Clients cannot receive immediate synchronous confirmation of their purchase; the frontend must poll an order status endpoint, listen on a WebSocket, or await an asynchronous webhook.
- Queue Head-of-Line Blocking: If a poisonous message or processing error causes batch execution to fail, SQS FIFO halts processing for that entire
MessageGroupIduntil the message retries or reaches the DLQ.
Pattern 4: Read-Side Hotspot Shielding with DAX (Surpassing 3,000 RCU/sec)
Failure Modes & Limitations:
- Zero Write Protection: DAX is a write-through cache; writes still pass through to DynamoDB and are subject to the hard 1,000 WCU/sec partition limit. DAX solves read hotspots, never write hotspots.
- Cache Eviction & Stampedes: During cluster failover or memory pressure evictions, sudden cold-cache misses trigger a read stampede that cascades directly to the underlying DynamoDB partition, causing immediate throttling.
2. GSI Backpressure — The Upstream Write Throttle
This is the most dangerous DynamoDB failure mode because it is invisible: a GSI under-provisioned relative to the base table write rate throttles base table writes, not GSI reads.
2.1 The Failure Chain
2.2 Monitoring and Mitigation
On-Demand mode eliminates GSI backpressure — DynamoDB scales GSI capacity automatically with table write throughput. For Provisioned mode, the GSI WCU must be sized to match the write rate of items that carry the GSI key. A GSI on a low-cardinality attribute (status field with 3 values) concentrates writes and is a hot-partition risk even in On-Demand mode.
3. ACID Transactions
DynamoDB supports ACID transactions via TransactWriteItems (up to 25 write operations) and TransactGetItems (up to 25 read operations) across any number of tables in the same account and region.
3.1 TransactWriteItems — Cross-Item Consistency
TransactWriteItems consumes 2× WCU per item — once for the write operation and once for the transactional overhead. A 3-item transaction costs 6 WCU minimum (2 WCU per 1 KB block per item). At high write rates, this doubles the effective write cost. Use transactions only when cross-item consistency is a genuine business requirement — not as a convenience wrapper for multi-item writes that do not require atomicity. For the complete capacity unit pricing model, 4 KB read aggregation rules, and On-Demand vs. Provisioned math, see Section 8: DynamoDB Cost Taxonomy.
3.2 Conditional Writes — Idempotency, OCC & Guard Patterns
Conditional writes turn single-item mutations into atomic predicate evaluations at the storage engine level. They enforce critical business invariants without paying the 2× WCU overhead of full transactions.
Pattern 1: Bounded Atomic Mutation (Safe Counter without Pre-Read)
Failure Modes & Limitations:
- Hot Partition Bottleneck: All concurrent purchases target the exact same partition key (
PRODUCT#p1), hard-capped at 1,000 WCU/sec. During flash sales, high write volume causesProvisionedThroughputExceededExceptionregardless of total table capacity.- Zero Audit Lineage: Decrementing in-place doesn't record which order or customer consumed the inventory. Production systems must combine this with an order item creation in a transaction or write an audit log to maintain reconciliation records.
Pattern 2: Idempotent Insert (First-Write-Wins Guard)
Failure Modes & Limitations:
- Composite Key Scope: On tables with a composite primary key (
PK+SK),attribute_not_exists(PK)checks the entire item identity (PKANDSK). It will not prevent inserting a duplicatepaymentIntentIdif the sort key differs.- Upstream Exception Handling: When a duplicate request arrives, DynamoDB throws
ConditionalCheckFailedException. Upstream API handlers must explicitly catch this error and return HTTP 200 OK (idempotent success) or return the existing record instead of bubbling an unhandled 500 error to clients.
Pattern 3: Versioned Optimistic Locking (Preventing the ABA Problem)
Failure Modes & Limitations:
- Retry Storms Under High Contention: Optimistic concurrency assumes collisions are rare. If dozens of workers concurrently attempt to update the same item (e.g., reserving seats on the same flight), all but one will fail the version check. If clients immediately retry, they trigger exponential retry storms and waste WCU on rejected writes. High-contention workflows should be serialized via SQS FIFO or write-sharded instead.
Pattern 4: State Machine Transition Guard
Failure Modes & Limitations:
- Sibling State Races: Transition guards prevent backward state jumps, but cannot arbitrate two competing, valid transitions from the same starting state (e.g., Worker 1 executes
PENDING → PROCESSINGwhile Worker 2 executesPENDING → CANCELLED). The first write wins; the loser receivesConditionalCheckFailedExceptionand must execute explicit compensation logic (e.g., refunding payment if processing lost the race).- Reserved Keyword Syntax Traps: Attributes like
status,date,order, andpercentare DynamoDB reserved keywords. ForgettingExpressionAttributeNamescauses an immediateValidationException.
Pattern 5: Distributed Mutex Lease Lock
Failure Modes & Limitations:
- Client Clock Skew (NTP Drift): DynamoDB does not evaluate server-side timestamps in
ConditionExpression;:nowis supplied by the client application. If a worker node suffers NTP clock drift and its system clock runs fast, it can prematurely steal the lease from an actively executing node.- GC Pauses and Fencing Violations: If worker A experiences a 35-second Garbage Collection or network pause, its 30-second lease expires and worker B acquires the lock. When worker A wakes up, it may write corrupt data assuming it still owns the lock unless guarded by a monotonic fencing token on every downstream mutation.
Conditional Write Cost Rule: When a ConditionExpression evaluates to false, DynamoDB throws ConditionalCheckFailedException and consumes the full write capacity (WCU) of the attempted write. While conditional writes avoid the 2× WCU multiplier of TransactWriteItems, high contention on a single key can consume significant WCU on rejected writes. For hot items, rate-limit upstream or use write sharding (Section 1.4).
4. DynamoDB Streams and Lambda Integration
| Stream view type | Content | Use case | Cost |
|---|---|---|---|
KEYS_ONLY |
PK + SK only | Change detection | Lowest |
NEW_IMAGE |
New item state | Event sourcing | Medium |
OLD_IMAGE |
Previous state | Soft deletes, undo log | Medium |
NEW_AND_OLD_IMAGES |
Both states | Full audit trail, diff detection | Highest |
5. On-Demand vs Provisioned — Cost Crossover
| Attribute | On-Demand | Provisioned + Auto Scaling |
|---|---|---|
| Billing model | Per request ($1.25/million WRU, $0.25/million RRU) | Per hour ($0.00065/WCU-hr, $0.00013/RCU-hr) |
| Zero-traffic cost | $0 | Minimum provisioned hours × rate |
| Traffic spike handling | Automatic | Delayed by Auto Scaling (3–15 min lag) |
| Cost crossover | Wins for spiky/unpredictable traffic | Wins for steady, predictable traffic |
| Typical break-even | ~200 WCU equivalent sustained | ~200 WCU sustained 24/7 |


Summary
| Concept | Rule |
|---|---|
| Per-partition limits | 1,000 WCU / 3,000 RCU per partition — table capacity cannot override these |
| Hot partition detection | Consumed WCU << Provisioned WCU + ThrottledRequests > 0 = hot partition, not under-provisioning |
| Hot partition mitigation | Sharding (deterministic/random), SQS FIFO write-buffering, and DAX read-shielding |
| GSI backpressure | Under-provisioned GSI throttles base table writes — alarm on GSI metrics, not only table metrics |
| TransactWriteItems | 2× WCU per item, 25 item max — use for genuine financial-grade consistency only |
| Conditional writes | Enforces invariants (bounded decrements, versioned OCC, state transitions, mutex leases) at 1× WCU |
What's Next
In Part 9: Messaging Foundations — SQS, SNS & Fan-Out Patterns, we shift from the data layer to the messaging layer: why SNS and SQS solve completely different problems, what happens when you use SNS as a work queue, and how the visibility timeout is the mechanism behind SQS's at-least-once delivery guarantee.
This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.