<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title> Aws on Hemant Sethi</title>
    <link>https://www.sethihemant.com/tags/-aws/</link>
    <description>Recent content in  Aws on Hemant Sethi</description>
    <generator>Hugo -- 0.146.0</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 28 Jan 2026 21:24:47 -0800</lastBuildDate>
    <atom:link href="https://www.sethihemant.com/tags/-aws/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>DynamoDB</title>
      <link>https://www.sethihemant.com/notes/dynamodb-2022/</link>
      <pubDate>Wed, 11 Dec 2024 00:00:00 +0000</pubDate>
      <guid>https://www.sethihemant.com/notes/dynamodb-2022/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href=&#34;https://www.usenix.org/conference/atc22/presentation/elhemali&#34;&gt;DynamoDB&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;dynamodb&#34;&gt;DynamoDB&lt;/h2&gt;
&lt;h3 id=&#34;summaryabstract&#34;&gt;Summary/Abstract&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Amazon DynamoDB is a NoSQL cloud database service that provides consistent performance at any scale.&lt;/li&gt;
&lt;li&gt;Fundamental properties: &lt;strong&gt;consistent performance&lt;/strong&gt;, &lt;strong&gt;availability&lt;/strong&gt;, &lt;strong&gt;durability&lt;/strong&gt;, and a &lt;strong&gt;fully managed serverless&lt;/strong&gt; experience.&lt;/li&gt;
&lt;li&gt;In 2021, during the 66-hour Amazon Prime Day shopping event, &lt;strong&gt;89.2 million requests per second&lt;/strong&gt;, while experiencing high availability with &lt;strong&gt;single-digit millisecond&lt;/strong&gt; performance.&lt;/li&gt;
&lt;li&gt;Design and implementation of &lt;strong&gt;DynamoDB&lt;/strong&gt; have evolved since the first launch in 2012. The system has successfully dealt with issues related to &lt;strong&gt;fairness&lt;/strong&gt;, &lt;strong&gt;traffic imbalance across partitions&lt;/strong&gt;, &lt;strong&gt;monitoring&lt;/strong&gt;, and &lt;strong&gt;automated system operations&lt;/strong&gt; without impacting availability or performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;goal of the design&lt;/strong&gt; of DynamoDB is to complete all requests with &lt;strong&gt;low single-digit millisecond&lt;/strong&gt; latencies.&lt;/li&gt;
&lt;li&gt;DynamoDB uniquely integrates the following six fundamental system properties:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB is a fully managed cloud service.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB employs a multi-tenant architecture.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB provides predictable performance&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB is highly available.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB supports flexible use cases.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;DynamoDB evolved as a distributed database service to meet the needs of its customers without losing its key aspect of providing a single-tenant experience to every customer using a multi-tenant architecture.&lt;/li&gt;
&lt;li&gt;The paper explains the challenges faced by the system and how the service evolved to handle those challenges while &lt;strong&gt;connecting&lt;/strong&gt; the required changes to a common theme of durability, availability, scalability, and predictable performance.
&lt;img loading=&#34;lazy&#34; src=&#34;https://www.sethihemant.com/images/notes/dynamodb-2022/image-1.png&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;history&#34;&gt;History&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Design of DynamoDB was motivated by our experiences with its predecessor &lt;strong&gt;Dynamo. Dynamo&lt;/strong&gt; was created in response to the need for a highly scalable, available, and durable key-value database for shopping cart data&lt;/li&gt;
&lt;li&gt;Amazon learned that providing applications with direct access to traditional enterprise database instances led to scal- ing bottlenecks such as connection management, interference between concurrent workloads, and operational problems with tasks such as schema upgrades.&lt;/li&gt;
&lt;li&gt;Service Oriented Architecture was adopted to encapsulate an application’s data behind service-level APIs that allowed sufficient decoupling to address tasks like reconfiguration without having to disrupt clients.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt; took the principles from &lt;strong&gt;Dynamo&lt;/strong&gt;(which was being run as Self-hosted DB but created operational burden for developers) &amp;amp; &lt;strong&gt;Simple DB&lt;/strong&gt;, a fully managed elastic NoSQL database service, but the data model couldn’t scale to the demands of the large Tables which DDB needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamo Limitations:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SimpleDB limitations&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;Amazon concluded that a better solution would combine the best parts of the original Dynamo design (incremental scalability and predictable high performance) with the best parts of SimpleDB (ease of administration of a cloud service, consistency, and a table-based data model that is richer than a pure key-value store)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;architecture&#34;&gt;Architecture&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A DynamoDB table is a collection of items.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>Paper:</strong> <a href="https://www.usenix.org/conference/atc22/presentation/elhemali">DynamoDB</a></p>
<hr>
<h2 id="dynamodb">DynamoDB</h2>
<h3 id="summaryabstract">Summary/Abstract</h3>
<ul>
<li>Amazon DynamoDB is a NoSQL cloud database service that provides consistent performance at any scale.</li>
<li>Fundamental properties: <strong>consistent performance</strong>, <strong>availability</strong>, <strong>durability</strong>, and a <strong>fully managed serverless</strong> experience.</li>
<li>In 2021, during the 66-hour Amazon Prime Day shopping event, <strong>89.2 million requests per second</strong>, while experiencing high availability with <strong>single-digit millisecond</strong> performance.</li>
<li>Design and implementation of <strong>DynamoDB</strong> have evolved since the first launch in 2012. The system has successfully dealt with issues related to <strong>fairness</strong>, <strong>traffic imbalance across partitions</strong>, <strong>monitoring</strong>, and <strong>automated system operations</strong> without impacting availability or performance.</li>
</ul>
<h3 id="introduction">Introduction</h3>
<ul>
<li>The <strong>goal of the design</strong> of DynamoDB is to complete all requests with <strong>low single-digit millisecond</strong> latencies.</li>
<li>DynamoDB uniquely integrates the following six fundamental system properties:</li>
<li><strong>DynamoDB is a fully managed cloud service.</strong></li>
<li><strong>DynamoDB employs a multi-tenant architecture.</strong></li>
<li><strong>DynamoDB provides predictable performance</strong></li>
<li><strong>DynamoDB is highly available.</strong></li>
<li><strong>DynamoDB supports flexible use cases.</strong></li>
<li>DynamoDB evolved as a distributed database service to meet the needs of its customers without losing its key aspect of providing a single-tenant experience to every customer using a multi-tenant architecture.</li>
<li>The paper explains the challenges faced by the system and how the service evolved to handle those challenges while <strong>connecting</strong> the required changes to a common theme of durability, availability, scalability, and predictable performance.
<img loading="lazy" src="/images/notes/dynamodb-2022/image-1.png"></li>
</ul>
<h3 id="history">History</h3>
<ul>
<li>Design of DynamoDB was motivated by our experiences with its predecessor <strong>Dynamo. Dynamo</strong> was created in response to the need for a highly scalable, available, and durable key-value database for shopping cart data</li>
<li>Amazon learned that providing applications with direct access to traditional enterprise database instances led to scal- ing bottlenecks such as connection management, interference between concurrent workloads, and operational problems with tasks such as schema upgrades.</li>
<li>Service Oriented Architecture was adopted to encapsulate an application’s data behind service-level APIs that allowed sufficient decoupling to address tasks like reconfiguration without having to disrupt clients.</li>
<li><strong>DynamoDB</strong> took the principles from <strong>Dynamo</strong>(which was being run as Self-hosted DB but created operational burden for developers) &amp; <strong>Simple DB</strong>, a fully managed elastic NoSQL database service, but the data model couldn’t scale to the demands of the large Tables which DDB needed.</li>
<li><strong>Dynamo Limitations:</strong></li>
<li><strong>SimpleDB limitations</strong>:</li>
<li>Amazon concluded that a better solution would combine the best parts of the original Dynamo design (incremental scalability and predictable high performance) with the best parts of SimpleDB (ease of administration of a cloud service, consistency, and a table-based data model that is richer than a pure key-value store)</li>
</ul>
<h3 id="architecture">Architecture</h3>
<ul>
<li>
<p>A DynamoDB table is a collection of items.</p>
</li>
<li>
<p>Each item is a collection of attributes. Uniquely identified by a primary key.</p>
</li>
<li>
<p>Schema of the primary key is specified at the table creation time.</p>
</li>
<li>
<p>The partition key’s value is always used as an input to an internal hash function.</p>
</li>
<li>
<p>The output from the hash function and the sort key value (if present) determines where the item will be stored.</p>
</li>
<li>
<p>Multiple items can have the same partition key value in a table with a composite primary key. However, those items must have different sort key values.</p>
</li>
<li>
<p>Supports secondary indexes to provide enhanced querying capability, which allows querying the data in the table using an alternate key.</p>
</li>
<li>
<p>DynamoDB provides a simple interface to store or retrieve items from a table or an index.
<img loading="lazy" src="/images/notes/dynamodb-2022/image-2.png"></p>
</li>
<li>
<p>DynamoDB supports ACID transactions for multi-item updates w/o affecting scalability/availability/performance.</p>
</li>
<li>
<p>A DynamoDB table is divided into multiple partitions.</p>
</li>
<li>
<p>Each partition of the table hosts a disjoint and contiguous part of the table’s key-range and has multiple replicas(<strong>replication Group</strong>) distributed across different Availability Zones for high availability and durability.</p>
</li>
<li>
<p>The Replication Group uses Multi-Paxos for leader election and consensus.</p>
</li>
<li>
<p>Any replica can trigger a round of the election.</p>
</li>
<li>
<p>Once elected leader, a replica can maintain leadership as long as it periodically renews its leadership lease.</p>
</li>
<li>
<p>Only the leader replica can serve <strong>write</strong> and <strong>strongly consistent read</strong> requests.</p>
</li>
<li>
<p>Leader generates a write-ahead log record and sends it to its peers.</p>
</li>
<li>
<p><strong>Write</strong> is acknowledged to the application once a quorum of peers persists the log record to their local write-ahead logs.</p>
</li>
<li>
<p>DynamoDB supports <strong>strong(Leader Read)</strong> and <strong>eventually consistent(Replica read)</strong> reads.</p>
</li>
<li>
<p>The leader of the group extends its leadership using a lease mechanism.</p>
</li>
<li>
<p>If the leader of the group is detected as a failure (considered unhealthy or unavailable) by any of its peers, the peer can propose a new round of the election to elect itself as the new leader. The new leader won’t serve any writes or consistent reads until the previous leader’s lease expires.</p>
</li>
<li>
<p>Partitioning/Replication Group
<img loading="lazy" src="/images/notes/dynamodb-2022/image-3.png"></p>
</li>
<li>
<p><strong>Log Replica/Node</strong> - Write Ahead Log(replicated) for High Availability and Durability.
<img loading="lazy" src="/images/notes/dynamodb-2022/image-4.png"></p>
</li>
<li>
<p>Multi-Paxos Leader Election and Consensus.</p>
</li>
<li>
<p>Writes and Strongly/Eventually Consistent Reads</p>
</li>
<li>
<p>Microservice architecture
<img loading="lazy" src="/images/notes/dynamodb-2022/image-5.png"></p>
</li>
<li>
<p><strong>Metadata Service</strong></p>
</li>
<li>
<p><strong>Request Router Service</strong></p>
</li>
<li>
<p><strong>Auto-Admin Service(Central Nervous System of DDB)</strong></p>
</li>
<li>
<p><strong>Storage Service</strong></p>
</li>
<li>
<p><strong>Features supported by other Services</strong></p>
</li>
</ul>
<h3 id="journey-from-provisioned-to-on-demand">Journey from Provisioned to On-Demand</h3>
<ul>
<li>
<p>DDB was launched with <strong>Partitions</strong> as an internal abstraction, as a way to dynamically scale both the <strong>capacity</strong> and <strong>performance</strong> of tables.</p>
</li>
<li>
<p>Customers explicitly specified the throughput that a table required in terms of read capacity units (RCUs) and write capacity units (WCUs). RCUs and WCUs collectively are called <strong>provisioned</strong> throughput.</p>
</li>
<li>
<p>As the demands from a table changed (because it grew in size or because the load increased), partitions could be further split and migrated to allow the table to scale elastically. <strong>Partition</strong> abstraction proved to be really valuable and continues to be central to the design of DynamoDB.</p>
</li>
<li>
<p><strong>[Challenge] This early version tightly coupled the assignment of both capacity and performance to individual partitions, which led to challenges</strong></p>
</li>
<li>
<p>DynamoDB uses <strong>admission control</strong> to ensure that storage nodes don’t become overloaded, to avoid interference between co-resident table partitions, and to enforce the throughput limits requested by customers.</p>
</li>
<li>
<p><strong>Admission control</strong> was the shared responsibility of all storage nodes for a table. Storage nodes independently performed admission control based on the allocations of their locally stored partitions.</p>
</li>
<li>
<p>Allocated throughput of each partition was used to isolate the workloads. DynamoDB enforced a cap on the maximum throughput that could be allocated to a single partition. Total throughput of all the partitions hosted by a storage node is less than or equal to the maximum allowed throughput on the node as determined by the physical characteristics of its storage drives.</p>
</li>
<li>
<p>The throughput allocated to partitions was adjusted when the overall table’s throughput was changed or its partitions were split into child partitions.</p>
</li>
<li>
<p>When a partition was split for size, the allocated throughput of the parent partition was equally divided among the child partitions and was allocated based on the table’s provisioned throughput.</p>
</li>
<li>
<p>E.g. Assume that a partition can accommodate a maximum provisioned throughput of 1000 WCUs. When a table is created with 3200 WCUs, DynamoDB created four partitions that each would be allocated 800 WCUs. If the table’s provisioned throughput was increased to 3600 WCUs, then each partition’s capacity would increase to 900 WCUs. If the table’s provisioned throughput was increased to 6000 WCUs, then the partitions would be split to create eight child partitions, and each partition would be allocated 750 WCUs. If the table’s capacity was decreased to 5000 WCUs, then each partition’s capacity would be decreased to 675 WCUs</p>
</li>
<li>
<p>The uniform distribution of throughput across partitions is based on the assumptions that an application uniformly accesses keys in a table and the splitting a partition for size equally splits the performance.</p>
</li>
<li>
<p>However, it was discovered that <strong>application workloads frequently have non-uniform access patterns both over time and over key ranges</strong>.</p>
</li>
<li>
<p><strong>Hot Partition Worsening with Split</strong>: When the request rate within a table is non-uniform, splitting a partition and dividing performance allocation proportionately can result in the hot portion of the partition having less available performance than it did before the split.</p>
</li>
<li>
<p><strong>[Single Hot Partition]</strong> Since throughput was allocated statically and enforced at a partition level, these non-uniform workloads occasionally resulted in an application’s reads and writes being rejected, called throttling, even though the total provisioned throughput of the table was sufficient to meet its needs.
Common Challenges faced by the applications were:</p>
</li>
<li>
<p><strong>Hot Partition</strong></p>
</li>
<li>
<p><strong>Throughput Dilution.</strong></p>
</li>
<li>
<p>Customers would increase the provisioned throughput of the table(even if they were under the limit overall), which caused poor performance. It was difficult to estimate the correct provisioned throughput.</p>
</li>
<li>
<p><strong>Hot partitions</strong> and <strong>throughput dilution</strong> stemmed from tightly coupling a rigid performance allocation to each partition, and dividing that allocation as partitions split. <strong>Bursting</strong> and <strong>Adaptive Capacity</strong> to address these concerns.</p>
</li>
</ul>
<h3 id="improvements-to-admission-control">Improvements to Admission Control:</h3>
<h3 id="key-observations">Key Observations:</h3>
<ul>
<li>Partitions had non-uniform access/traffic.</li>
<li>Not all partitions hosted by a storage node used their allocated throughput simultaneously.</li>
</ul>
<h3 id="bursting">Bursting</h3>
<ul>
<li>The idea behind <strong>Bursting</strong> was to let applications tap into the unused capacity at a partition level on a best effort basis to absorb short-lived spikes.</li>
<li>DynamoDB retained a portion of a partition’s unused capacity for later bursts of throughput usage for up to 300 seconds and utilized it when consumed capacity exceeded the provisioned capacity of the partition.</li>
<li>DynamoDB still maintained <strong>workload isolation</strong> by ensuring that a partition could only burst if there was <strong>unused throughput at the node level</strong>. The capacity was managed on the storage node using <strong>multiple token buckets</strong> to provide admission control:</li>
<li>**[Partition Token + Node Token]**When a read or write request arrives on a storage node, if there were tokens in the partition’s allocated token bucket, then the request was admitted and tokens were deducted from the partition and node level bucket.</li>
<li><strong>[Burst Token + Node Token]</strong> Once a partition had exhausted all the provisioned tokens, requests were allowed to burst only when tokens were available both in the burst token bucket and the node level token bucket.</li>
<li>Read requests were accepted based on the local token buckets.</li>
<li><strong>[Replica node’s Token Bucket for Write]</strong> Write requests using burst capacity require an additional check on the node-level token bucket of other member replicas of the partition.</li>
<li>The leader replica of the partition periodically collected information about each of the members node-level capacity.</li>
</ul>
<h3 id="adaptive-capacity">Adaptive Capacity</h3>
<ul>
<li>DynamoDB launched adaptive capacity to better <strong>absorb long-lived spikes</strong> that cannot be absorbed by the burst capacity.</li>
<li>Better absorb work-loads that had heavily skewed access patterns across partitions.</li>
<li>Adaptive capacity actively monitored the provisioned and consumed capacity of all the tables.</li>
<li>If a table experienced throttling and the table level throughput was not exceeded, then it would automatically increase (boost) the allocated throughput of the partitions of the table using a <strong>proportional control algorithm.</strong></li>
<li>The <strong>autoadmin system</strong> ensured that partitions receiving boost were relocated to an appropriate node that had the capacity to serve the increased throughput, however like bursting, adaptive capacity was also <strong>best-effort</strong> but <strong>eliminated over 99.99%</strong> of the throttling due to skewed access pattern.</li>
</ul>
<h3 id="global-admission-control">Global Admission Control</h3>
<ul>
<li>Even though Bursting and Adaptive Capacity significantly reduced throughput problems for non-uniform access, they had <strong>limitations</strong>.</li>
<li>Takeaway from bursting and adaptive capacity was that we had <strong>tightly coupled partition level capacity</strong> to <strong>admission control</strong>.</li>
<li>Admission control was <strong>distributed and performed at a partition level.</strong></li>
<li>DynamoDB realized <strong>it would be beneficial to remove admission control from the partition</strong> and <strong>let the partition always burst while providing workload isolation</strong>.</li>
<li>DynamoDB replaced <strong>adaptive capacity</strong> with <strong>global admission control (GAC)</strong>.</li>
<li>GAC builds on the same idea of <strong>Token Bucket</strong>.</li>
<li>The GAC service <strong>centrally tracks the total consumption of the table</strong> capacity in terms of tokens.</li>
<li>Each request router maintains a <strong>local token bucket</strong> to make admission decisions and communicates with <strong>GAC to replenish tokens at regular intervals</strong> (in the order of a few seconds).</li>
<li><strong>[Important Design Consideration] Each GAC server can be stopped and restarted without any impact on the overall operation of the service</strong>.</li>
<li>Each GAC server can track one or more token buckets configured independently.</li>
<li>All the GAC servers are part of an independent hash ring.</li>
<li><strong>Request routers manage several time-limited tokens locally</strong>. When a request from the application arrives, the request router deducts tokens. Eventually, the request router will run out of tokens because of consumption or expiry. <strong>When the request router runs off of tokens, it requests more tokens from GAC.</strong></li>
<li>The GAC instance uses the information provided by the client to <strong>estimate the global token consumption and vends tokens available for the next time unit to the client’s share of overall tokens</strong>.</li>
<li>Thus, it ensures that non-uniform workloads that send traffic to only a subset of items can execute up to the maximum partition capacity.</li>
<li>In addition to the global admission control scheme, the partition-level token buckets were retained for defense in-depth. The capacity of these token buckets is then capped to ensure that one application doesn’t consume all or a significant share of the resources on the storage nodes.</li>
</ul>
<h3 id="balancing-consumed-capacity">Balancing Consumed Capacity</h3>
<ul>
<li>Letting partitions burst(always) required DynamoDB to manage burst capacity effectively.</li>
<li><strong>Colocation</strong> was a straightforward problem with provisioned throughput tables because of static partitions.</li>
</ul>
<h3 id="splitting-for-consumption">Splitting for Consumption</h3>
<ul>
<li><strong>[Problem]</strong> Even with GAC and the ability for partitions to always burst, tables could experience throttling if their traffic was skewed to a specific set of items.</li>
<li><strong>[Solution]</strong></li>
<li>DynamoDB automatically scales out partitions once the consumed throughput of a partition crosses a certain threshold.</li>
<li>The split point in the key range is chosen based on key distribution the partition has observed.</li>
<li>The observed key distribution serves as a proxy for the application’s access pattern and is more effective than splitting the key range in the middle.</li>
<li>Partition splits usually complete in the order of minutes.</li>
<li><strong>[Catch]</strong> Still class of workloads exist that cannot benefit from split for consumption. E.g. a partition receiving high traffic to a single item or a partition where the key range is accessed sequentially will not benefit from split. DDB avoids splitting the partition.</li>
</ul>
<h3 id="on-demand-provisioning">On Demand Provisioning</h3>
<ul>
<li><strong>[Context]</strong></li>
<li>Initially, applications migrated to DDB, were on self provisioned servers either on-prem or on self-hosted databases.</li>
<li>DynamoDB provides a simplified serverless operational model and a new model for provisioning - read and write capacity units.</li>
<li><strong>[Problem]</strong></li>
<li>The concept of capacity units was new to customers, some found it challenging to forecast the provisioned throughput.</li>
<li>Customers either over provisioned(Low utilization) or under provisioned(Throttling).</li>
<li><strong>[Solution]</strong> To improve the customer experience for spiky workloads, DDB launched <strong>On-Demand Tables</strong>.</li>
<li>DynamoDB provisions the on-demand tables <strong>based on the consumed capacity</strong> by <strong>collecting the signal of reads and writes</strong> and instantly accommodates <strong>up to double the previous peak traffic</strong> on the table.</li>
<li>On-demand scales a table by splitting partitions for consumption. The split decision algorithm is based on traffic.</li>
<li>GAC allows DynamoDB to monitor and protect the system from one application consuming all the resources.</li>
</ul>
<h3 id="durability-and-correctness">Durability and Correctness</h3>
<ul>
<li>Data loss can occur because of hardware failures, software bugs, or hardware bugs.</li>
<li>DynamoDB is designed for high durability by having mechanisms to prevent, detect, and correct any potential data losses.</li>
</ul>
<h3 id="hardware-failures">Hardware Failures</h3>
<ul>
<li><strong>Write-ahead logs(WAL)</strong> in DynamoDB are central for providing durability and crash recovery. Write ahead logs are stored in all three replicas of a partition.</li>
<li>For higher durability, the write ahead logs are periodically archived to S3, an object store that is designed for 11 nines(99.999999999) of durability.</li>
<li>The unarchived logs are typically a few hundred megabytes in size.</li>
<li>When a node fails, all replication groups hosted on the node are down to two copies.</li>
<li>The process of healing a storage replica can take several minutes because the repair process involves copying the <strong>B-tree</strong> and <strong>write-ahead logs</strong>.</li>
<li><strong>[Solution]</strong> Upon detecting an unhealthy storage replica, the leader of a replication group adds a <strong>log replica</strong> to ensure there is no impact on durability.</li>
<li>Adding a log replica takes only a few seconds because the system has to copy only the recent write-ahead logs from a healthy replica to the new replica without the B-tree. Quick healing of impacted replication groups using log replicas ensures high durability of most recent writes.</li>
</ul>
<h3 id="silent-data-errors">Silent Data Errors</h3>
<ul>
<li><strong>[Problem]</strong> Some hardware failures can cause incorrect data to be stored . These errors can happen because of the storage media, CPU, or memory.</li>
<li>It&rsquo;s very difficult to detect these and they can happen anywhere in the system.</li>
<li><strong>[Solution]</strong> DynamoDB makes extensive use of <strong>checksums</strong> to detect silent errors.</li>
<li>By maintaining checksums within every log entry, message, and log file, DynamoDB validates data integrity for every data transfer between two nodes.</li>
<li><strong>Checksums</strong> serve as guardrails to prevent errors from spreading to the rest of the system.</li>
<li>Every log file that is archived to S3 has a <strong>manifest</strong> that contains information about the log, such as a table, partition and start and end markers for the data stored in the log file.</li>
<li>The agent responsible for archiving log files to S3 performs various checks before uploading the data. These include and are not limited to verification of every log entry to ensure that it belongs to the correct table and partition, verification of checksums to detect any silent errors, and verification that the log file doesn’t have any holes in the sequence numbers.</li>
<li>Once all the checks are passed, the log file and its manifest are archived. Log archival agents run on all three replicas of the replication group. If one of the agents finds that a log file is already archived, the agent downloads the uploaded file to verify the integrity of the data by comparing it with its local write-ahead log.</li>
<li>Every log file and manifest file are uploaded to <strong>S3 with a content checksum</strong>. The <strong>content checksum is checked by S3 as part of the put operation</strong>, which guards against any <strong>errors during data transit to S3.</strong></li>
</ul>
<h3 id="continuous-verification">Continuous Verification</h3>
<ul>
<li>DynamoDB also continuously verifies data at rest. Our goal is to detect any silent data errors or bit rot in the system. An example of such a continuous verification system is the <strong>scrub process</strong>.</li>
<li>The goal of <strong>scrub</strong> is to detect errors that we had not anticipated, such as <strong>bit rot</strong>.</li>
<li>The <strong>scrub process</strong> runs and verifies two things:</li>
<li>The verification is done by computing the checksum of the live replica and matching that with a snapshot of one generated from the log entries archived in S3.</li>
<li><strong>Scrub</strong> acts as a defense in depth <strong>to detect divergences</strong> between the <strong>live storage replicas</strong> with the <strong>replicas built using the history of logs</strong> from the inception of the table.</li>
<li>A similar technique of continuous verification is used to verify replicas of <strong>global tables.</strong></li>
<li>We have learned that continuous verification of data-at-rest is the most reliable method of protecting against hardware failures, silent data corruption, and even software bugs.</li>
</ul>
<h3 id="software-bugs">Software Bugs</h3>
<ul>
<li><strong>[Problem]</strong> DDB is a complex Distributed Key Value store. High complexity increases the probability of human error in design, code, and operations. Errors in the system could cause loss or corruption of data, or violate other interface contracts that our customers depend on.</li>
<li><strong>[Solution]</strong> DDB uses formal methods extensively to ensure the correctness of our replication protocols. The core replication protocol was specified using TLA+.</li>
<li>When new features that affect the replication protocol are added, they are incorporated into the specification and model checked.</li>
<li>Model checking has allowed us to catch subtle bugs that could have led to durability and correctness issues before the code went into production. S3 also uses Model Checking.</li>
<li><strong>Extensive failure injection testing</strong> and <strong>stress testing</strong> to ensure the correctness of every piece of software deployed.</li>
<li>In addition to testing and verification of the replication protocol of the data plane, <strong>formal methods have also been used to verify the correctness of our control plane and features such as distributed transactions</strong>.</li>
</ul>
<h3 id="backups-and-restore">Backups and Restore</h3>
<ul>
<li>In addition to guarding against physical media corruption, DynamoDB also supports backup and restore <strong>to protect against any logical corruption</strong> <strong>due to a bug in a customer’s application</strong>. Backups or restores don’t affect performance or availability of the table as they are built using the write-ahead logs that are archived in S3.</li>
<li>The backups are consistent across multiple partitions up to the nearest second.</li>
<li>The backups are full copies of DynamoDB tables and are stored in an Amazon S3 bucket.</li>
<li>DynamoDB also supports <strong>point-in-time restore</strong> where customers can <strong>restore the contents of a table that existed at any time in the previous 35 days to a different DynamoDB table in the same region.</strong></li>
<li>For tables with the point-in-time restore enabled, DynamoDB creates <strong>periodic</strong>(<strong>based on the amount of write-ahead logs accumulated for the partition</strong>) snapshots of the partitions that belong to the table and uploads them to S3.</li>
<li>Snapshots, in conjunction to write-ahead logs, are used to do point-in-time restore.</li>
<li><strong>[Workflow]</strong> When a point-in-time restore is requested for a table,</li>
</ul>
<h3 id="availability">Availability</h3>
<ul>
<li>To achieve high availability, DynamoDB tables are distributed and replicated across multiple Availability Zones (AZ) in a Region. DynamoDB regularly tests resilience to node, rack, and AZ failures.</li>
<li>To test the availability and durability of the overall service, power-off tests are exercised. Using realistic simulated traffic, random nodes are powered off using a job scheduler. At the end of all the power-off tests, the test tools verify that the data stored in the database is logically valid and not corrupted.</li>
</ul>
<h3 id="write-and-consistent-read-availability">Write and Consistent Read Availability</h3>
<ul>
<li>A partition’s <strong>write availability</strong> depends on its ability to have a <strong>healthy leader</strong> and a <strong>healthy write quorum.</strong></li>
<li>A <strong>healthy write quorum</strong> in the case of DynamoDB consists of two out of the three replicas from different AZs.</li>
<li>A partition remains available as long as there are enough healthy replicas for a write quorum and a leader</li>
<li>A partition will become unavailable for writes if the number of replicas needed to achieve the minimum quorum are unavailable</li>
<li>The leader replica serves <strong>consistent reads</strong>.</li>
<li>Introducing <strong>log replicas</strong> was a big change to the system, and the formally proven implementation of Paxos provided us the confidence to safely tweak and experiment with the system to achieve higher availability</li>
<li>Eventually consistent reads can be served by any of the replicas.</li>
<li>In case a leader replica fails, other replicas detect its failure and elect a new leader to minimize disruptions to the availability of consistent reads.</li>
</ul>
<h3 id="failure-detection">Failure Detection</h3>
<ul>
<li><strong>[Problem]</strong> A newly elected leader will have to wait for the expiry of the old leader’s lease before serving any traffic. While this only takes a couple of seconds, the elected leader cannot accept any new writes or consistent read traffic during that period, thus disrupting availability.</li>
<li>Failure detection must be quick and robust to minimize disruptions. False positives in failure detection can lead to more disruptions in availability. Failure detection works well for failure scenarios where every replica of the group loses connection to the leader.</li>
<li>However, nodes can experience gray network failures(Gray Failure).</li>
<li>Gray network failures can happen because of communication issues between a leader and follower, issues with outbound or inbound communication of a node, or front-end routers facing communication issues with the leader even though the leader and followers can communicate with each other.</li>
<li>Gray failures can disrupt availability because there might be a false positive in failure detection or no failure detection</li>
<li>For example, a replica that isn’t receiving heartbeats from a leader will try to elect a new leader. This can disrupt availability.</li>
<li><strong>[Solution]</strong> To solve the availability problem caused by gray failures, a follower that wants to trigger a failover sends a message to other replicas in the replication group asking if they can communicate with the leader. If replicas respond with a healthy leader message, the follower drops its attempt to trigger a leader election. This change in the failure detection algorithm used by DynamoDB significantly minimized the number of false positives in the system, and hence the number of spurious leader elections.</li>
</ul>
<h3 id="measuring-availability">Measuring Availability</h3>
<ul>
<li>DynamoDB is designed for <strong>99.999(5-9s)</strong> percent availability for global tables and 99.99**(4-9s)** percent availability for regional tables.</li>
<li>To ensure these goals are being met, DynamoDB continuously monitors availability at service and table levels. The tracked availability data is used to analyze customer perceived availability trends and trigger alarms if customers see errors above a certain threshold. These alarms are called customer-facing alarms (CFA) to report any availability-related problems and proactively mitigate the problem either automatically or through operator intervention.</li>
<li>In addition to real time monitoring of availability, the system runs daily jobs that trigger aggregation to calculate aggregate availability metrics per customer.</li>
<li>DynamoDB also measures and alarms on availability observed on the client-side. There are <strong>two sets of clients</strong> used to measure the <strong>user-perceived availability</strong>.</li>
<li>Real application traffic allows us to reason about DynamoDB availability and latencies as seen by our customers and catch gray failures.</li>
</ul>
<h3 id="deployments">Deployments</h3>
<ul>
<li>Unlike a traditional relational database, <strong>DynamoDB takes care of deployments without the need for maintenance windows</strong> and without impacting the performance and availability that customers experience.</li>
<li>The rollback procedure is often missed in testing and can lead to customer impact. DynamoDB runs a suite of <strong>upgrade and downgrade tests</strong> at a component level before every deployment.</li>
<li><strong>[Problem]</strong> Deployments are not atomic in a distributed system. At any given time, there will be software running the old code on some nodes and new code on other parts of the fleet.</li>
<li>New software might introduce a new type of message or change the protocol in a way that old software in the system doesn’t understand.</li>
<li><strong>[Solution]</strong> DynamoDB handles these kinds of changes with <strong>read-write deployments</strong>. Read-write deployment is completed as a multi-step process.</li>
<li>The first step is to deploy the software to read the new message format or protocol. Once all the nodes can handle the new message, the software is updated to send new messages.</li>
<li><strong>Read-write</strong> deployments ensure that both types of messages can coexist in the system. Even in the case of rollbacks, the system can understand both old and new messages.</li>
<li><strong>[OneBox]</strong> Deployments are done on a small set of nodes before pushing them to the entire fleet of nodes. The strategy reduces the potential impact of faulty deployments.</li>
<li>[<strong>AutoRollback AlarmWatcher/ApprovalWorkflow</strong>] DynamoDB sets alarm thresholds on availability metrics. If error rates or latency exceed the threshold values during deployments, the system triggers automatic rollbacks.</li>
<li>**[Problem]**Software deployments to storage nodes trigger <strong>leader failovers</strong> that are designed to avoid any impact to availability.</li>
</ul>
<h3 id="dependencies-on-external-services">Dependencies on External Services</h3>
<ul>
<li>To ensure high availability, all the services that DynamoDB depends on in the request path should be more highly available than DynamoDB.</li>
<li>Alternatively, DynamoDB should be able to continue to operate even when the services on which it depends are impaired.</li>
<li>Examples of services DynamoDB depends on for the request path include AWS Identity and Access Management Services (IAM), and AWS Key Management Service (AWS KMS) for tables encrypted using customer keys. DynamoDB uses IAM and AWS KMS to authenticate every customer request.</li>
<li>While these services are highly available, DynamoDB is designed to operate when these services are unavailable without sacrificing any of the security properties that these systems provide.</li>
<li>In the case of IAM and AWS KMS, DynamoDB employs a <strong>statically stable design</strong>, where the overall system keeps working even when a dependency becomes impaired.</li>
<li>Perhaps the system doesn’t see any updated information that its dependency was supposed to have delivered. However, everything before the dependency became impaired continues to work despite the impaired dependency.</li>
<li>DynamoDB caches result from IAM and AWS KMS in the request routers that perform the authentication of every request. DynamoDB periodically refreshes the cached results asynchronously.</li>
<li>If AWS IAM or KMS were to become unavailable, the routers will continue to use the cached results for a predetermined extended period.</li>
<li>Caches improve response times by removing the need to do an off-box call, which is especially valuable when the system is under high load.</li>
</ul>
<h3 id="metadata-availability">Metadata Availability</h3>
<ul>
<li>One of the most important pieces of metadata the request routers needs is the mapping between a table’s primary keys and storage nodes.</li>
<li>[<strong>Metadata Storage</strong>]At launch, DynamoDB stored the metadata in DynamoDB itself.</li>
<li>[<strong>Routing Schema</strong>] This routing information consists of all the partitions for a table, the key range of each partition, and the storage nodes hosting the partition.</li>
<li>[<strong>Router Metadata Caching</strong>] When a router received a request for a table it had not seen before, it downloaded the routing information for the entire table and cached it locally. Since the configuration information about partition replicas rarely changes, the <strong>cache hit rate</strong> was approximately <strong>99.75 percent</strong>.</li>
</ul>
<h3></h3>
<h3 id="dynamodb-limits">DynamoDB Limits</h3>
<ul>
<li>**Per Partition Read and Write Capacity Units - **<strong>Ref</strong></li>
<li><strong>1 MB limit</strong> on the size of data returned by a single Query, <strong>Scan</strong>/<strong>GetItem Op</strong>.</li>
<li><strong>BatchGetItem</strong> operation can return up to <strong>16MB</strong> of data - <strong>Ref</strong></li>
<li><strong>Item Size Limit</strong>: Ref</li>
<li>**Secondary Indexes - **<strong>Ref</strong></li>
<li><strong>Transactions:</strong></li>
</ul>
<h3 id="microbenchmarks">MicroBenchmarks</h3>
<ul>
<li>To show that scale doesn’t affect the latencies observed by applications, we ran YCSB [8] workloads of types A (50 percent reads and 50 percent updates) and B (95 percent reads and 5 percent updates)</li>
<li>Both benchmarks used a uniform key distribution and items of size 900 bytes.</li>
<li>The workloads were scaled from 100 thousand total operations per second to 1 million total operations per second.</li>
<li>The purpose of the graph is to show, even at different throughput, DynamoDB read latencies show very little variance and remain identical even as the throughput of the workload is increased.
<img loading="lazy" src="/images/notes/dynamodb-2022/image-6.png"></li>
</ul>
<hr>
<p><strong>Paper Link:</strong> <a href="https://www.usenix.org/conference/atc22/presentation/elhemali">https://www.usenix.org/conference/atc22/presentation/elhemali</a></p>
<hr>
<p><em>Last updated: March 15, 2026</em></p>
<p><em>Questions or discussion? <a href="mailto:sethi.hemant@gmail.com">Email me</a></em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Dynamo </title>
      <link>https://www.sethihemant.com/notes/dynamo-2007/</link>
      <pubDate>Mon, 02 Dec 2024 00:00:00 +0000</pubDate>
      <guid>https://www.sethihemant.com/notes/dynamo-2007/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href=&#34;https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf&#34;&gt;Dynamo &lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;dynamo--distributed-key-value-store&#34;&gt;Dynamo / Distributed Key Value Store&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Design a &lt;strong&gt;distributed key-value store(or Distributed Hash Table)&lt;/strong&gt; that is highly available (i.e., reliable), highly scalable, and completely decentralized.&lt;/p&gt;
