On-demand DynamoDB is wonderful until a known cliff (Black Friday, product launch, nightly batch) makes provisioned-plus-auto-scaling dramatically cheaper—if you switch without inventing throttles mid-checkout. The unfair advantage is a rehearsal playbook: pre-warm provisioned capacity, flip modes in a maintenance window you control, and keep adaptive capacity headroom for hot partitions.
⚡ TL;DR: Never flip modes under peak; pre-provision RCU/WCU above the cliff; use auto-scaling with aggressive scale-in cooldown; verify CloudWatch
ThrottledRequestsstays zero for 30 minutes post-cut; keep a fast rollback to on-demand. Pair with DynamoDB Hot Key Mitigation and Lambda Reserved Concurrency.
Know your cliff shape
# ✅ Estimate peak from Insights / billing metrics, not folklore
# PeakConsumedWriteCapacityUnits * safety (1.4–2.0) → target provisioned WCU
peak_wcu = 12_000 # from CloudWatch max over last 4 similar events
safety = 1.5
target_wcu = int(peak_wcu * safety) # 18_000
# On-demand bill ≈ peak * hours * price; provisioned = reserved + auto-scale burst
| Mode | Best when | Failure mode |
|---|---|---|
| On-demand | Spiky / unknown | Cost cliffs surprise finance |
| Provisioned + auto-scale | Predictable cliffs | Under-provision → throttle |
| Provisioned + reserved | Steady 24×7 | Waste if traffic vanishes |
Switch without throttling checkout
# ✅ Pre-warm: create provisioned table settings BEFORE traffic arrives
# 1) UpdateTable → BillingMode=PROVISIONED with RCU/WCU >= target
# 2) Wait TableStatus=ACTIVE and Consumed < Provisioned for 10+ min
# 3) Enable Application Auto Scaling (target 70% utilization)
# 4) Only then route launch traffic / open feature flag
# ❌ Flip to provisioned at T-0 with default 5 WCU — instant throttle storm
# ✅ boto3 cutover helper (run in change window, not in the request path)
import boto3
ddb = boto3.client("dynamodb")
def to_provisioned(table: str, rcu: int, wcu: int) -> None:
ddb.update_table(
TableName=table,
BillingMode="PROVISIONED",
ProvisionedThroughput={"ReadCapacityUnits": rcu, "WriteCapacityUnits": wcu},
)
waiter = ddb.get_waiter("table_exists")
waiter.wait(TableName=table) # ACTIVE again
def rollback_on_demand(table: str) -> None:
ddb.update_table(TableName=table, BillingMode="PAY_PER_REQUEST")
Hot partitions still bite
Mode switching does not fix a single hot partition. Adaptive capacity helps, but shard write keys before the cliff. Monitor SuccessfulRequestLatency, ThrottledRequests, and per-partition metrics if enabled.
# ✅ Canary: synthetic checkout writer at 20% of cliff for 15 minutes post-switch
# Abort / rollback if ThrottledRequests > 0 sustained for 60s
❌ Assuming on-demand → provisioned is instant and free of account-level soft limits — file limit increases days ahead for large WCU jumps.
Closing checklist
✅ Dos
– ✅ Size from historical peak × safety factor
– ✅ Pre-warm provisioned capacity in a quiet window
– ✅ Enable auto-scaling with slow scale-in
– ✅ Canary write path before opening the floodgates
– ✅ Document rollback to on-demand as a one-command runbook
❌ Don’ts
– ❌ Don’t switch modes during the cliff itself
– ❌ Don’t ignore hot-key sharding before a launch
– ❌ Don’t set scale-in cooldown so aggressive you thrash
– ❌ Don’t skip account quota tickets for large WCU
Related reading
- DynamoDB Hot Key Mitigation: Write Sharding With Scatter-Gather Reads
- Lambda Reserved Concurrency: Bulkheads That Protect Tenant Workloads
- Read-Your-Writes on Global Tables
- SLA Error Budgets: Drive Automated Load Shedding
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
