RCUWCUOn-DemandProvisionedTransactWriteItemsTTLAuto Scaling

Capacity, Billing & Transactions

DynamoDB's operational model — understanding capacity units prevents surprise bills and throttling. Transactions enable atomic multi-item operations at 2Ɨ cost.

40 min read9 sections
01

Read Capacity Units (RCU)

Read TypeCost per 4 KBNotes
Eventually consistent0.5 RCUDefault, reads from any replica
Strongly consistent1 RCURoutes to leader replica
Transactional2 RCUSnapshot isolation across items

Calculation Examples

rcu-calculation.txttext
RCU Calculation:
═══════════════════════════════════════════════════════════════
Item Size    | Eventually Consistent | Strongly Consistent
═══════════════════════════════════════════════════════════════
1 KB item    | ceil(1/4) Ɨ 0.5 = 0.5 → 1 RCU  | ceil(1/4) Ɨ 1 = 1 RCU
4 KB item    | ceil(4/4) Ɨ 0.5 = 0.5 → 1 RCU  | ceil(4/4) Ɨ 1 = 1 RCU
10 KB item   | ceil(10/4) Ɨ 0.5 = 1.5 → 2 RCU | ceil(10/4) Ɨ 1 = 3 RCU
50 KB item   | ceil(50/4) Ɨ 0.5 = 6.5 → 7 RCU | ceil(50/4) Ɨ 1 = 13 RCU
═══════════════════════════════════════════════════════════════

Key insight: item size directly impacts cost.
Smaller items = lower cost per read.

ProjectionExpression Does NOT Reduce RCU

Even if you project only 2 attributes from a 50 KB item, you still pay RCU for the full 50 KB. ProjectionExpression reduces network bandwidth, not read cost. To reduce RCU, make items smaller or split large items.

02

Write Capacity Units (WCU)

Write TypeCost per 1 KBNotes
Standard write1 WCUPutItem, UpdateItem, DeleteItem
Transactional write2 WCUTransactWriteItems
wcu-calculation.txttext
WCU Calculation:
═══════════════════════════════════════════════════════════════
Item Size    | Standard Write  | Transactional Write
═══════════════════════════════════════════════════════════════
0.5 KB item  | ceil(0.5/1) = 1 WCU  | 2 WCU
1 KB item    | ceil(1/1) = 1 WCU    | 2 WCU
3.5 KB item  | ceil(3.5/1) = 4 WCU  | 8 WCU
10 KB item   | ceil(10/1) = 10 WCU  | 20 WCU
═══════════════════════════════════════════════════════════════

For updates: WCU based on max(old item size, new item size).
Deletes: WCU based on the deleted item's size.

Write Amplification from GSIs

Every write to the base table also writes to each GSI that includes the item. With 3 GSIs: effective WCU = base write WCU + (3 Ɨ GSI write WCU). A 1 KB item write with 3 GSIs costs 4 WCU minimum. Factor this into capacity planning.

03

Provisioned vs On-Demand

FeatureProvisionedOn-Demand
PricingPay for provisioned capacity (used or not)Pay per request (~7Ɨ more per request)
ScalingManual or auto scaling (not instant)Instant, automatic
ThrottlingExceeding capacity → ProvisionedThroughputExceededExceptionHot partition throttling only
Best forPredictable, sustained trafficUnpredictable, spiky, new tables
Cost at scaleCheaper with reserved capacityExpensive at sustained high throughput
SwitchingCan switch once per 24 hoursCan switch once per 24 hours

Provisioned Mode Details

Provisioned Mode Details

  • āœ…You specify RCU and WCU per table — DynamoDB guarantees that throughput
  • āœ…Burst capacity: unused capacity banked for up to 5 minutes
  • āœ…Auto scaling: CloudWatch alarms trigger capacity adjustments (not instant — minutes)
  • āœ…Reserved capacity: commit 1 or 3 years for significant discount (up to 77%)
  • āœ…Throttling: requests exceeding capacity return ProvisionedThroughputExceededException

Decision Framework

Choose Provisioned When

  • Traffic is predictable and sustained
  • Cost optimization is priority
  • Can commit to reserved capacity
  • Team can manage auto scaling policies

Choose On-Demand When

  • Traffic is unpredictable or spiky
  • New table (unknown traffic patterns)
  • Dev/staging environments
  • Simplicity over cost optimization
04

Adaptive Capacity & Auto Scaling

Adaptive capacity automatically redistributes provisioned capacity to hot partitions. If one partition needs more throughput, DynamoDB borrows from underutilized partitions.