&lt;h3 id=&#34;features&#34;&gt;Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Highly available Key-Value Store.&lt;/li&gt;
&lt;li&gt;Shopping Cart, Bestseller Lists, Sales Rank, Product Catalog, etc which &lt;strong&gt;needs only primary-key access to data&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Multi-table RDBMS would limit scalability and availability.&lt;/li&gt;
&lt;li&gt;Can choose desired Level of &lt;strong&gt;Availability&lt;/strong&gt; and &lt;strong&gt;Consistency&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;background&#34;&gt;Background?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Designed for **high availability(**at a massive scale) and &lt;strong&gt;partition tolerance&lt;/strong&gt; at the expense of &lt;strong&gt;strong consistency&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Primary Motivation for being optimized for High Availability(Over consistency) was to be always up for serving customer requests to provide better customer experience.&lt;/li&gt;
&lt;li&gt;Dynamo design inspired various NoSQL Databases, &lt;strong&gt;Cassandra&lt;/strong&gt;, &lt;strong&gt;Riak&lt;/strong&gt;, &lt;strong&gt;VoldemortDB&lt;/strong&gt;, &lt;strong&gt;DynamoDB&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;design-goals&#34;&gt;Design Goals?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Highly Available&lt;/li&gt;
&lt;li&gt;Reliability&lt;/li&gt;
&lt;li&gt;Highly Scalable&lt;/li&gt;
&lt;li&gt;Decentralized&lt;/li&gt;
&lt;li&gt;Eventually Consistent(EC) - Weaker Consistency model than Strong Consistency(Linearizability)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;Notes:&lt;/strong&gt; ) Latency Requirements?&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;Notes:&lt;/strong&gt; ) Geographical Distribution of Data?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;use-cases&#34;&gt;Use cases&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Dynamo can achieve strong consistency, but it comes with a performance impact. If Strong Consistency is a requirement, Dynamo is not the best option.&lt;/li&gt;
&lt;li&gt;Applications that need tight control over the trade-offs between availability, consistency, cost-effectiveness, and performance.&lt;/li&gt;
&lt;li&gt;Services that need only Primary Key access to the data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;system-apis&#34;&gt;System APIs:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;get(key)&lt;/strong&gt; : T… Object, Context&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;put(key, context, object)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Dynamo treats both the &lt;strong&gt;object&lt;/strong&gt; and &lt;strong&gt;the key&lt;/strong&gt; as an arbitrary &lt;strong&gt;array of bytes&lt;/strong&gt; (typically &lt;strong&gt;less than 1 MB&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Uses &lt;strong&gt;MD5 Hashing algorithm&lt;/strong&gt; on the key to generate &lt;strong&gt;128-bit HashID&lt;/strong&gt;, which is used to determine the storage nodes that are responsible for serving the key.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;high-level-architecture&#34;&gt;High Level Architecture&lt;/h3&gt;
&lt;h3 id=&#34;agenda&#34;&gt;Agenda&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Data Distribution(Partitioning)&lt;/li&gt;
&lt;li&gt;Data Replication and Consistency&lt;/li&gt;
&lt;li&gt;Handing Temporary Failures(Fault Tolerance)&lt;/li&gt;
&lt;li&gt;Inter-Node communication(Unreliable Network) and Failure Detection&lt;/li&gt;
&lt;li&gt;High Availability&lt;/li&gt;
&lt;li&gt;Conflict resolution and handling permanent failures.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;data-partitioning&#34;&gt;Data Partitioning&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Distributing data across a set of nodes is called &lt;strong&gt;data partitioning&lt;/strong&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>Paper:</strong> <a href="https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf">Dynamo </a></p>
<hr>
<h2 id="dynamo--distributed-key-value-store">Dynamo / Distributed Key Value Store</h2>
<p><strong>Problem:</strong> Design a <strong>distributed key-value store(or Distributed Hash Table)</strong> that is highly available (i.e., reliable), highly scalable, and completely decentralized.</p>
<h3 id="features">Features</h3>
<ul>
<li>Highly available Key-Value Store.</li>
<li>Shopping Cart, Bestseller Lists, Sales Rank, Product Catalog, etc which <strong>needs only primary-key access to data</strong>.</li>
<li>Multi-table RDBMS would limit scalability and availability.</li>
<li>Can choose desired Level of <strong>Availability</strong> and <strong>Consistency</strong>.</li>
</ul>
<h3 id="background">Background?</h3>
<ul>
<li>Designed for **high availability(**at a massive scale) and <strong>partition tolerance</strong> at the expense of <strong>strong consistency</strong>.</li>
<li>Primary Motivation for being optimized for High Availability(Over consistency) was to be always up for serving customer requests to provide better customer experience.</li>
<li>Dynamo design inspired various NoSQL Databases, <strong>Cassandra</strong>, <strong>Riak</strong>, <strong>VoldemortDB</strong>, <strong>DynamoDB</strong>.</li>
</ul>
<h3 id="design-goals">Design Goals?</h3>
<ul>
<li>Highly Available</li>
<li>Reliability</li>
<li>Highly Scalable</li>
<li>Decentralized</li>
<li>Eventually Consistent(EC) - Weaker Consistency model than Strong Consistency(Linearizability)</li>
<li>(<strong>Notes:</strong> ) Latency Requirements?</li>
<li>(<strong>Notes:</strong> ) Geographical Distribution of Data?</li>
</ul>
<h3 id="use-cases">Use cases</h3>
<ul>
<li>Dynamo can achieve strong consistency, but it comes with a performance impact. If Strong Consistency is a requirement, Dynamo is not the best option.</li>
<li>Applications that need tight control over the trade-offs between availability, consistency, cost-effectiveness, and performance.</li>
<li>Services that need only Primary Key access to the data.</li>
</ul>
<h3 id="system-apis">System APIs:</h3>
<ul>
<li><strong>get(key)</strong> : T… Object, Context</li>
<li><strong>put(key, context, object)</strong></li>
<li>Dynamo treats both the <strong>object</strong> and <strong>the key</strong> as an arbitrary <strong>array of bytes</strong> (typically <strong>less than 1 MB</strong>).</li>
<li>Uses <strong>MD5 Hashing algorithm</strong> on the key to generate <strong>128-bit HashID</strong>, which is used to determine the storage nodes that are responsible for serving the key.</li>
</ul>
<h3 id="high-level-architecture">High Level Architecture</h3>
<h3 id="agenda">Agenda</h3>
<ul>
<li>Data Distribution(Partitioning)</li>
<li>Data Replication and Consistency</li>
<li>Handing Temporary Failures(Fault Tolerance)</li>
<li>Inter-Node communication(Unreliable Network) and Failure Detection</li>
<li>High Availability</li>
<li>Conflict resolution and handling permanent failures.</li>
</ul>
<h3 id="data-partitioning">Data Partitioning</h3>
<ul>
<li>
<p>Distributing data across a set of nodes is called <strong>data partitioning</strong>.</p>
</li>
<li>
<p><strong>Challenges with Partitioning?</strong></p>
</li>
<li>
<p><strong>Naive Approach(Modulo Hashing)</strong></p>
</li>
<li>
<p><strong>Better Approach(Consistent Hashing)</strong>
<img loading="lazy" src="/images/notes/dynamo-2007/image-1.png"></p>
</li>
<li>
<p>Consistent hashing represents the data managed by a cluster as a <strong>ring</strong>. The ring is divided into smaller <strong>predefined ranges</strong>. Each node in the ring is assigned a range of data. The <strong>start of the range</strong> is called a <strong>token</strong>(each node is assigned one token).
<img loading="lazy" src="/images/notes/dynamo-2007/image-2.png"></p>
</li>
<li>
<p>Above works great when a node is added or removed from the ring; as only the next node is affected in these scenarios</p>
</li>
<li>
<p>The basic Consistent Hashing algorithm assigns a single token (or a consecutive hash range) to each physical node and does a <strong>static division of ranges</strong> that requires calculating tokens based on a given number of nodes.</p>
</li>
<li>
<p>Dynamo efficiently handles these scenarios(node addition/removal) through the use of <strong>Virtual Nodes</strong>(or <strong>Vnodes</strong>). New scheme for distributing Tokens to physical nodes.</p>
</li>
<li>
<p>Instead of assigning a single token to a node, the hash range is divided into multiple smaller ranges, and each physical node is assigned multiple of these smaller ranges. Each of these subranges is called a <strong>Vnode</strong>.
<img loading="lazy" src="/images/notes/dynamo-2007/image-3.png"></p>
</li>
<li>
<p><strong>Vnodes</strong> are randomly distributed across the cluster and are generally non-contiguous so that no two neighboring <strong>Vnodes</strong> are assigned to the same physical node.</p>
</li>
<li>
<p>Nodes also carry replicas of other nodes for <strong>fault-tolerance</strong>.
<img loading="lazy" src="/images/notes/dynamo-2007/image-4.png"></p>
</li>
<li>
<p>Since there can be <strong>heterogeneous</strong> machines in the clusters, some servers might hold more Vnodes than others.</p>
</li>
<li>
<p><strong>Advantages of VNodes:</strong></p>
</li>
</ul>
<h3 id="data-replication">Data Replication</h3>
<p>Agenda</p>
<ul>
<li>
<p>Optimistic replication</p>
</li>
<li>
<p>Preference List</p>
</li>
<li>
<p>Sloppy Quorum and Handling of Temporary failures</p>
</li>
<li>
<p>Hinted Handoff
<strong>Optimistic replication</strong></p>
</li>
<li>
<p>Replicates each data item on N nodes(N = Replication Factor, configurable per Dynamo instance).</p>
</li>
<li>
<p>Each key is assigned a <strong>Coordinator node</strong>(node that falls first in the hash range), which stores the data locally and <strong>replicates</strong> <strong>asynchronously(What?? or Synchronously?)</strong> to <strong>N-1 Clockwise successor</strong> nodes in the ring(eventually consistent) called <strong>Optimistic replication</strong>.
<img loading="lazy" src="/images/notes/dynamo-2007/image-5.png"></p>
</li>
<li>
<p>As Dynamo stores N copies of data spread across different nodes, if one node is down, other replicas can respond to queries for that range of data.</p>
</li>
<li>
<p>If a client cannot contact the coordinator node, it sends the request to a node holding a replica.
<strong>Preference List</strong></p>
</li>
<li>
<p>The list of nodes responsible for storing a particular key is called the preference list.</p>
</li>
<li>
<p>Dynamo is designed so that <strong>every node in the system can determine which nodes should be in this list for any specific key</strong>.</p>
</li>
<li>
<p>This list contains more than N nodes to account for failure and skip virtual nodes on the ring so that the list only contains distinct physical nodes.
<strong>Sloppy Quorum and handling of temporary failures</strong></p>
</li>
<li>
<p>Following <strong>traditional/strict</strong> quorum approaches, any distributed system becomes unavailable during server failures or network partitions and would have reduced availability even under simple failure conditions. Dynamo uses Sloppy Quorums.</p>
</li>
<li>
<p>With this approach, all read/write operations are performed on the first N healthy nodes from the preference list, which may not always be the first N nodes encountered while moving clockwise on the consistent hashing ring.</p>
</li>
<li>
<p><strong>Fault Tolerance with Sloppy Quorum</strong>.
<img loading="lazy" src="/images/notes/dynamo-2007/image-6.png"></p>
</li>
<li>
<p><strong>Hinted Handoff</strong></p>
</li>
</ul>
<h3 id="vector-clocks-and-conflicting-dataconflict-resolution">Vector Clocks and Conflicting Data(Conflict Resolution)</h3>
<p><strong>Agenda:</strong></p>
<ul>
<li>
<p>Clock Skew?</p>
</li>
<li>
<p>Vector Clock?</p>
</li>
<li>
<p>Conflict Free Replicated Data Types(CRDTs)</p>
</li>
<li>
<p>Last Write Wins(LWW)
Clock Skew</p>
</li>
<li>
<p>Physical clocks have clock skews, which is okay in single node systems, but can create concurrency updates in distributed systems, due to clock skews across different nodes.</p>
</li>
<li>
<p>Physical clocks are synchronized using NTP, but that still has skew, and 2 different nodes&rsquo; physical clocks can’t be accurately synchronized.</p>
</li>
<li>
<p>Using special hardware like GPS clocks and Atomic Clocks can reduce the clock skews, but doesn’t entirely eliminate it.</p>
</li>
<li>
<p>Physical clock has a problem with <strong>Causal Ordering</strong> of events(<strong>happens-before</strong> relationship).
Vector Clock?</p>
</li>
<li>
<p>Captures Causal ordering between events.</p>
</li>
<li>
<p>Vector clock is a (node, counter) pair. <strong>What? Isn’t it Lamport Clocks?</strong></p>
</li>
<li>
<p>Vector timestamps are attached to every version of the object stored in Dynamo.</p>
</li>
<li>
<p>One can determine whether two versions of an object are on parallel branches or have a causal ordering by examining their vector clocks.</p>
</li>
<li>
<p>If the counters on the first object’s clock are less-than-or-equal to all of the nodes in the second clock, then the first is an ancestor of the second and can be forgotten. Otherwise, the two changes are considered to be in conflict and require reconciliation. Dynamo resolves these conflicts at read-time.</p>
</li>
<li>
<p>Version branching may happen in the presence of failures combined with concurrent updates, resulting in conflicting versions of an object.</p>
</li>
<li>
<p>Dynamo <strong>truncates vector clocks (oldest first) when they grow too large</strong>. If Dynamo ends up deleting older vector clocks that are required to reconcile an object’s state, <strong>Dynamo would not be able to achieve eventual consistency</strong>.
<strong>Conflict Free Replicated Data Types</strong>?</p>
</li>
<li>
<p>To make use of CRDTs, we need to model our data in such a way that concurrent changes can be applied to the data in any order and will produce the same end result. This way, the system does not need to worry about any ordering guarantees.</p>
</li>
<li>
<p>The idea that any two nodes that have received the same set of updates will see the same end result is called <strong>strong eventual consistency</strong>.
<strong>Last Write Wins</strong></p>
</li>
<li>
<p>Dynamo(and Cassandra) also offer a way to do server side conflict resolution, <strong>LWW</strong>.</p>
</li>
<li>
<p>Uses <strong>Physical</strong>(Wall Clock/Time-Of-the-Day) Clocks.</p>
</li>
<li>
<p>Can potentially lead to Data-Loss during concurrent writes.</p>
</li>
</ul>
<h3 id="life-of-dynamos-put-and-get-operations">Life of Dynamo’s put() and get() operations.</h3>
<p><strong>Agenda:</strong></p>
<ul>
<li>Strategies for Coordinator selection</li>
<li>Consistency protocol</li>
<li>put() process</li>
<li>get() process</li>
<li>Request handling through a state machine.</li>
</ul>
<h3 id="strategies-for-choosing-coordinator">Strategies for choosing coordinator</h3>
<ul>
<li>Clients route request using Generic Load Balancer.</li>
<li>Clients use a <strong>partition-aware client library</strong> that routes requests to the appropriate coordinator with <strong>lower latency</strong>.
<img loading="lazy" src="/images/notes/dynamo-2007/image-7.png"></li>
</ul>
<h3 id="consistency-protocol">Consistency Protocol</h3>
<ul>
<li>
<p>Uses a consistency protocol similar to quorum systems.</p>
</li>
<li>
<p>R + W &gt; N ( R /W = minimum number of nodes to participate in Read/Write)</p>
</li>
<li>
<p>Common configurations(N, R, W) for Dynamo (3, 2, 2)</p>
</li>
<li>
<p>Latency of get() and put() depends upon the slowest of replicas.
Put() Process</p>
</li>
<li>
<p>Coordinator generates new data version and vector timestamp.</p>
</li>
<li>
<p>Saves data locally.</p>
</li>
<li>
<p>Sends write requests to N-1 highest ranked healthy nodes from the preference list.</p>
</li>
<li>
<p>Put() is considered successful after receiving W-1 confirmations.
Get() process</p>
</li>
<li>
<p>Coordinator requests the data version from N-1 highest ranked healthy nodes from the preference list.</p>
</li>
<li>
<p>Waits until R - 1 replies.</p>
</li>
<li>
<p>Coordinator handles causal data versioning using vector clocks/timestamps.</p>
</li>
<li>
<p>Returns all data versions to the caller.</p>
</li>
</ul>
<h3 id="request-handling-through-the-state-machine">Request handling through the state machine</h3>
<ul>
<li>Each client request results in creating a state machine on the node that received the client request.</li>
<li>The state machine contains all the logic for</li>
<li>Each state machine instance handles exactly one client request.</li>
<li>A <strong>read operation</strong> implements following <strong>state machine</strong>:</li>
<li><strong>Writes:</strong></li>
</ul>
<h3 id="anti-entropy-through-merkle-trees">Anti-Entropy through Merkle Trees</h3>
<ul>
<li>
<p>Dynamo uses Vector clocks to remove write conflicts(Read Repair) while serving read requests if it receives stale responses from some of the replicas.</p>
</li>
<li>
<p>If a replica fell significantly behind others, it might take a very long time to resolve conflicts using read repair(vector clocks), depending upon if those keys were read or not. It may happen that some of the keys are never accessed, and they cold remain stale for longer.</p>
</li>
<li>
<p>We need a mechanism to automatically reconcile replicas in the background(and do conflict resolution if any).</p>
</li>
<li>
<p>To do this, we need to quickly <strong>compare two copies of a range of data residing on different replicas</strong> and figure out exactly which parts are <strong>different</strong>.</p>
</li>
<li>
<p>Naively splitting up the entire data range for checksums is not very feasible; there is simply too much data to be transferred.(<strong>Transferred? How?</strong>)</p>
</li>
<li>
<p>Dynamo uses <strong>Merkle trees</strong> to compare replicas of a range.</p>
</li>
<li>
<p><strong>A Merkle tree</strong> is a <strong>binary tree of hashes</strong>, where each internal node is the hash of its two children, and each leaf node is a hash of a portion of the original data.
<img loading="lazy" src="/images/notes/dynamo-2007/image-8.png"></p>
</li>
<li>
<p>Now comparing the ranges of data on two replicas is equivalent to comparing two Merkle Trees</p>
</li>
<li>
<p>The principal advantage of using a Merkle tree is that each branch of the tree can be checked independently without requiring nodes to download the entire tree or the whole data set.</p>
</li>
<li>
<p>Merkle trees minimize the amount of data that needs to be transferred for synchronization and reduce the number of disk reads performed during the anti-entropy process.</p>
</li>
<li>
<p>The disadvantage of using Merkle trees is that many key ranges can change when a node joins or leaves, and as a result, the trees need to be recalculated.</p>
</li>
</ul>
<h3 id="gossip-protocol">Gossip Protocol</h3>
<h3 id="what-is-a-gossip-protocol">What is a Gossip Protocol?</h3>
<ul>
<li>How does <strong>Node Failure Detection</strong> happen in Dynamo?</li>
<li>Since we do not have any central node that keeps track of all nodes to know if a node is down or not, how does a node know every other node’s current state?</li>
<li><strong>Naive Approach:</strong> Each Node broadcast HeatBeat message to every other Node</li>
<li>Optimized Approach: <strong>Gossip Protocol</strong>
<img loading="lazy" src="/images/notes/dynamo-2007/image-9.png"></li>
</ul>
<h3 id="external-discovery-through-seed-nodes">External Discovery Through Seed Nodes?</h3>
<ul>
<li>Dynamo nodes use gossip protocol to find the current state of the ring. This can result in a logical partition of the cluster in a particular scenario.</li>
<li>An administrator joins node A to the ring and then joins node B to the ring. Nodes A and B consider themselves part of the ring, yet neither would be immediately aware of each other. To prevent these logical partitions, Dynamo introduced the concept of seed nodes.</li>
<li>Seed nodes are fully functional nodes and can be obtained either from a static configuration or a configuration service. This way, all nodes are aware of seed nodes.</li>
<li>Each node communicates with seed nodes through gossip protocol to reconcile membership changes; therefore, logical partitions are highly unlikely.</li>
</ul>
<h3 id="characteristics-and-criticism-of-dynamo">Characteristics and Criticism of Dynamo</h3>
<p>Responsibilities of a Dynamo Node</p>
<ul>
<li>Managing get() and put() requests via acting as a Coordinator(or request Forwarder).</li>
<li>Keeping track of membership(Hash ranges in a Ring) and detecting failures(Gossip)</li>
<li>Local Persistent Storage</li>
</ul>
<h3 id="characteristics-of-dynamo">Characteristics of Dynamo</h3>
<ul>
<li>Distributed(Can run across several machines)</li>
<li>Decentralized(No external coordinator, all nodes identical)</li>
<li>Scalable(Horizontally scaled on commodity hardware with Fault Tolerance. No Manual intervention/rebalancing required)</li>
<li>Highly Available</li>
<li>Fault Tolerant and Reliable</li>
<li>Tunable Consistency(Trade Offs b/w Availability and Consistency by adjusting the replication factor 3,2,2, or 3,1,3, or 3,3,1 etc).</li>
</ul>
<h3 id="criticism-on-dynamo-design">Criticism on Dynamo Design?</h3>
<ul>
<li>Each Dynamo node contains the entire routing table. Could affect scalability of the system as this routing table gets larger as more nodes are added to the system.</li>
<li>Dynamo seems to imply that it strives for symmetry(all nodes have the same set of responsibilities). But it does specify some nodes as seed nodes for external discovery to avoid logical partition. May violate Dynamo’s symmetry principle.</li>
<li>DHTs can be susceptible to Several different types of attack?[Research More?]</li>
<li>Dynamo’s design can be described as a <strong>Leaky Abstraction</strong>.</li>
</ul>
<h3 id="datastores-developed-on-principles-of-dynamo">DataStores developed on Principles of Dynamo</h3>
<ul>
<li><strong>Riak</strong> is a distributed NoSQL key-value data store that is highly available, scalable, fault-tolerant, and easy to operate.</li>
<li><strong>Cassandra</strong> is a distributed, decentralized, scalable, and highly available NoSQL wide-column database.
<img loading="lazy" src="/images/notes/dynamo-2007/image-10.png"></li>
</ul>
<h3 id="summary">Summary</h3>
<p><img loading="lazy" src="/images/notes/dynamo-2007/image-11.png"></p>
<p>Paper reading Video.</p>
<p>References:</p>
<ul>
<li>
<p><a href="https://www.allthingsdistributed.com/2007/10/amazons_dynamo.html">https://www.allthingsdistributed.com/2007/10/amazons_dynamo.html</a></p>
</li>
<li>
<p><a href="https://docs.riak.com/riak/kv/2.2.0/developing/data-types/">https://docs.riak.com/riak/kv/2.2.0/developing/data-types/</a></p>
</li>
<li>
<p><a href="https://research.google/pubs/bigtable-a-distributed-storage-system-for-structured-data/">https://research.google/pubs/bigtable-a-distributed-storage-system-for-structured-data/</a></p>
</li>
<li>
<p><a href="https://www.allthingsdistributed.com/2012/01/amazon-dynamodb.html">https://www.allthingsdistributed.com/2012/01/amazon-dynamodb.html</a></p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type</a></p>
</li>
<li>
<p><a href="https://www.allthingsdistributed.com/2017/10/a-decade-of-dynamo.html">https://www.allthingsdistributed.com/2017/10/a-decade-of-dynamo.html</a></p>
</li>
<li>
<p><a href="https://news.ycombinator.com/item?id=915212">https://news.ycombinator.com/item?id=915212</a>
<strong>Open Questions:</strong></p>
</li>
<li>
<p>Anti-Entropy and Merkle Trees</p>
</li>
<li>
<p>DHTs can be susceptible to Several different types of attack?[Research More?]</p>
</li>
<li>
<p>Underlying storage for Dynamo store. Berkeley, in-memory + persistent + more.</p>
</li>
<li>
<p>Why does it use MD5 hashing? Why not something else?</p>
</li>
<li>
<p>Logical partitioning and seed nodes?</p>
</li>
<li>
<p>New Features and revision in the design?</p>
</li>
</ul>
<hr>
<p><strong>Paper Link:</strong> <a href="https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf">https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf</a></p>
<hr>
<p><em>Last updated: March 15, 2026</em></p>
<p><em>Questions or discussion? <a href="mailto:sethi.hemant@gmail.com">Email me</a></em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
