AWS Events
Applying normalized relational schemas to cloud NoSQL triggers severe performance collapse, requiring single-partition pre-aggregation to eliminate expensive compute joins at scale.
AI-assisted summary of AWS re:Invent 2019: [REPEAT 1] Amazon DynamoDB deep dive: Advanced design patterns (DAT403-R1) — original source. Check the original for full context.
Source published:
Key ideas
- Amazon migrated roughly 12,000 internal services away from Oracle to NoSQL, decommissioning over 3,000 Oracle instances by re-architecting systems around predictable OLTP access patterns rather than normalized relational schemas. (0:00)
- Modern cloud economics invert the historical cost model that produced relational databases: storage now costs pennies per gigabyte while compute represents the dominant expense, making scale-out denormalization far more economical than runtime joins. (2:16)
- DynamoDB replaces expensive read-time relational joins by pre-aggregating heterogeneous entities into shared physical partitions, allowing parent and child records to be retrieved in a single network round-trip. (10:26)
- Physical DynamoDB partitions enforce an upper limit of approximately 1,000 WCUs and 3,000 RCUs; workloads exceeding these thresholds require write sharding via computed suffixes and client-side scatter-gather logic. (14:45)
- Storing lightweight delta records instead of full 50 KB documents cut write capacity consumption from 1,000 WCUs to 50 WCUs for an insurance quoting workload, shifting assembly downstream to client devices. (53:19)
Detailed notes
## Background Published on YouTube by AWS Events on December 7, 2019, this presentation captures a technical session delivered at AWS re:Invent 2019. The session focuses on advanced Amazon DynamoDB design patterns and practical data modeling strategies drawn from large-scale implementations across Amazon businesses, covering index overloading, materialized graphs, and high-scale relational modeling.
## TL;DR Presenter Rick Houlihan explains how to shift from traditional relational schemas to access-pattern-first NoSQL modeling optimized for cloud economics and predictable scale. Drawing lessons from Amazon's internal migration away from thousands of Oracle databases, he demonstrates how to pre-aggregate heterogeneous data into single-table designs that mimic relational joins without compute-heavy read-time overhead. The talk walks through concrete techniques—such as composite keys, Global Secondary Index overloading, write sharding, and change data capture pipelines—illustrated via an end-to-end 23-access-pattern employee portal and real-world payload optimizations.
## Atmosphere & style The session carries the authoritative, no-nonsense energy of a veteran systems architect who has navigated massive production migrations firsthand. Houlihan speaks with brisk pacing, practical humor, and sharp conviction, actively dismantling industry buzzwords and challenging attendees to unlearn relational dogmas. Watching it feels like an intensive masterclass in distributed engineering, combining architectural rigor with pragmatic, hard-won insights from high-stakes operational environments.
## Bulletin - [00:00](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=0s) Amazon migrated roughly 12,000 internal services away from Oracle to NoSQL, decommissioning over 3,000 Oracle instances by re-architecting systems around predictable OLTP access patterns rather than normalized relational schemas. - [02:16](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=136s) Modern cloud economics invert the historical cost model that produced relational databases: storage now costs pennies per gigabyte while compute represents the dominant expense, making scale-out denormalization far more economical than runtime joins. - [10:26](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=626s) DynamoDB replaces expensive read-time relational joins by pre-aggregating heterogeneous entities into shared physical partitions, allowing parent and child records to be retrieved in a single network round-trip. - [14:45](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=885s) Physical DynamoDB partitions enforce an upper limit of approximately 1,000 WCUs and 3,000 RCUs; workloads exceeding these thresholds require write sharding via computed suffixes and client-side scatter-gather logic. - [53:19](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=3199s) Storing lightweight delta records instead of full 50 KB documents cut write capacity consumption from 1,000 WCUs to 50 WCUs for an insurance quoting workload, shifting assembly downstream to client devices.
## Key takeaways - [02:16](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=136s) Relational 3NF modeling was invented to minimize expensive storage at the cost of compute, whereas cloud-native architectures should optimize for expensive CPU cycles over cheap disk space. - [04:35](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=275s) NoSQL data modeling demands an access-pattern-first methodology; forcing normalized relational designs into distributed NoSQL systems inevitably leads to severe operational pain and escalating costs at scale. - [07:11](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=431s) Managed cloud-native architectures eliminate the operational overhead of self-hosted clusters like Cassandra or MongoDB, which require complex node rebuilds and manual re-sharding routines. - [08:26](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=506s) A DynamoDB partition key defines the boundary of an item collection, while sort keys uniquely identify items and enable targeted range queries across pre-joined heterogeneous data. - [12:58](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=778s) Global Secondary Indexes provide SLA-backed automatic replication with typical p99 latencies under 10 milliseconds, but projecting all attributes duplicates both write capacity consumption and storage costs. - [17:22](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1042s) Overloading generic GSI attributes across multiple entity types consolidates provisioned capacity pools and simplifies CloudWatch monitoring compared to managing fragmented single-purpose indexes. - [22:00](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1320s) DynamoDB request routers maintain local partition routing caches during sustained load, often yielding lower latency under heavy traffic while leveraging a rolling five-minute burst bucket to absorb spikes without throttling. - [24:07](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1447s) The phrase 'non-relational data' is a marketing misnomer; all business domains have inherent entity relationships that should be modeled explicitly via composite keys and inverted index mappings. - [28:18](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1698s) Inverting table keys on a Global Secondary Index converts directed relationships into undirected graphs, efficiently resolving many-to-many associations without duplicating base entity records. - [36:28](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2188s) Because DynamoDB does not execute server-side aggregations like sums or averages, analytical rollups must be handled asynchronously via DynamoDB Streams, Lambda, and CDC pipelines. - [39:38](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2378s) Filter expressions discard unmatching items only after reading them; combining multiple attributes into composite sort keys with the begins_with operator prevents wasted read capacity units. - [41:33](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2493s) An enterprise portal supporting 23 distinct access patterns across employees, projects, tickets, and facilities can be consolidated entirely into a single DynamoDB table. - [45:51](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2751s) Offloading differential calculations, state reconciliation, and multi-record assembly to client devices reduces application server fleet sizes and operational infrastructure overhead. - [50:11](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=3011s) Hierarchical data modeling using composite paths paired with begins_with queries enables flexible subtree aggregations across organizational levels like cities, buildings, and floors. - [57:32](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=3452s) NoSQL does not deprecate relational databases; distributed NoSQL engines should handle high-scale OLTP and decision-support workloads, while relational systems remain optimal for ad-hoc OLAP analytics.
## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=0s) Introduction and the Amazon Migration Journey
Rick Houlihan introduces himself as a Principal Technologist for NoSQL at AWS, opening the session by acknowledging the massive turnout in Las Vegas despite all the competing evening distractions. He shares his deep background in NoSQL data modeling: building out the specialist practice across North America and serving on the internal "Black Belt" team at Amazon. On that team, he helped lead the monumental migration of roughly 12,000 Amazon services away from Oracle to NoSQL databases, shutting down over 3,000 Oracle instances in the process.
The hard-earned lessons from this multi-year migration form the bedrock of the talk. While basic design concepts had been introduced previously, Houlihan emphasizes that this session targets advanced and complex NoSQL data modeling techniques that many practitioners assume are impossible outside of relational engines. The goal is to avoid high-level API overviews or generic feature listings and instead focus on real-world engineering: how to structure data models so that applications perform predictably and operate with maximum economic efficiency. Although several diagrams and schema walkthroughs in the talk use synthetic examples, they directly map to proven access patterns deployed in critical production services across Amazon.
[02:16](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=136s) A Brief History of Data Processing and the Economics of Compute vs. Storage
To explain why the industry is moving toward NoSQL, Houlihan walks through the historical arc of data processing, framing it as a succession of peaks and valleys driven by "data pressure"—the friction that occurs when the system cannot execute required operations fast enough or cheaply enough. The earliest human database was biological memory: high availability when conscious, but possessing questionable fault tolerance, no true durability, and strictly single-user access. Human civilization addressed this pressure by inventing ledger accounting, which powered public- and private-sector records for millennia.
```mermaid flowchart TD accTitle: Evolution of Data Processing Technology accDescr: Chronological progression from human memory to modern distributed NoSQL systems driven by data pressure.
subgraph Manual["Manual Eras"] A["Human Memory<br/>(Single-user, non-durable)"] --> B["Ledger Accounting<br/>(Millennia of structured records)"] end
subgraph Mechanical["Mechanical & Early Digital"] B --> C["1890: Hollerith Punch Cards<br/>(Machine-readable sorting)"] C --> D["Tapes & Disks<br/>(Random access file systems)"] end
subgraph Database["Database Architectures"] D --> E["1980s: Relational DBs<br/>(Normalized to save expensive disk)"] E --> F["Modern Era: Distributed NoSQL<br/>(Denormalized to save expensive CPU)"] end ```
By the 1880 US Census, ledger accounting broke down under extreme data pressure: processor Herman Hollerith spent seven to eight years of a ten-year census cycle manually tabulating ledger records. To prevent total operational failure for the 1890 census, Hollerith invented the machine-readable punch card and tabulating sorter, dramatically collapsing processing times and igniting modern data processing innovation. Over the subsequent century, this spurred the invention of paper tape, magnetic tape, distributed block devices, and random-access file systems.
Houlihan stresses that the relational database management system (RDBMS) emerged in the late 1970s and 1980s to solve a very specific hardware constraint: storage subsystem bottlenecks. Disk storage back then was astronomically expensive, whereas compute was comparatively cheaper to dedicate toward on-the-fly normalization. Normalizing data eliminated redundancy on disk by deduplicating records into separate tables linked by keys. Four decades later, that economic equation has completely inverted:
> “When I deploy applications [in the] data center today, the storage is pennies per gigabyte, the CPU is dollars per CPU minute. I don't want to use technologies optimized for yesteryear's situations; I want to optimize for today's situations, and this is why we are going to NoSQL databases.”
- Herman Hollerith required 8 years using paper ledgers to tally the 1880 US Census; his punch card system processed the 1890 census in a fraction of that time. - In the 1980s, storage was the most expensive resource in the data center, making third normal form (3NF) essential to deduplicate disk footprint. - Modern cloud architecture flips this: storage costs pennies per gigabyte, while compute cycles consume the largest portion of the monthly bill.
[04:35](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=275s) The Technology Adoption Curve and NoSQL vs. Relational Fit
Transitioning to new database paradigms exposes engineering teams to an adoption curve that begins with a technology trigger and often plunges into failure if developers apply old habits to new systems. Houlihan admits that Amazon itself experienced this firsthand during its internal migrations: initial small workloads seemed straightforward, but applying normalized relational patterns to scaled-out NoSQL systems produced catastrophic performance degradations and spiraling infrastructure bills. Teams mistakenly concluded the technology was broken, when in reality they were attempting to run normalized models on engines designed strictly for denormalization:
> “It’s really easy to do small things badly and think that you’re doing okay, right? As you move those bigger workloads you start to find out that wow, this thing’s not working very well, we’re not having the experiences we expected, it’s not cheaper, it’s not faster, why is this happening? You start to analyze the system and you really, when you figure out you didn’t know how to use the new technologies, you tried to use them the same way you used the old technologies. In the case of NoSQL, that’s about data models: if you use a normalized data model, you are going to have pain at scale.”
Houlihan rejects the claim that NoSQL data modeling is inherently harder than relational modeling; it simply requires a shift from schema-first thinking to access-pattern-first thinking. Once developers grasp these techniques, NoSQL design is just as straightforward as standard entity-relationship mapping.
Furthermore, NoSQL is not a universal drop-in replacement for every use case. It is optimized for applications with known, repeatable access patterns that can leverage pre-joined, denormalized layouts. Conversely, ad-hoc, exploratory queries where access paths cannot be predicted in advance are far better suited for relational or Online Analytical Processing (OLAP) systems. However, because approximately 90% of business applications are Online Transaction Processing (OLTP) engines running fixed business workflows, NoSQL data modeling is the single most critical architecture skill for modern software developers.
| Dimension | Relational Databases (RDBMS / OLAP) | Modern NoSQL (OLTP) | |:--- |:--- |:--- | | **Primary Economic Optimization** | Minimizes disk footprint via normalization | Minimizes CPU consumption via denormalization | | **Query Flexibility** | High (Supports ad-hoc SQL joins and arbitrary filters) | Low (Access patterns must be known beforehand) | | **Best-fit Workload** | OLAP, ad-hoc business intelligence, unknown patterns | OLTP, high-scale predictable business workflows (~90% of apps) | | **Scaling Bottleneck** | Compute and join complexity across partitioned disks | Up-front data modeling and access-pattern analysis |
[07:11](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=431s) Managed Cloud-Native Scale: Amazon DynamoDB vs. Self-Hosted Clusters
Houlihan pivots to the architectural value of Amazon DynamoDB as a fully managed, cloud-native NoSQL database. Rather than getting lost in API signatures, he highlights the operational burden of running self-hosted, legacy NoSQL clusters such as MongoDB or Apache Cassandra. When self-managed clusters scale out, operational tasks—such as rebuilding failed storage devices, managing nodes, and rebalancing partitions across new shards—become an overwhelming drag on developer productivity.
Large enterprise customers running massive self-managed deployments often find that scaling their clusters becomes practically impossible: adding capacity to a MongoDB cluster that has reached 15 to 20 shards can take weeks or even months of careful node staging and re-sharding operations. DynamoDB removes this operational friction entirely by decoupling capacity scaling, provisioning, and partition management behind a managed service boundary. It delivers single-digit millisecond latency consistently at any volume, handling production single-table workloads at Amazon that exceed 7 million transactions per second. Dedicating valuable engineering payroll to operational node firefighting instead of writing business logic represents an unnecessary and expensive anti-pattern.
[08:26](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=506s) The Scale of DynamoDB and Core Table Constructs
NoSQL systems operate at an entirely different magnitude than legacy providers when pushed to the limit. During Prime Day 2019, Amazon Customer Delivery Organizations (CDO) tables achieved a staggering 54.5 million transactions per second—a benchmark reportedly broken again shortly after during Cyber Monday and Black Friday. Rick Houlihan points out that this kind of immense throughput is simply not something seen from legacy NoSQL providers.
Understanding data modeling in DynamoDB starts with fundamental primitives: tables, items, and attributes. At its simplest, a DynamoDB table acts as an object repository where items require only a single partition key (PK), while every other attribute remains optional. In this simple key-value configuration, an item is retrieved by querying for an exact partition key match. This resembles a document store like Amazon DocumentDB, where a collection holds documents identified by an `_id` field. However, DynamoDB diverges sharply with wide-column modeling by allowing a composite primary key consisting of a partition key and a sort key (SK). The partition key forms an aggregation boundary—a distinct partition—while the sort key uniquely identifies items inside that boundary and permits range-based query operations.
```mermaid flowchart TD accTitle: DynamoDB Single Table vs Document Store accDescr: Visual comparison between simple key-value lookups and composite partition-and-sort-key range queries.
subgraph SimpleKV["Key-Value Store (e.g. DocumentDB / DynamoDB PK only)"] D1["Partition Key: customer_id = X"] -->|"Direct Lookup"| R1["Returns Single Document / Item"] end
subgraph CompositeKey["Wide-Column Store (DynamoDB PK + SK)"] P1["Partition Key: customer_id = X"] P1 --> SK1["Sort Key: order_date = 2019-11-01"] P1 --> SK2["Sort Key: order_date = 2019-11-15"] P1 --> SK3["Sort Key: order_date = 2019-12-01"] SK1 & SK2 & SK3 -->|"Range Query: > 30 days"| R2["Returns Filtered Set of Heterogeneous Items"] end ```
- Amazon CDO tables sustained 54.5 million TPS on Prime Day 2019. - Items require only a partition key unless configured with a composite primary key (partition key plus sort key). - Partition keys define a collection boundary; sort keys permit selective range queries (e.g., date ranges, inequalities) across those collections.
[10:26](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=626s) Model Joins and Pre-Aggregating Heterogeneous Data
Rather than forcing items into uniform shapes, DynamoDB partitions are designed to hold heterogeneous collections of data. Modeling in NoSQL does not mean discarding the concept of joins; instead, it shifts relational join logic into physical data modeling. By pre-aggregating related entities within the same physical partition, applications can retrieve parent and child records in a single network round-trip.
As Rick Houlihan explains: “So in essence what we've done is joined objects into a partition and we're going to start to decorate these objects with additional attributes that we're going to index to create additional aggregations or grouping of objects.” This approach directly mirrors the internal mechanics of a relational engine executing hash joins over non-indexed columns, where the database engine constructs a hash table and looks up rows as it iterates across an outer collection. DynamoDB persists data directly inside that distributed hash table layout.
In practice, a customer partition identified by `CustomerID` can contain customer profile metadata alongside multiple order records, with sort keys formatted to distinguish metadata from time-ordered events. A single query requesting records updated within the last 30 days can fetch the customer profile and every recent order simultaneously. This eliminates complex, multi-hop operational workflows common in relational patterns—such as querying a customer table, fetching their order IDs from a secondary table, and looping over an items table to retrieve product lines.
[12:58](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=778s) Global Secondary Indexes (GSIs) and Projections
To service alternative query patterns across items grouped differently than the base table, DynamoDB utilizes Global Secondary Indexes (GSIs). A GSI operates as an automatically replicated, secondary projection of the underlying base table. Whenever data is written or updated on the base table, DynamoDB transparently replicates those mutations to the GSI with an SLA-backed guarantee.
Houlihan highlights the operational distinction between managed cloud indexes and open-source alternatives: “Cassandra they even tell you don't don't use indexes because there's no way to maintain consistency on their indexes DynamoDB indexes are guaranteed to be consistent.” Although GSI updates are eventually consistent, they boast p99 latencies within 10 milliseconds when properly provisioned.
When defining a GSI, developers must choose which attributes to project into the index: - **Keys only (`KEYS_ONLY`)**: Minimizes storage and write provisioning costs, projecting only the base table and index keys. - **Specific attributes (`INCLUDE`)**: Balances read efficiency and cost by projecting only attributes needed to satisfy secondary read patterns without subsequent table fetches. - **All attributes (`ALL`)**: Duplicates the entire item into the GSI.
Projecting all attributes doubles both the storage footprint and the consumed Write Capacity Units (WCUs) of each write on the parent table. To optimize cost, systems should project only what the downstream access patterns strictly require.
[14:45](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=885s) Partition Sharding and Write-Throughput Scaling
DynamoDB uses an internal, unordered hash index to distribute partition keys evenly across an arbitrary physical storage key space. Storage partitions split and replicate automatically as storage volume or throughput demands grow. However, an individual physical storage node enforces hard throughput constraints—specifically, an upper boundary of roughly 1,000 WCUs per second per partition key.
When applications build dense aggregations—such as retail systems indexing all customer purchases by sales channel (`source = online`) or product items by ASIN—writes will bottleneck on that single key if traffic exceeds 1,000 WCUs per second. To scale beyond this hardware limit, DynamoDB designs must employ write sharding (salting). Developers append a calculated suffix (e.g., a pseudo-random number or hash modulo) to the GSI partition key, scattering high-frequency writes evenly across multiple distinct physical partitions.
```mermaid flowchart LR accTitle: Write Sharding Across Physical Partitions accDescr: Inbound writes are salted with an integer suffix to split traffic across physical partitions and queried concurrently by a data layer API.
subgraph Ingest["Ingestion Layer"] W["High-Volume Inbound Writes"] end
subgraph ShardedGSIs["Salted GSI Partitions"] S0["Source#online_0"] S1["Source#online_1"] S2["Source#online_N"] end
subgraph DataLayer["Application / Data Layer API"] P["Parallel Scatter-Gather Engine"] C["Aggregated Client Result"] end
W -->|"Salt with Hash Modulo"| S0 & S1 & S2 S0 & S1 & S2 -->|"Concurrent Async Queries"| P P --> C ```
To prevent downstream clients and frontend engineers from having to manually coordinate salted partitions, this scatter-gather complexity should live behind a centralized data access layer API. This abstraction queries all sharded partitions concurrently, merges the results in parallel, and delivers a unified collection back to the caller. Houlihan points out that this abstraction is critical because sharding is rarely applied uniformly: “oftentimes when I'm designing for DynamoDB I need to write shard some of my customers maybe but not all of them”. This mirrors the architecture behind internal AWS service configuration limits and IAM quotas, where account-level scaling requests are frequently driven by the need to scale DynamoDB write partitions dynamically under the hood.
[17:22](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1042s) Reusing Global Secondary Indexes with Generic Keys
To support access patterns like write sharding, an application API inspects conditions provided by the user, shards incoming writes, and queries across multiple shards to assemble results. Just as generic keys (`PK` and `SK`) are used on the base table to co-locate multiple entity types, the same pattern applies to Global Secondary Indexes (GSIs). By designing GSIs with generic attributes, multiple item types can be projected into a single index. Creating dedicated GSIs for distinct access patterns—such as querying orders by source or retrieving items by status—forces teams to maintain isolated capacity pools, manage independent CloudWatch alarms, and risk stranding provisioned throughput when patterns do not run concurrently. Reusing an overloaded GSI eliminates structural sprawl while consolidating provisioned capacity across alternating workloads.
```mermaid flowchart TD accTitle: GSI Overloading Flow accDescr: Diagram illustrating how different access patterns share a single GSI using generic partition and sort keys without collision.
subgraph TableData["Base Table Items"] Order["Order Item<br/>PK: Order#123<br/>SK: Details"] OrderItem["Order Item Part<br/>PK: Order#123<br/>SK: Item#456"] end
subgraph OverloadedGSI["Shared GSI (Generic Keys)"] GSI_PK["GSI_PK: Source / ASIN"] GSI_SK["GSI_SK: Status / Date"] end
subgraph Queries["Query Execution"] Q1["Query by Source<br/>(Yields Order Items)"] Q2["Query by ASIN<br/>(Yields Product Orders)"] end
Order -->|"Projected into"| OverloadedGSI OrderItem -->|"Projected into"| OverloadedGSI OverloadedGSI --> Q1 OverloadedGSI --> Q2 ```
Partition and sort key query conditions strictly determine what data returns from an overloaded index, preventing disparate objects from colliding. If key values share the same namespace, partition keys can be prefixed with the entity type name (such as `ORDER#` or `ITEM#`). However, when indexing distinct identifiers like an ASIN versus a source warehouse code, collisions are structurally impossible. When querying by source partition key, order items from that source return; querying by an ASIN returns only items linked to that specific ASIN. As Rick Houlihan explains:
> “Because if I create two GSIs, I got allocate two capacity buckets, got to set up two sets of alerts and alarms, and I can't reuse the capacity that's allocated to one when these access patterns are probably not happening in parallel.”
- **Operational efficiency**: Overloading GSIs reduces the total number of cloud infrastructure components requiring production monitoring. - **Capacity pooling**: Sharing GSI capacity prevents unused provisioned throughput from sitting idle during alternating query cycles. - **Collision avoidance**: Combining domain-specific entity prefixes with partition values guarantees isolation inside shared indexes.
---
[19:06](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1146s) Workload Distribution and Heat Map Anti-Patterns
Scaling any NoSQL database fundamentally depends on distributing throughput across the entire partition key space. When an application drives high-velocity operations through a narrow subset of keys, hot partitions develop. A diagnostic heat map plotting physical storage nodes on the y-axis against time on the x-axis illustrates this imbalance vividly: a single server (such as server 11 in a fleet) glows under intense read/write pressure while neighboring nodes sit idle. High-velocity access patterns concentrated on a small set of keys degrade throughput and create throttles, presenting a classic distributed systems anti-pattern. While Amazon CloudWatch provides tooling to inspect hot partition keys under pressure, eliminating these hot spots requires application-level distribution.
```mermaid flowchart LR accTitle: Hot Key vs Uniform Distribution accDescr: Contrast between anti-pattern hot key routing hitting one server versus uniform distribution spreading across hundreds of storage nodes.
subgraph AntiPattern["Hot Key Anti-Pattern"] TrafficA["High-Velocity Requests"] --> Node11["Storage Node 11<br/>(Hot Key / High Pressure)"] TrafficA -.-> IdleNodes["Other Storage Nodes<br/>(0% Utilization)"] end
subgraph UniformPattern["Uniform Sharded Pattern"] TrafficB["Evenly Distributed Requests"] --> Fleet["450+ Storage Nodes<br/>(White Noise / 40k-50k WCU)"] end ```
The objective is to spread request volume uniformly across key ranges so that storage nodes participate equally in the workload. When sharding and partitioning are configured correctly, the resulting heat map resembles uniform white noise on an untuned television set—what Rick Houlihan characterizes as a system "lighting up like a Christmas tree." A production fleet of approximately 450 servers sustaining 40,000 to 50,000 write capacity units (WCUs) demonstrates this balance, spreading load smoothly despite sudden spikes like Black Friday or Cyber Monday thundering herds. Rick observes the visual difference:
> “This looks like the white noise on the TV when you change between channels, right, that's the—I don't know if anybody that is even happening anymore, does it, okay, I'm an old man, I can't help it.”
---
[20:53](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1253s) Just-in-Time Provisioning and Enterprise Cost Savings
Migrating from legacy data tier technologies to cloud-native NoSQL enables dynamic just-in-time provisioning across storage volume and throughput capacity. On-premises relational databases, alongside self-managed NoSQL platforms like MongoDB and Apache Cassandra, require sizing infrastructure to accommodate peak demand. Because enterprise applications rarely operate at peak capacity continuously, average enterprise data center utilization hovers at roughly 10%. Maintaining permanent headroom for worst-case bursts results in substantial capital and operational waste across server fleets.
```mermaid flowchart LR accTitle: Legacy Provisioning vs DynamoDB Auto Scaling accDescr: Comparison of flat peak provisioning in traditional databases versus dynamic auto-scaling in DynamoDB following demand curves.
subgraph Legacy["Traditional Provisioning"] PeakCap["Static Capacity Provisioned for Peak"] ActualUsage["Actual Demand (~10% Avg Utilization)"] PeakCap -.->|"Wasted Idle Spend"| ActualUsage end
subgraph DynamoDB["DynamoDB Auto-Scaling"] AutoCap["Auto-Scaled Capacity Curve"] FulfillmentDemand["Fulfillment Center Workload"] AutoCap ===|"Tracks Closely (~40% Cost Savings)"| FulfillmentDemand end ```
DynamoDB transforms this operational model by integrating dynamic auto-scaling that tracks real-time traffic profiles. Production services deployed inside Amazon fulfillment centers highlight this shift: switching static allocations to auto-scaling yielded roughly 40% cost reductions across warehouse applications. For services exhibiting structured, predictable demand curves throughout standard operating shifts, capacity scales down during low-activity intervals and ramps up during processing peaks without human intervention. Rick points out the stark contrast in utilization:
> “You are provisioning for peak, you are hoping you are consuming a high percentage of that capacity that you have provisioned, but the reality is the average data center utilization in the enterprise is 10 percent.”
- **Legacy utilization**: Typical enterprise server capacity runs at roughly 10% average utilization over time. - **Auto-scaling savings**: Real-world fulfillment center workloads save approximately 40% on infrastructure spend by enabling DynamoDB auto-scaling. - **Dynamic elasticity**: Storage footprint and throughput scale independently to mirror changing operational demand curves.
---
[22:00](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1320s) Request Router Mechanics and the Five-Minute Burst Bucket
A counter-intuitive operational property of DynamoDB is that it can exhibit lower latency as request volume scales up. In a synthetic benchmark scaling beyond one million requests per second, response times dropped and stabilized at single-digit millisecond levels during peak utilization. This occurs because DynamoDB decouples incoming requests through a massively distributed fleet of request routers. When a router encounters traffic for a table it has not recently seen, it queries a centralized configuration service to look up tenant policies, routing keys, and storage node partition layouts. As sustained high throughput routes through the fleet, the routing nodes cache partition metadata locally, eliminating configuration lookups and accelerating response execution.
```mermaid flowchart TD accTitle: Request Router Caching and Burst Handling accDescr: Flowchart displaying how request routers cache partition metadata and how the burst bucket handles unexpected traffic spikes.
subgraph RouterFleet["Request Router Mechanics"] Req["Incoming High-Volume Requests"] --> Router["Request Router Node"] Router -->|"Cold State: Fetch Metadata"| ConfigService["Config Service"] Router -->|"Warm State: Cache Hit"| StorageNodes["DynamoDB Storage Nodes"] end
subgraph ElasticBurst["Elastic Throughput Handling"] Normal["Provisioned Baseline: e.g., 6M WCU"] --> Spike["Traffic Spike: e.g., 10M WCU"] Spike -->|"Absorbed by"| BurstBucket["5-Minute Burst Bucket"] BurstBucket -->|"Prevents Throttling"| Success["Zero Throttles / Immediate Response"] end ```
DynamoDB also incorporates an elastic buffering mechanism called the burst bucket. Unused partition capacity over a rolling five-minute window accumulates into a reserve that can be tapped during sudden, unexpected traffic spikes without throttling. During a high-profile Super Bowl broadcast, a messaging service provider provisioned for approximately 6 million WCUs experienced a post-game surge that crested at over 10 million WCUs. Because unused capacity from preceding minutes was credited to their burst bucket, the table absorbed the 4-million WCU overrun without generating throttling errors or system degradation—an event the engineering team discovered only while reviewing CloudWatch graphs the following morning. Rick contrasts this resilience with self-hosted alternatives:
> “You have a five-minute burst bucket with DynamoDB, you can go to this database and you can say, ‘Hey look, I know you weren't busy five minutes ago, can you give me that now, please?’ And what happens with MongoDB when you do this? What happens with Cassandra? What happens with your relational database when you do this? Right, it goes offline, okay, that's what happens, it goes bye-bye.”
- **Metadata caching**: Request routers cache partition maps and security rules during active traffic, reducing network hops and latencies at scale. - **Burst bucket duration**: DynamoDB automatically banks unused provisioned throughput over a five-minute window to absorb unforeseen traffic spikes. - **Failure modes of traditional stores**: Surges that exceed host memory or connection pools on traditional relational or NoSQL servers lead to exhaustion, latency spikes, and system crashes.
---
[24:07](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1447s) Deconstructing the Myth of Non-Relational Data
Data modeling in software design is fundamentally rooted in relationships, regardless of whether the storage engine is relational or NoSQL. The misconception that NoSQL data is inherently "non-relational" led developers to treat documents as unstructured catch-alls, storing deeply nested JSON structures inside individual items. In production, these oversized documents create severe serialization and throughput bottlenecks. Every domain—whether enterprise IT management, social networks, document tracking, or simple key-value lookups (which represent explicit 1:1 associations)—is governed by relationships between distinct entities. Rick humorously dismisses the idea of data without relationships:
> “As a matter of fact, I would love to have a non-relational banking application, because I would log in over and over again until I saw the numbers that I liked, right, and then I would write my checks.”
A normalized product catalog demonstrates how these entity relationships are handled across database paradigms:
| Parent Entity | Related Child Entity | Relationship Cardinality | |:--- |:--- |:--- | | **Product** | Book, Album, or Video | One-to-One (1:1) | | **Album** | Track | One-to-Many (1:N) | | **Video** | Actor | Many-to-Many (M:N) |
In a relational database, assembling a complete view of a video and its acting cast requires CPU-intensive ad-hoc joins across multiple normalized tables. In DynamoDB, rather than performing dynamic joins at read time or storing unpredictable, un-indexable document blobs, developers pre-structure these relationships directly into the key schema, pre-joining hierarchical data to satisfy exact read patterns at any operational scale.
[26:08](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1568s) Relational Join Complexity vs. NoSQL Pre-Joined Partitions
In relational systems, assembling related data across multiple tables comes at a steep computational cost. Even for a simple one-to-one join with appropriate indexes and efficient execution paths like nested loop joins—avoiding expensive hash joins or merge joins—the time complexity scales with the size of the datasets ($O(\log N)$, $O(N \log M)$, or worse). As an application adds more tables to a query, that complexity climbs relentlessly with no architectural way to prevent the degradation.
NoSQL engines, by contrast, are fundamentally distributed hash tables designed around $O(1)$ time complexity. When a relational database encounters a worst-case scenario with massive datasets, its query optimizer typically resorts to a hash join: scanning a large table, constructing an in-memory hash table on the join dimension, and iterating through the input set to probe the hash index. As the speaker highlights:
> “Well this is why not do that first because obviously the database believes that a hash table is the actual you know the most efficient database data structure to query when I need to do repeated query so let's go ahead and do that and that's really what we're doing here we're creating partitions these partitions have the rows or the items from our relational structures and as we query through this in the particular case here I'm querying for a book it's a time constant if I query for the album I query by the album title it's again time constant know sort condition so I'm just going to the databases give me everything with this partition key I give me everything with the movie partition key.”
Pre-joining items within single DynamoDB partitions eliminates join-time overhead, allowing single key lookups to return rich collections of related records in constant time.
```mermaid flowchart LR accTitle: Relational Hash Join vs DynamoDB Pre-Joined Partitions accDescr: Comparison showing how relational joins build temporary hash tables at runtime while DynamoDB pre-materializes relational rows directly into constant-time partitions.
subgraph Relational["Relational Database Join"] direction TB R1["Table Scan Input"] --> R2["Build Runtime Hash Table"] R2 --> R3["Iterate and Probe Hash Keys"] end
subgraph DynamoDB["DynamoDB Storage Model"] direction TB D1["Partition Key: Entity ID"] D1 --> D2["Item 1: Root Metadata"] D1 --> D3["Item 2: Associated Record"] D1 --> D4["Item 3: Associated Record"] end ```
[28:18](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1698s) Modeling Graph Relationships and Many-to-Many Mappings with GSIs
When modeling related entities like movies and actors, a DynamoDB partition acts as a node within a graph. In a movie partition, the primary item describes the film itself (sorted under the director's name), while subsequent items represent relationships to actors, sorted by actor name. This structure represents a directed graph: the movie partition explicitly knows its cast members, but the actors do not inherently reference the movie within their own primary items.
To optimize reads, denormalized metadata can be projected directly onto the connecting edge items. Static attributes such as an actor's name, gender, role, or date of birth are safe to duplicate because they rarely or never change—the speaker jokes that he wishes he could change his own birth date, but it simply is not happening. Conversely, mutable content like an actor's full biography should remain on the primary actor record to avoid cascading updates across every role item. By inverting the primary key attributes on a Global Secondary Index (GSI)—making the sort key the partition key and the partition key the sort key—the directed graph becomes an undirected graph.
> “This is now an undirected graph both sides understand what they're related to I'll query the table for one side I'll query the GSI for the other right this is how we model many to many relationships in no SQL and avoid massive duplication of data right the only data where I'm duplicating is denormalized across those edges and I can decide what I'm going to project onto those edges oftentimes those edges need the extended metadata describes what is the relationship what is the role what are the permissions what are the things about this association to this object that this person has or that this other object has in my system.”
- Inverting table keys on a GSI allows access patterns in both directions: querying a movie yields all cast members; querying an actor on the GSI yields all movies they appeared in. - The inverted index pattern supports querying books by author, albums containing a given song, movies directed by a specific director, or records produced by a musician. - The base table serves one set of core access patterns, while secondary indexes absorb complementary dimensions.
[31:07](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1867s) Document Databases vs. Wide-Column Stores
Industry discussions frequently assert that document databases and wide-column databases serve fundamentally different modeling paradigms. At the core data-modeling level, that distinction is largely an illusion: DynamoDB payloads and query results are simply JSON structures. Hierarchical trees and flat row schemas map interchangeably between the two systems. In DynamoDB, unindexed attributes can be packed into a single nested JSON attribute. Because DynamoDB's underlying storage engine incurs overhead per distinct top-level attribute, storing unindexed data in a single top-level JSON blob optimizes both write efficiency and query speed. All attributes that require indexing can then be projected as discrete top-level attributes at the item root.
The true operational difference between document stores (such as MongoDB or Amazon DocumentDB) and wide-column engines (such as DynamoDB) lies in query planning and indexing mechanics. In document databases, items feature a default `_id` field. While systems like MongoDB generate a default UUID when `_id` is omitted, the speaker warns that this creates a useless data index: real human beings do not telephone customer support announcing themselves by a UUID, but by meaningful natural keys like an email address.
Furthermore, document engines rely on dynamic query planners that evaluate indexes or risk falling back to collection scans. The speaker notes his frequent use of MongoDB's `$hint` directive precisely to force the planner to use intended indexes. When profiling queries via `explain`, two metrics indicate performance: `nReturned` (documents returned) and `totalDocsExamined` / `docsExamined` (documents scanned). If scanned documents equal zero while returned documents exceed zero, the index fully covered the query. If documents scanned matches documents returned, the engine had to step back into the collection to fetch unindexed fields. Projecting extended fields in a document database requires expanding the index definition itself, directly driving up insert and update overhead.
- DynamoDB indexes top-level attributes; deeply nested JSON paths within attributes cannot be indexed directly. - Document-level query planners require explicit covering indexes to avoid storage lookups, whereas DynamoDB provisions projected GSI tables explicitly with granular projections without altering primary item schemas.
[34:53](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2093s) Indexing and Routing: Document Databases vs. DynamoDB
When querying document databases, defining indexes requires explicit awareness of data distribution. You must make sure that the shard key is included as the very first attribute of your compound index because the system uses that shard key to determine where to route the query. Furthermore, developers must remember to supply a shard key condition in the query itself; failing to do so causes the query to be fanned out across every single node in the cluster, resulting in wasted bandwidth and mostly empty responses.
DynamoDB bypasses this operational pitfall by architectural design. Because you tailor Global Secondary Indexes (GSIs) to exact access patterns and query them using an equality condition on the partition key, the request is routed directly to the appropriate storage node. Rather than relying on compound indexes to achieve query isolation, DynamoDB uses projections to preload indexed attributes, ensuring that write complexity does not increase CPU utilization even if larger projected payloads consume more write capacity units (WCUs). Regardless of whether data is stored as nested documents or wide-column structures, the underlying modeling discipline is identical. As Rick Houlihan puts it:
> “So anyone who tells you that there's a difference between these systems is wrong there's just not okay.”
- In document databases, omitting the shard key from compound indexes forces expensive scatter-gather queries across every storage node. - DynamoDB enforces partition key equality on queries, automatically isolating access to the targeted partition without risking cross-node broadcast overhead. - Preloading data through GSI attribute projections incurs predictable WCU consumption without driving up CPU complexity during index updates.
---
[36:28](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2188s) Aggregations and CDC Pipelines with DynamoDB Streams
DynamoDB does not natively execute complex analytical operations like top-N, last-N, averages, counts, or sums through its core API. Supporting these high-level key performance indicators (KPIs) requires a change data capture (CDC) processing architecture built on DynamoDB Streams. A DynamoDB Stream provides an ordered, running change log of all item modifications and write operations. By attaching AWS Lambda triggers to the stream, applications can asynchronously calculate running summaries, maintain materialized rollups, or stream updates to specialized search engines like Elasticsearch for full-text, geospatial, or index-intersection searches. This approach mirrors Amazon's early strategies for scaling relational databases, where offloading expensive aggregate calculations into pre-computed summary tables revealed that wide-column NoSQL patterns were the natural long-term solution. Relying on database-level aggregation engines (such as MongoDB’s aggregation framework) routinely breaks down at scale, requiring eventual data remodeling and asynchronous CDC pipelines.
```mermaid flowchart LR accTitle: DynamoDB Stream CDC Architecture accDescr: Pipeline showing data updates moving from a DynamoDB table to Streams, Lambda, and downstream analytics or search engines. subgraph Ingestion["Ingestion Layer"] T["DynamoDB Table"] -->|"Change Log"| S["DynamoDB Stream"] end subgraph Processing["Compute Layer"] S -->|"Trigger (At-least-once)"| L["AWS Lambda (Idempotent)"] end subgraph Targets["Downstream Targets"] L -->|"Update Rollups"| T L -->|"Specialized Search"| ES["Elasticsearch"] L -->|"Stream"| FH["Kinesis Data Firehose"] FH -->|"Parquet Files"| S3["Amazon S3"] S3 -->|"Ad-hoc SQL"| AT["Amazon Athena"] end ```
Because Lambda invocation from a stream operates under an at-least-once delivery contract, functions must be written to be strictly idempotent to guard against transient container restarts or mid-execution failures. Beyond operational summaries, these streams can push events through Amazon Kinesis Data Firehose to compress data into Parquet files on Amazon S3, allowing teams to run ad-hoc analytics via Amazon Athena without touching operational capacity. Comparing this model to traditional database features, Rick jokes about the safety and billing aspects of serverless offloading:
> “It's like the world's best stored procedure engine you're not going to knock the server sideways by running a bad stored procedure so you just get paid more I'm okay with that no no I'm not a lot a lot of my job is spent trying to get people to pay less trust me and I do I'm pretty good at it I think I'm pretty good at it all right.”
- **At-least-once processing:** Lambda stream triggers guarantee delivery, requiring idempotent business logic to prevent duplicate rollup calculations if a execution container fails. - **Analytics offloading:** Streaming into Kinesis Data Firehose, writing Parquet to S3, and querying via Amazon Athena enables real-time analytics with zero read capacity consumption on production tables. - **Search enhancements:** CDC streams keep external search clusters (such as Elasticsearch) in sync for capabilities DynamoDB does not support natively, including full-text search, geospatial queries, and index intersections.
---
[39:38](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2378s) Composite Keys and High-Density Partition Filtering
A common trap in NoSQL design is relying on DynamoDB filter expressions to narrow down items within a partition. For example, in a game sessions table partitioned by user ID and sorted by session date, an application might want to fetch all pending sessions sorted chronologically. If the query applies a filter expression for `status = 'pending'`, DynamoDB must read and charge for every session item in that date range before discarding non-matching records. If a partition holds three items and returns two, the read cost difference is negligible; however, if the partition contains 10,000 items and the query only needs two pending sessions, scanning and filtering the entire collection results in an expensive, inefficient read.
The solution is to merge multiple attributes into a composite sort key—such as concatenating status and date into a single attribute like `pending#2019-12-07`. The application can then execute a query using a `begins_with(pending)` sort key condition, fetching only the required items directly off the storage node. Dense partitions containing thousands of items are perfectly acceptable provided the throughput velocity remains within physical partition limits. Addressing misconceptions around partition sizing, Rick explains the true boundaries:
> “And I have a lot of people say oh we're not supposed to put lots of items in the partitions no we can put all the items we want in the partitions it's about the velocity how fast am I writing how fast am I reading.”
- **Composite sort key pattern:** Combining attributes (e.g., `status#date`) transforms table scans into selective range reads using operators like `begins_with()`. - **Partition limits:** A single logical DynamoDB partition accommodates up to 1,000 Write Capacity Units (WCUs, at 1 KB/s per unit) and 3,000 Read Capacity Units (RCUs, at 4 KB/s per unit for strongly consistent reads). - **Density vs. velocity:** Partition sizing is constrained by access rate (throughput velocity), not by the static quantity of items stored under a single partition key.
---
[41:33](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2493s) Relational Modeling and Evolution: The 23-Access-Pattern Employee Portal
Developers transitioning from relational systems often struggle to conceptualize applications without normalized tables, foreign key lookups, and multi-table joins. However, running multi-table traversals in application logic by iterating over result sets mimics the very relational join mechanisms that fail to scale. Emphasizing the need to avoid application-tier joins, Rick notes:
> “We don't want to use those types of data models because I mean if it's it's that hard for a relational database to do it how hard is it going to be for your app server?”
To prove that complex relational systems can run seamlessly in a single DynamoDB table, Rick introduces an enterprise employee portal model. While the previous year's presentation demonstrated a delivery tracking system with 12 access patterns, this employee portal scales across 23 distinct access patterns across multiple business dimensions. The schema grew organically: starting with a basic employee document entity, it evolved to support trouble tickets, project tracking, room reservations, and meeting schedules.
Instead of isolating entities into separate tables, the design organizes multiple entity types into distinct logical partitions within the same physical table: - **Building partitions:** Store physical room definitions alongside time-bound meeting room reservations. - **Employee partitions:** Hold employee core metadata together with invitations and assigned meeting schedules. - **Project partitions:** Track project assignments, tickets, and associated staff members.
By loading multiple related item types into shared and distinct partitions, the system services all 23 access patterns without application-level joins, relational normalization, or cross-table performance degradation.
[43:25](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2605s) Primary Table Access Patterns: Employees, Projects, Tickets, and Buildings
The base table is designed around multi-entity partitions containing timecards submitted by employees, employee roles on projects, project metadata, and ticketing data (including opening records, messages, owners, and assignees). To serve complex workloads, this single table directly supports nine core access patterns. First, querying employee meetings by employee ID uses the employee partition with a date-range condition and a filter condition checking for greater than zero. Because employee partitions generally contain a small number of items, relying on a less-than-selective read with a post-filter to drop non-meeting items is fully acceptable. Second, getting all meetings for a specific building during a time range queries the building partition with a date range. However, because the DynamoDB Query API does not support a `contains` operator directly on the sort key, the schema duplicates the sort key value into an ordinary attribute:
> “I don't know why but in DynamoDB API you can't use contains on a sort key so if you actually need that condition you have to create a copy of the sort key and use contains on that as a filter condition which is what we'll do...”
This duplicated attribute holds the concatenated building, floor, or room identifier so the client can filter across those specific dimensions. Third, retrieving employee metadata by employee ID is accomplished by querying the employee ID partition with a sort key condition where the key starts with `E`. Fourth, pulling ticket history queries the ticket partition directly to return the entire log of actions and messages for that ticket. Fifth, project metadata is retrieved by selecting on the project name with an exact match sort key. Sixth, querying the project partition with a date range pulls back all submitted employee timecards, which can be further refined with an optional filter condition on the role attribute (such as returning every technical product manager on the project during that timeframe).
- **Employee meetings query**: Partition key = `EmployeeID`, Sort key = date range, Filter condition = greater than zero. - **Building meetings query**: Partition key = `BuildingID`, Sort key = date range, Filter condition = `contains` on sort key copy (`Building#Floor#Room`). - **Employee metadata query**: Partition key = `EmployeeID`, Sort key `begins_with("E")`. - **Ticket history**: Partition key = `TicketID`, retrieves opening items and all subsequent messages. - **Project timecards**: Partition key = `ProjectName`, Sort key = date range, optional filter = `Role`.
---
[45:51](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2751s) Client-Side Computation: The Room Reservation Architecture
The seventh and eighth access patterns manage room and resource reservations—a scenario frequently encountered in internal Amazon systems. When scheduling resources, systems cannot immediately know which rooms are open without evaluating what exists versus what is booked. Rather than resolving this reservation logic inside a heavyweight application tier, the workload is split into two lightweight queries: first fetching room metadata for a building, and second fetching active reservations for the specified building and floor over the requested time window. The application pushes both result sets straight down to the client device, allowing the client to execute the differential calculation locally.
This architectural shift prevents application servers from spending compute cycles triaging and merging data. In practice, multiple enterprise customers have successfully shrunk the size of their application server fleets by offloading these evaluations to client hardware, where compute capacity is abundant. A similar principle applies to version control workflows, where the server streams down delta versions and lets the client assemble history rather than forcing multiple server-side roundtrips:
> “...what ends up happening at scale and we have several customers who have used this they've come back and said they've actually been able to decrease the size their application fleet server fleet because they're not triaging that data anymore the application server there's shoving it all down to the client let the client figure it out alright the clients have an abundance of CPU...”
```mermaid flowchart LR accTitle: Client-side reservation calculation pattern accDescr: The client fetches building room metadata and active reservations via lightweight queries, then resolves availability locally instead of using app servers. subgraph Storage ["DynamoDB Base Table"] Bld["Building Rooms Metadata"] Res["Active Reservations (Date Range)"] end subgraph Compute ["Application Server Fleet"] App["Stateless App Tier (Pass-Through)"] end subgraph Client ["Client Device (Plentiful CPU)"] Diff["Local Availability Diff Engine"] end
Bld -->|"1. Fetch rooms"| App Res -->|"2. Fetch reservations"| App App -->|"3. Send raw sets"| Diff ```
---
[47:26](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=2846s) GSI 1: Unified Portal Queries and Date Window Filtering
Secondary indexes in an overloaded DynamoDB design naturally hold fewer total items than the primary table because only a subset of entities require alternative access paths. Global Secondary Index 1 (GSI 1) covers nine additional access patterns, largely anchored around employee email. Meeting objects are projected into GSI 1 using the employee's email to satisfy lookups by email instead of ID. This index enables a unified corporate portal dashboard: when an employee logs in, the application needs the past 30 days of activity—meetings, timecards, tickets, and associated messages. By querying the employee partition on GSI 1 with a sort key condition of `date > (now - 30 days)`, the database returns the employee metadata and every relevant multi-type record in a single roundtrip.
> “One query, five different access patterns solved with one single query and this is what we're going to try and do with NoSQL databases we're going to try to create queries that support the retrieval of multiple types of objects when this is required group these objects into partitions on the table to support these joins right these are joins this is really what this is this is flat-out a join.”
GSI 1 also handles ticket history by employee email, employee metadata by email, and complex project delivery filtering. For an enterprise budgeting use case requiring all projects started within the last three months that will also complete within the next three months, the model couples a sort key condition with a filter condition. Querying active projects where start date is greater than three months ago fetches the candidate partition, while a filter condition checking that `target_delivery < (now + 3 months)` weeds out anything finishing further out. This non-selective read within the partition isolates projects safe from budget cuts while highlighting those on the chopping block.
- **Unified portal lookup**: Partition key = `EmployeeEmail`, Sort key > `30 days ago`; returns meetings, timecards, tickets, and employee metadata in one query. - **Multi-entity collection**: Acts as a pre-computed NoSQL join by grouping disparate item types under the same GSI partition key. - **Two-condition range simulation**: Sort condition = `StartDate >= -3 months`, Filter condition = `TargetDeliveryDate <= +3 months`.
---
[50:11](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=3011s) GSI 2 & GSI 3: Organizational Trees and Ticket Monitoring
As indexing needs narrow further down the schema, Global Secondary Index 2 (GSI 2) indexes items on secondary dimensions like assignee email and management hierarchy. To inspect workload by ticket assignee rather than ticket creator (owner email), ticket creation items and individual message items are decorated with assignee attributes before insertion, enabling full ticket history queries keyed by assignee. Similarly, employee metadata records are projected onto GSI 2 using the manager's employee ID as the partition key, allowing the retrieval of an entire reporting team with a single manager lookup.
Global Secondary Index 3 (GSI 3) handles deep structural aggregation and operational oversight. It implements subtree aggregation for hierarchical locations: by concatenating a location hierarchy path (e.g., city, building, floor) into a composite string attribute, queries using `begins_with` can retrieve all employees across an entire city, within a specific building, or on a single floor. Finally, GSI 3 supports system monitoring by targeting languishing tickets across the organization that have ongoing message activity but remain unclosed.
- **Ticket lookup by assignee (GSI 2)**: Query by assignee email over decorated ticket and message items to view assigned backlog and correspondence. - **Reporting structures (GSI 2)**: Query by `ManagerID` on projected employee metadata items to retrieve all direct reports. - **Subtree aggregation (GSI 3)**: Composite path hierarchy (e.g., `City#Building#Floor`) evaluated with `begins_with` to aggregate employees or inventory at any structural level. - **Languishing ticket detection (GSI 3)**: Identifies stagnant organizational tickets accumulating messages without reaching resolution.
[51:47](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=3107s) Write Sharding for Escalation and the Iterative Data Modeling Process
The speaker wraps up the ticketing workflow example by explaining how write sharding handles scale when identifying items that have not been touched within the last 24 hours for escalation. In the example shown, a ticket resides in partition 7, but the workload could be spread across 20 partitions to increase overall throughput depending on the volume of tickets being processed. Because DynamoDB provides 1,000 WCUs (Write Capacity Units) per partition, striping writes across 10 partitions allows an application to process up to 10,000 writes per second—a substantial workload, even when accounting for items that might exceed 1 KB. Ultimately, this approach yields an access-pattern-driven design summarized by a clean reference table listing each access pattern, required query conditions, target table or Global Secondary Index (GSI), and any necessary filter conditions.
```mermaid flowchart LR accTitle: Ticket Escalation Sharding accDescr: Tickets are distributed across N write-sharded partitions to achieve high aggregate write throughput. subgraph Ingestion["Ticket Ingestion"] T["Incoming Ticket Updates"] end subgraph ShardedPartitions["Write-Sharded Partitions"] P1["Partition 0 (up to 1,000 WCU)"] P7["Partition 7 (up to 1,000 WCU)"] PN["Partition N (up to 1,000 WCU)"] end T --> P1 T --> P7 T --> PN ```
This discipline mirrors the exact exercise the speaker conducts with every development team: begin with an Entity-Relationship (ER) diagram to understand the data, define the required access patterns, shape the data model to support those patterns, and establish unambiguous query conditions. The speaker emphasizes that this modeling journey is fundamentally iterative, starting with one or two initial access patterns and expanding gradually as requirements mature. When working with existing production data, teams rarely need to start from scratch; they can perform an ETL on an existing table, decorate items with new attributes, or adjust attribute values. As the speaker reassures the audience:
> “The reality is you're not going to have to throw the baby out with the bathwater right this is an evolution and it's not dissimilar to what you go through with the relational database it's just we're much more familiar with that.”
- Partition throughput scaling: 1,000 WCUs available per partition; scaling across 10 partitions yields 10,000 WCUs/sec. - Iterative design methodology: ER diagram $\rightarrow$ access patterns $\rightarrow$ storage model $\rightarrow$ concrete query/filter conditions. - Schema evolution: Existing tables can be updated via ETL by decorating items with new attributes rather than tearing down the entire model.
---
[53:19](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=3199s) Optimizing Quote Versioning: From 1,000 WCUs to 50 WCUs
Shifting to common architectural design patterns, the speaker addresses the pitfalls of working with oversized items through an engagement with an online insurance quoting service. The customer was processing around 800 quotes per minute at peak and had provisioned 1,000 WCUs on their DynamoDB table. Under their original design, every time an end user adjusted quote parameters (such as driving mileage or residential address), the system generated a full new quote version under a composite sort key formatted as `quoteId_version`. Retrieving the latest quote required a reverse scan (`ScanIndexForward = false` with `Limit = 1`), and whenever users stepped backward through quote history, the client application repeatedly hit the database to retrieve full past versions.
```mermaid flowchart TD accTitle: Quote Versioning Optimization Architecture accDescr: Replacing full quote copies with deltas and client-side assembly reduces table WCU from 1,000 to 50. subgraph OriginalPattern["Original Pattern (1,000 WCUs)"] O1["Update Quote Parameter"] --> O2["Write Full 50 KB Document"] O2 --> O3["Database: customer_id / quoteId_version"] end subgraph DeltaPattern["Optimized Pattern (50 WCUs)"] D1["Update Quote Parameter"] --> D2["Write Small Delta Object"] D2 --> D3["Client Browser"] D3 -->|"Assembles view locally"| D4["Zero API calls on back/forward pagination"] end ```
Upon analyzing the workload, the speaker discovered that nearly 90% of the quote payload consisted of immutable or rarely changing fields (such as customer name, address, and telephone numbers). By rewriting the entire 50 KB document for every revision, the system was wasting massive amounts of write capacity. The speaker recommended altering the schema so that the partition stored only individual delta updates alongside a base quote. When an end user queries a quote, the service transmits the collection of version delta objects downstream, delegating the assembly of the current view directly to the client browser. This change allowed the customer to slash provisioned capacity from 1,000 WCUs down to 50 WCUs, relying on DynamoDB's burst capacity to absorb occasional bursts of concurrent new quote creations. This architecture also eliminated repetitive API calls to the application servers when users navigated back and forth through quote history, leading the speaker to emphasize the holistic power of data modeling:
> “This is a really good example of how I can optimize the data model by thinking about how I'm implementing the system and how it affects the entire stack all right we have a lot of customers that do things like this to come back and say you know I was able to decrease my application server fleet by 5% because I'm no longer doing I'm not handling that data in the application server anymore I'm just passing it off to the front end and letting them do that triage.”
- Workload profile: ~800 quotes per minute at peak. - Previous capacity: 1,000 provisioned WCUs writing full 50 KB quote objects for minor edits. - Optimized capacity: 50 provisioned WCUs storing version deltas within the partition. - Burst capacity handling: Occasional bursts of 4–5 simultaneous new quote creations safely absorb spikes above the 50 WCU baseline. - Full-stack performance gain: Zero API round-trips when users navigate back through prior quote versions in the front end, accompanied by an estimated 5% reduction in application server fleet overhead.
---
[56:30](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=3390s) NoSQL Workbench for DynamoDB
The speaker highlights NoSQL Workbench for DynamoDB, an official visual design tool released a few months prior to the conference, characterizing it as the premier tool for modeling NoSQL databases. The tool allows developers to generate the aggregate multi-entity table views featured throughout the presentation. Users can define sample JSON records, import them directly into the modeler, specify secondary indexes across various attributes, and visually inspect how sample data partitions and aligns across the table and global indexes.
In addition to visual schema design, NoSQL Workbench includes features to load schemas from existing relational databases and an integrated code generator to bootstrap application development. The speaker observes that an increasing number of customers now bring fully realized NoSQL Workbench models to design review sessions, making collaborative schema tuning significantly faster.
- Visual modeling: Maps and renders aggregate single-table design views and index projections. - Sample data layout: Ingests raw JSON data to visualize partition and sort key layouts before deployment. - Relational integration: Connects to existing relational database instances to import table definitions. - Code generation: Generates application code scaffolding based on defined entities and queries.
---
[57:32](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=3452s) Conclusions: Deconstructing the "Non-Relational" Myth and Closing
In his closing reflections, the speaker pushes back firmly against industry terminology, urging attendees never to describe NoSQL data as "non-relational." Data by its very nature has relationships; the term "non-relational" arose purely as a marketing misnomer coined by people trying to describe technologies they did not understand. Entity-Relationship modeling remains as vital to NoSQL system design as it is to traditional databases. NoSQL does not deprecate the relational database; instead, engineers should pair the right tool with the right workload—running OLTP and large-scale decision support workloads on NoSQL, while reserving relational database engines for complex OLAP analytics. Above all, systems should be designed cloud-native first to capitalize on the elasticity of AWS compute and the unmatched scale of DynamoDB. As the speaker succinctly puts it:
> “Data is all relational or doesn't matter the ERD still matters yeah a relational database is not deprecated by NoSQL you know use NoSQL for OLTP or decisions support systems at scale use that relational database for OLAP but I always think cloud native first I'll tell you the elasticity of the the AWS compute cloud is amazing and of cloud native services like DynamoDB is is unparalleled in scale so I would go there first.”
The session concludes with pointers to training resources, including AWS Training and Certification digital courses and a recently released DynamoDB data modeling course from Linux Academy. The speaker thanks the audience and finishes with a lighthearted plea regarding the session feedback evaluations:
> “So by the way if you didn't like the session then it's DAT450 if you like it it's 403 please rate me.”
- Terminology rule: Avoid the term "non-relational"; relationships exist across all real-world data, making the ER diagram indispensable. - Architectural division of labor: NoSQL for high-scale OLTP; relational databases for OLAP workloads. - Learning resources: AWS Training and Certification platform and Linux Academy's DynamoDB training course. - Session ratings joke: Rate session DAT403 if you enjoyed it, or DAT450 if you did not.
## Notable quotes > [20:53](https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1253s) You are provisioning for peak, you are hoping you are consuming a high percentage of that capacity that you have provisioned, but the reality is the average data center utilization in the enterprise is 10 percent. — Rick Houlihan
## Mentioned Amazon Web Services (AWS), Amazon DynamoDB, Rick Houlihan, Oracle, MongoDB, Apache Cassandra, Herman Hollerith, US Census Bureau, Amazon Customer Delivery Organizations (CDO), Amazon CloudWatch, Amazon DocumentDB, AWS Lambda, DynamoDB Streams, Amazon Kinesis Data Firehose, Amazon S3, Amazon Athena, Elasticsearch, NoSQL Workbench for DynamoDB, AWS Training and Certification, Linux Academy
## Video description This technical session is for advanced users of Amazon DynamoDB. The patterns and data models discussed in this presentation summarize a collection of implementations and best practices used by large customers—including Amazon retail businesses—to deliver highly scalable solutions for a wide variety of business problems. We delve into strategies for global secondary index sharding and index overloading, scalable graph processing with materialized queries, relational modeling with composite keys, executing transactional workflows on DynamoDB, and more.
[Source](https://www.youtube.com/watch?v=6yqfmXiZTlM)