Adaptive Capacity Characteristics

  • āœ…Reduces isolated partition throttling — hot partition gets more of table's capacity
  • āœ…Does NOT eliminate hot partition problem entirely — design is still critical
  • āœ…Works within provisioned capacity — cannot exceed total table capacity
  • āœ…Automatic — no configuration needed
  • āœ…Helps with temporary spikes, not sustained hot keys

Auto Scaling (Provisioned Mode)

1

Target Utilization

Set target (e.g., 70% of provisioned capacity)

2

CloudWatch Alarm

Consumed capacity exceeds target for sustained period

3

Scale Up

Application Auto Scaling increases provisioned capacity

4

Cool Down

Wait period before next scaling action (default 60s up, 60s down)

Auto Scaling is Not Instant

Auto scaling reacts to CloudWatch metrics — there is a delay of 1-2 minutes between traffic spike and capacity increase. For sudden spikes (flash sales, viral events), auto scaling is too slow. Use on-demand mode or pre-warm with scheduled scaling.

05

Transactions

DynamoDB transactions provide atomic, consistent, isolated operations across multiple items — potentially across multiple tables in the same region. They cost 2Ɨ normal operations.

TransactWriteItems

TransactWriteItems Characteristics

  • āœ…Up to 25 items or 4 MB in one atomic transaction
  • āœ…Operations: Put, Update, Delete, ConditionCheck
  • āœ…All succeed or all fail — atomicity guaranteed
  • āœ…2Ɨ WCU cost per item
  • āœ…Idempotency: ClientRequestToken — safe to retry without double-applying
  • āœ…Cannot span regions (Global Tables limitation)
transact-write-example.tstypescript
// Transfer funds between two accounts atomically
const params = {
  TransactItems: [
    {
      Update: {
        TableName: "Accounts",
        Key: { PK: { S: "ACCOUNT#sender" }, SK: { S: "BALANCE" } },
        UpdateExpression: "SET #balance = #balance - :amount",
        ConditionExpression: "#balance >= :amount",  // sufficient funds
        ExpressionAttributeNames: { "#balance": "balance" },
        ExpressionAttributeValues: { ":amount": { N: "100" } }
      }
    },
    {
      Update: {
        TableName: "Accounts",
        Key: { PK: { S: "ACCOUNT#receiver" }, SK: { S: "BALANCE" } },
        UpdateExpression: "SET #balance = #balance + :amount",
        ExpressionAttributeNames: { "#balance": "balance" },
        ExpressionAttributeValues: { ":amount": { N: "100" } }
      }
    }
  ]
};
// Both updates succeed or both fail — no partial state

Transaction Conflicts

Transaction Conflicts

  • āŒTransactionCanceledException — one or more conditions failed or conflict detected
  • āŒTransactionConflict: another transaction modified the same item simultaneously
  • āŒRetry strategy: exponential backoff with jitter
  • āŒTransaction isolation: serializable — no two transactions interleave
  • āŒCancellation reasons returned per item — diagnose which item failed

When NOT to Use Transactions

Transactions cost 2Ɨ and have higher latency. Avoid on high-throughput paths where condition expressions alone suffice. Single-item operations are already atomic without transactions. Use transactions only when you need atomicity ACROSS multiple items.

06

TTL (Time to Live)

TTL enables automatic item expiration. You specify an attribute that holds a Unix epoch timestamp — DynamoDB deletes items after that time passes.

TTL Characteristics

  • āœ…TTL attribute must be Number type — Unix epoch seconds
  • āœ…Deletion is asynchronous — can be up to 48 hours after expiry
  • āœ…Expired but not yet deleted items still returned by reads — filter in application
  • āœ…No storage charge after expiry timestamp (even if not yet deleted)
  • āœ…Items without the TTL attribute never expire
  • āœ…TTL deletes appear in DynamoDB Streams — can trigger Lambda on expiry

TTL Use Cases

Use CaseTTL StrategyBenefit
Session expirySet TTL to session timeoutAuto-cleanup without cron jobs
Cache entriesSet TTL to cache durationSelf-managing cache layer
Temporary locksSet TTL to lock timeoutAuto-release distributed locks
Event deduplicationSet TTL to processing windowExpire seen event IDs
Soft-delete cleanupSet TTL on soft-deleted itemsAuto hard-delete after retention
Regulatory complianceSet TTL to retention periodAuto-delete after legal requirement

TTL + Streams Pattern

TTL deletions appear in DynamoDB Streams with userIdentity.principalId = "dynamodb.amazonaws.com". Use this to trigger Lambda on expiry: audit logging of deleted items, cascade deletes to other systems, or archival to S3.

07

Cost Optimization

Cost Optimization Strategies

  • āœ…Right-size items: smaller items = lower RCU/WCU cost per operation
  • āœ…Short attribute names: they count toward 400 KB limit and storage cost
  • āœ…Choose capacity mode based on traffic pattern (provisioned for steady, on-demand for spiky)
  • āœ…Reserved capacity for predictable workloads (up to 77% discount)
  • āœ…Standard-IA table class for infrequently accessed data (lower storage, higher read cost)
  • āœ…Compress large attribute values (gzip binary attributes)
  • āœ…Use sparse indexes — only items needing the query appear in the index
  • āœ…Minimize GSI count — each GSI adds write amplification
  • āœ…Use KEYS_ONLY projection on GSIs when possible
  • āœ…Eventually consistent reads by default (half the RCU cost)
OptimizationSavingsTrade-off
Reserved capacity (3yr)Up to 77%Commitment, less flexibility
Eventually consistent reads50% RCUMay read stale data (< 1s)
On-demand → ProvisionedUp to 85% at steady stateRisk of throttling
Standard-IA table class~60% storage costHigher per-read cost
Fewer GSIsLinear WCU reductionFewer access patterns supported
Smaller itemsLinear cost reductionMay need more queries
08

Interview Questions

Q:How do you calculate RCU for a 10 KB item with eventually consistent reads?

A: ceil(10 KB / 4 KB) Ɨ 0.5 = ceil(2.5) Ɨ 0.5 = 3 Ɨ 0.5 = 1.5, rounded up to 2 RCU. For strongly consistent: 3 Ɨ 1 = 3 RCU. For transactional: 3 Ɨ 2 = 6 RCU. Item size is the primary cost driver — keep items small.

Q:When would you choose on-demand over provisioned capacity?

A: On-demand for: new tables with unknown traffic, spiky/unpredictable workloads, dev/staging environments, applications where simplicity matters more than cost. Provisioned for: predictable sustained traffic, cost-sensitive production workloads, when you can commit to reserved capacity. On-demand is ~7Ɨ more expensive per request at steady state.

Q:What are DynamoDB transactions and when should you use them?

A: TransactWriteItems/TransactGetItems provide atomic operations across up to 25 items (potentially across tables). Use for: fund transfers, booking systems, any operation where partial completion is unacceptable. Cost: 2Ɨ normal operations. Don't use for: single-item operations (already atomic), high-throughput paths where condition expressions suffice.

Q:How does TTL work and what are its limitations?

A: TTL uses a Number attribute (Unix epoch seconds). DynamoDB's background process deletes expired items, but deletion can be delayed up to 48 hours. Expired items may still appear in reads — applications must filter them. TTL deletes are free (no WCU charged) and appear in Streams for downstream processing.

Q:What happens when a DynamoDB table is throttled?

A: Requests return ProvisionedThroughputExceededException. AWS SDK retries automatically with exponential backoff. Causes: exceeded provisioned capacity, hot partition (even with sufficient table-level capacity), or under-provisioned GSI throttling base table writes. Solutions: increase capacity, fix hot partition keys, enable auto scaling, or switch to on-demand.

09

Common Mistakes

šŸ’°

Using on-demand mode for predictable high-throughput workloads

On-demand is ~7Ɨ more expensive per request than provisioned at steady state. For a table doing 10,000 WCU sustained, on-demand costs significantly more. Switch to provisioned with auto scaling once traffic patterns are understood.

āœ…Switch to provisioned capacity with auto scaling once traffic patterns stabilize, and consider reserved capacity for further savings.

šŸ”„

Using transactions for single-item operations

Individual DynamoDB operations (PutItem, UpdateItem) are already atomic per item. Using TransactWriteItems for a single item doubles the cost for no benefit. Transactions are only needed for atomicity ACROSS multiple items.

āœ…Use standard PutItem/UpdateItem with ConditionExpression for single-item atomic operations instead of transactions.

ā°

Not filtering expired TTL items in application code

TTL deletion can be delayed up to 48 hours. If your application reads items and doesn't check the TTL attribute, it may process expired items. Always add a filter: if item.ttl < currentTime, treat as deleted.

āœ…Add application-level filtering to check the TTL attribute against current time and discard expired items from query results.

šŸ“ˆ

Ignoring burst capacity limits

DynamoDB banks unused capacity for up to 5 minutes of burst. But if your traffic consistently exceeds provisioned capacity, burst runs out and throttling begins. Burst is for short spikes, not sustained over-provisioning.

āœ…Provision capacity for your sustained throughput needs and rely on burst only for brief spikes. Use auto scaling for gradual increases.

šŸ“Š

Not accounting for GSI capacity separately

GSIs have their own provisioned capacity. If a GSI is under-provisioned, writes to the BASE TABLE get throttled (DynamoDB can't replicate). Monitor and provision GSI capacity independently from the base table.

āœ…Monitor and provision each GSI's capacity independently. Set up CloudWatch alarms on GSI throttling metrics.