<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>Timilearning - A blog by Timi Adeniran</title>
	<subtitle>Writing about computer science topics that I am curious about, ranging from distributed systems to (eventually) bioinformatics.</subtitle>
	<link href="https://timilearning.com/feed/feed.xml" rel="self"/>
	<link href="https://timilearning.com"/>
	<updated>2022-07-26T20:31:30-00:00</updated>
	<id>https://timilearning.com/</id>
	<author>
		<name>Timi Adeniran</name>
		<email>oluwatimilehinadeniran@gmail.com</email>
	</author>
	
	<entry>
		<title>A Library for Incremental Computing</title>
		<link href="https://timilearning.com/posts/incremental-computing/"/>
		<updated>2022-07-26T20:31:30-00:00</updated>
		<id>https://timilearning.com/posts/incremental-computing/</id>
		<content type="html">&lt;p&gt;Late last year, while working on my &lt;a href=&quot;https://timilearning.com/tags/c++17/&quot;&gt;C++ series&lt;/a&gt; and on the lookout for a project to build in C++, I came across &lt;a href=&quot;https://lord.io/spreadsheets/&quot;&gt;&amp;quot;How to Recalculate a Spreadsheet&amp;quot;&lt;/a&gt;, which inspired me to build &lt;a href=&quot;https://github.com/oluwatimilehin/anchors&quot;&gt;Anchors&lt;/a&gt; — a C++ library for incremental computing.&lt;/p&gt;
&lt;p&gt;I highly recommend reading the post on &lt;a href=&quot;http://lord.io/&quot;&gt;lord.io&lt;/a&gt; if you want to learn about incremental computing, and perhaps return here if you&#39;re interested in some implementation details.&lt;/p&gt;
&lt;p&gt;But if you have just thought to yourself, &amp;quot;I&#39;m not sure I want to read more than one article on this topic, so I&#39;d rather not open a new tab&amp;quot;, I will also summarize incremental computing before getting into the implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#incremental-computing&quot;&gt;Incremental Computing&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#modelling-computation-as-a-graph&quot;&gt;Modelling computation as a graph&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#dirty-marking&quot;&gt;Dirty Marking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#topological-sorting&quot;&gt;Topological Sorting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#demand-driven-incremental-computing&quot;&gt;Demand-driven Incremental Computing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#anchors&quot;&gt;Anchors&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-anchor-class&quot;&gt;The Anchor class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-engine-class&quot;&gt;The Engine class&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#observing-an-anchor&quot;&gt;Observing an Anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#setting-an-anchor%27s-value&quot;&gt;Setting an Anchor&#39;s value&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#reading-an-anchor%27s-value&quot;&gt;Reading an Anchor&#39;s value&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#closing-thoughts&quot;&gt;Closing Thoughts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;incremental-computing&quot;&gt;Incremental Computing &lt;a class=&quot;direct-link&quot; href=&quot;#incremental-computing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Imagine we find ourselves working on a program to calculate the answer to something as difficult as &lt;a href=&quot;https://www.scientificamerican.com/article/for-math-fans-a-hitchhikers-guide-to-the-number-42/&quot;&gt;“the ultimate question of life, the universe, and everything”&lt;/a&gt;. Typically, a question so grand will require a complex formula to solve. But in this alternate universe, we have been told that we can solve this by simply plugging in values for &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; into the formula: &lt;code&gt;z = (x + y) * 42&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We sigh with relief as this problem is now simpler. But our relief is short-lived: we are told that the values of &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; each take up to a day to compute, because their formulas are so complex, and our program will have to wait.&lt;/p&gt;
&lt;p&gt;Still, we&#39;re happy to wait and eventually, we receive our values, plug them into the formula, and go out to celebrate our new discovery.&lt;/p&gt;
&lt;p&gt;Midway through our celebrations, we get interrupted and are told that the universe has received an update to &lt;code&gt;x&lt;/code&gt;&#39;s formula, so we would need to recompute it, as well as the value of &lt;code&gt;z&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since only the value of &lt;code&gt;x&lt;/code&gt; has changed and &lt;code&gt;y&lt;/code&gt; hasn&#39;t, we know we can reuse the previously computed value of &lt;code&gt;y&lt;/code&gt; and simply plug the new &lt;code&gt;x&lt;/code&gt; value into our formula to get the result.&lt;/p&gt;
&lt;p&gt;But while it may seem obvious to us that there&#39;s no need to recompute &lt;code&gt;y&lt;/code&gt; because its formula has not changed, &lt;a href=&quot;https://aresluna.org/attached/computerhistory/articles/spreadsheets/tenyearsofrowsandcolumns&quot;&gt;early models of computing were not so smart&lt;/a&gt;. They were more likely to recompute the values of all of &lt;code&gt;z&lt;/code&gt;&#39;s inputs before recomputing &lt;code&gt;z&lt;/code&gt;, even if an input had received no updates.&lt;/p&gt;
&lt;p&gt;This mental model we have, where we know to only recompute what depends on either a changed formula (&lt;code&gt;x&lt;/code&gt; in our example) or a changed input (&lt;code&gt;z&lt;/code&gt; after &lt;code&gt;x&lt;/code&gt; changed), is the concept of incremental computing. From Wikipedia,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Incremental computing is a software feature which, whenever a piece of data changes, attempts to save time by only recomputing those outputs which depend on the changed data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&#39;ve used a trivial (and far-fetched) example, but this computing model generalises to different applications, ranging from &lt;a href=&quot;https://lord.io/spreadsheets/&quot;&gt;spreadsheets&lt;/a&gt; to &lt;a href=&quot;https://github.com/janushendersonassetallocation/loman/blob/master/examples/Example%20-%20Using%20Loman%20to%20Value%20a%20Portfolio.ipynb&quot;&gt;complex financial applications&lt;/a&gt; to &lt;a href=&quot;https://www.youtube.com/watch?v=DSuX-LIAU-I&quot;&gt;rendering GUIs&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;modelling-computation-as-a-graph&quot;&gt;Modelling computation as a graph &lt;a class=&quot;direct-link&quot; href=&quot;#modelling-computation-as-a-graph&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We can achieve incremental computing by modelling data as a directed acyclic graph, where each data element is a node in the graph, and there is an edge from a node A to node B if A is an input to B. That is, node B depends on node A.&lt;/p&gt;
&lt;p&gt;We can represent our alternate universe example as the graph:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/universe.png&quot; alt=&quot;Graph with three nodes: x, y, and z. x and y have edges to z.&quot;&gt; &lt;/p&gt;
&lt;p&gt;Now, let&#39;s use a less trivial example with a larger graph. Recall the quadratic formula for solving equations of the form ax&lt;sup&gt;2&lt;/sup&gt; + bx + c = 0:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/quadratic-formula-200.png&quot; alt=&quot;Quadratic formula&quot;&gt; &lt;/p&gt;
&lt;p&gt;We can represent this as a computation graph with three initial inputs, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, and &lt;code&gt;c&lt;/code&gt;:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/quadratic-graph-fill-2.png&quot; alt=&quot;Computation graph representing the quadratic formula. &quot;&gt; &lt;/p&gt;
&lt;p&gt;In this graph, there is an outgoing edge from a node if the node is an input to another node.&lt;/p&gt;
&lt;p&gt;If any of the inputs, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, or &lt;code&gt;c&lt;/code&gt; changes, we would need to recalculate the outputs, &lt;code&gt;x1&lt;/code&gt; and &lt;code&gt;x2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But if only &lt;code&gt;b&lt;/code&gt; changes, we should recalculate only the nodes that depend on &lt;code&gt;b&lt;/code&gt; either directly or indirectly, and we should calculate them in the right order: &lt;code&gt;b^2&lt;/code&gt; before &lt;code&gt;sq&lt;/code&gt;, &lt;code&gt;sq&lt;/code&gt; before &lt;code&gt;-b + sq&lt;/code&gt;, and &lt;code&gt;-b + sq&lt;/code&gt; before &lt;code&gt;x1&lt;/code&gt;, for example.&lt;/p&gt;
&lt;p&gt;We do not need to recalculate nodes &lt;code&gt;4ac&lt;/code&gt; and &lt;code&gt;2a&lt;/code&gt;, which do not depend on &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;From the structure of the graph, we can answer two questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What nodes should we update if an input changes?&lt;/li&gt;
&lt;li&gt;In what order should we update them?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Answering these questions, while they may seem obvious to us, are two major challenges in writing a program to perform incremental computations.&lt;/p&gt;
&lt;p&gt;Thankfully, some smart people have gone before us and offered two solutions to these questions: &lt;em&gt;dirty marking&lt;/em&gt; and &lt;em&gt;topological sorting&lt;/em&gt;, which I will describe next.&lt;/p&gt;
&lt;h4 id=&quot;dirty-marking&quot;&gt;Dirty Marking &lt;a class=&quot;direct-link&quot; href=&quot;#dirty-marking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Dirty marking works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When a node&#39;s value or formula changes, the program marks all the nodes that depend on it (directly or indirectly) as &lt;em&gt;dirty&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In the quadratic formula example, it will start with &lt;code&gt;b&lt;/code&gt; and go down to the leaf nodes.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To bring the graph up to date, in a loop, the program finds a dirty node that has no dirty inputs (or dependencies) and recomputes its value — making it &lt;em&gt;clean&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It continues the loop until there are no more dirty nodes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dirty marking answers our two questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;What nodes should the program update if an input changes?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Only those it has marked as dirty.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In what order should the program update them?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It should compute dirty nodes with no dirty inputs before computing dirty nodes with dirty inputs, which ensures that it computes dependencies before their dependants.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;topological-sorting&quot;&gt;Topological Sorting &lt;a class=&quot;direct-link&quot; href=&quot;#topological-sorting&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;With topological sorting, the program gives each node a &lt;em&gt;height&lt;/em&gt; and uses this height and a minimum heap to answer our questions.  It works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The program gives a node with no inputs a height of 0.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If a node has inputs, its height is max(height of inputs) + 1, which guarantees that a node will always have a greater height than its inputs&#39; heights.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When a node&#39;s value or formula changes, the program adds the node to the minimum heap.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To bring the graph up to date, the program removes the node with the smallest height from the heap and recomputes it.  If the node&#39;s value has changed after recomputing, it adds the node&#39;s &lt;em&gt;dependants&lt;/em&gt; to the heap.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It continues with the previous step until the heap is empty.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Topological sorting answers our questions in the following ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;What nodes should the program update if an input changes?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Only those it has added to the heap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In what order should the program update them?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It should recompute nodes with a smaller height before those with a large height, ensuring that it recomputes a node&#39;s inputs before the node itself.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;demand-driven-incremental-computing&quot;&gt;Demand-driven Incremental Computing &lt;a class=&quot;direct-link&quot; href=&quot;#demand-driven-incremental-computing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So far, I have described a form of incremental computing in which a program recomputes the values of all affected nodes in the graph to bring them up to date when an input changes.&lt;/p&gt;
&lt;p&gt;Let&#39;s say we have a scenario where a node &lt;code&gt;A&lt;/code&gt; is an input to many nodes, but we are only interested in the value of one of these nodes, &lt;code&gt;B&lt;/code&gt;, at a time. In the model I have described, when node &lt;code&gt;A&lt;/code&gt; changes, the program will recompute node &lt;code&gt;B&lt;/code&gt; and any other nodes that depend on &lt;code&gt;A&lt;/code&gt;, even though we don&#39;t care about them.&lt;/p&gt;
&lt;p&gt;In a graph with complex formulas, performing these unnecessary computations could be costly.&lt;/p&gt;
&lt;p&gt;Ideally, we would want to specify that a program should only perform computations that are necessary for node(s) we are interested in. We want computations to be &lt;em&gt;demand-driven&lt;/em&gt; or &lt;em&gt;lazy.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This requirement has led to the creation of libraries for demand-driven incremental computing, such as &lt;a href=&quot;https://docs.rs/adapton/latest/adapton/&quot;&gt;Adapton&lt;/a&gt;, which uses dirty marking and &lt;a href=&quot;https://opensource.janestreet.com/incremental/&quot;&gt;Incremental&lt;/a&gt;, which uses topological sorting as its algorithm.&lt;/p&gt;
&lt;p&gt;They support lazy computations by allowing clients to &lt;em&gt;observe&lt;/em&gt; nodes they&#39;re interested in. When an input changes and a client wants to bring observed nodes up to date, the programs only recompute nodes that affect the observed nodes.&lt;/p&gt;
&lt;p&gt;The next section covers how you might build a program for demand-driven incremental computing.&lt;/p&gt;
&lt;h2 id=&quot;anchors&quot;&gt;Anchors &lt;a class=&quot;direct-link&quot; href=&quot;#anchors&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://oluwatimilehin.github.io/anchors/&quot;&gt;Anchors&lt;/a&gt; is a C++ library for demand-driven incremental computing, inspired by the &lt;a href=&quot;https://github.com/lord/anchors&quot;&gt;Rust library&lt;/a&gt; of the same name—yes, I got permission before using the name.&lt;/p&gt;
&lt;p&gt;The Rust version implements a hybrid algorithm combining dirty marking and topological sorting described &lt;a href=&quot;https://lord.io/spreadsheets/&quot;&gt;here&lt;/a&gt;, but the C++ version currently only implements topological sorting. Implementing the hybrid algorithm is on the roadmap.&lt;/p&gt;
&lt;p&gt;The rest of this section will cover the C++ implementation, which is based on Jane Street&#39;s Incremental library.&lt;/p&gt;
&lt;p&gt;Two classes make up the core of Anchors: an &lt;code&gt;Anchor&lt;/code&gt; class, which represents a node in the graph, and an &lt;code&gt;Engine&lt;/code&gt;, which handles the logic. A simple example of their usage is this program below, which performs a simple addition:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// First create an Engine object&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;Engine d_engine&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create two input Anchors A and B&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anchorA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Anchors&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anchorB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Anchors&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create the function to map from the inputs to the output&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; sum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create an Anchor C, using A and B as inputs, &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// whose value is the sum of the input Anchors&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anchorC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Anchors&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;map2&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;anchorA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; anchorB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Observe Anchor C and verify that its value is correct&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;d_engine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;anchorC&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;d_engine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;anchorC&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Update one of the inputs, A, and verify that Anchor C is kept up to date.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;d_engine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;anchorA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;d_engine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;anchorC&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-anchor-class&quot;&gt;The Anchor class &lt;a class=&quot;direct-link&quot; href=&quot;#the-anchor-class&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An &lt;code&gt;Anchor&lt;/code&gt; represents a node in the computation graph. As shown in the example above, you can create an &lt;code&gt;Anchor&lt;/code&gt; with a value:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; anchorA &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Anchors&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or with a function that takes one or more input &lt;code&gt;Anchor&lt;/code&gt; objects and a function to map from the inputs to an output value:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;auto&lt;/span&gt; anchorC &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Anchors&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;map2&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;anchorA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; anchorB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An &lt;code&gt;Anchor&lt;/code&gt;&#39;s  state includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it&#39;s current value.&lt;/li&gt;
&lt;li&gt;height — 0 if it has no inputs, max(height of inputs) + 1 otherwise.&lt;/li&gt;
&lt;li&gt;whether it is necessary. An &lt;code&gt;Anchor&lt;/code&gt; is necessary if a client marks it as observed, or if it is a dependency (direct or indirect) of an observed &lt;code&gt;Anchor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;a recompute id, which indicates when last the &lt;code&gt;Anchor&lt;/code&gt; was brought up to date.&lt;/li&gt;
&lt;li&gt;a change id, representing when the &lt;code&gt;Anchor&lt;/code&gt;&#39;s value last changed. An &lt;code&gt;Anchor&lt;/code&gt; can be brought up to date without its value changing.&lt;/li&gt;
&lt;li&gt;its dependencies (inputs) and dependants, if any.&lt;/li&gt;
&lt;li&gt;an updater function to compute the &lt;code&gt;Anchor&lt;/code&gt;&#39;s value from its dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The class exposes getters and setters for these elements, as well as a &lt;code&gt;compute(id)&lt;/code&gt; function to the &lt;code&gt;Engine&lt;/code&gt; class. I&#39;ll describe how the &lt;code&gt;Engine&lt;/code&gt; class derives the &lt;code&gt;id&lt;/code&gt; it uses as the function argument in the next section.&lt;/p&gt;
&lt;p&gt;When called, the &lt;code&gt;compute(id)&lt;/code&gt; function invokes the updater function, passing the &lt;code&gt;Anchor&lt;/code&gt;&#39;s dependencies as arguments to bring the &lt;code&gt;Anchor&lt;/code&gt;&#39;s value up to date. It also sets the recompute id to &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the newly computed value differs from the old, the &lt;code&gt;compute(id)&lt;/code&gt; function sets the change id of the &lt;code&gt;Anchor&lt;/code&gt; to &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-engine-class&quot;&gt;The Engine class &lt;a class=&quot;direct-link&quot; href=&quot;#the-engine-class&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;Engine&lt;/code&gt; is the brain of the Anchors library, through which clients interact with &lt;code&gt;Anchor&lt;/code&gt; objects. Its state includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;a recompute heap, containing the &lt;code&gt;Anchor&lt;/code&gt; objects that need to be recomputed, ordered by height.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a recompute set, to prevent adding duplicates to the heap. An &lt;code&gt;Anchor&lt;/code&gt; could potentially keep track of whether it&#39;s in the recompute heap, but that&#39;s not a solution I&#39;ve explored yet.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a set of observed nodes.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a monotonically increasing stabilization number, which indicates when the &lt;code&gt;Anchor&lt;/code&gt; objects were last brought up to date, or when an &lt;code&gt;Anchor&lt;/code&gt;&#39;s value last changed. It passes this number as an argument to the &lt;code&gt;compute(id)&lt;/code&gt; function.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Its API exposes functions to &lt;code&gt;observe()&lt;/code&gt; an &lt;code&gt;Anchor&lt;/code&gt;, as well as &lt;code&gt;get()&lt;/code&gt; and &lt;code&gt;set()&lt;/code&gt; an &lt;code&gt;Anchor&lt;/code&gt;&#39;s value.&lt;/p&gt;
&lt;h4 id=&quot;observing-an-anchor&quot;&gt;Observing an Anchor &lt;a class=&quot;direct-link&quot; href=&quot;#observing-an-anchor&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;observe(anchor)&lt;/code&gt; function does the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the &lt;code&gt;Anchor&lt;/code&gt; to the set of observed nodes and mark it as necessary.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the &lt;code&gt;Anchor&lt;/code&gt; is &lt;em&gt;stale&lt;/em&gt;, add it to the recompute heap. An &lt;code&gt;Anchor&lt;/code&gt; is stale if it has never been computed, or if its recompute id is less than the change id of one of its dependencies. That is, if the &lt;code&gt;Engine&lt;/code&gt; has not recomputed the &lt;code&gt;Anchor&lt;/code&gt; since any of its dependencies changed.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Walk the graph to mark the necessary &lt;code&gt;Anchor&lt;/code&gt; nodes, including those that the &lt;code&gt;Engine&lt;/code&gt; should recompute. For each dependency of the given &lt;code&gt;Anchor&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add the &lt;code&gt;Anchor&lt;/code&gt; as a dependant.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Mark the dependency as necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;If the dependency is stale and not already in the recompute heap, add it to the recompute heap.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Repeat step 3 using this dependency as the new &amp;quot;given&amp;quot; &lt;code&gt;Anchor&lt;/code&gt; until there are no more dependencies to compute.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;setting-an-anchor&#39;s-value&quot;&gt;Setting an Anchor&#39;s value &lt;a class=&quot;direct-link&quot; href=&quot;#setting-an-anchor&#39;s-value&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You can update an &lt;code&gt;Anchor&lt;/code&gt;&#39;s value using &lt;code&gt;set(anchor, newValue)&lt;/code&gt; on the &lt;code&gt;Engine&lt;/code&gt; class, which does the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the &lt;code&gt;Anchor&lt;/code&gt;&#39;s value using the &lt;code&gt;setValue()&lt;/code&gt; function in the &lt;code&gt;Anchor&lt;/code&gt; class. The &lt;code&gt;Anchor&lt;/code&gt; class exposes this function only to the &lt;code&gt;Engine&lt;/code&gt; class.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Increase the stabilization number and set the change id of the given &lt;code&gt;Anchor&lt;/code&gt; to the new number if the &lt;code&gt;Anchor&lt;/code&gt;&#39;s value has changed.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;If the &lt;code&gt;Anchor&lt;/code&gt; is necessary, add all its dependants to the recompute heap.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;reading-an-anchor&#39;s-value&quot;&gt;Reading an Anchor&#39;s value &lt;a class=&quot;direct-link&quot; href=&quot;#reading-an-anchor&#39;s-value&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;Engine&lt;/code&gt; class exposes a &lt;code&gt;get(anchor)&lt;/code&gt; function to read an &lt;code&gt;Anchor&lt;/code&gt;s value. Anchors guarantees that reading an &lt;em&gt;observed&lt;/em&gt; &lt;code&gt;Anchor&lt;/code&gt; will return its most up-to-date value.&lt;/p&gt;
&lt;p&gt;It achieves this through a process called &lt;em&gt;stabilization,&lt;/em&gt; which involves the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Increase the current stabilization number.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remove the &lt;code&gt;Anchor&lt;/code&gt; with the smallest height from the recompute heap.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the &lt;code&gt;Anchor&lt;/code&gt; is stale, recompute its value by calling &lt;code&gt;compute(id)&lt;/code&gt; on the &lt;code&gt;Anchor&lt;/code&gt;, passing the new stabilization number as the argument.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the value of the current &lt;code&gt;Anchor&lt;/code&gt; changed after recomputing, i.e. its change id is equal to the stabilization number, add its dependants to the recompute heap.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Repeat steps 2-5 until the recompute heap is empty.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When you call &lt;code&gt;get(anchor)&lt;/code&gt; on any observed &lt;code&gt;Anchor&lt;/code&gt;, the &lt;code&gt;Engine&lt;/code&gt; class will run the stabilization process provided the recompute heap is not empty.&lt;/p&gt;
&lt;p&gt;To summarize:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Anchors minimizes wasteful computations by only adding necessary nodes to the recompute heap.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;By computing nodes in increasing order of height, Anchors ensures that it brings a node&#39;s dependencies up to date before the node itself.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing Thoughts &lt;a class=&quot;direct-link&quot; href=&quot;#closing-thoughts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The examples in this article are trivial compared to real-world use cases of incremental computing, where nodes typically have many more inputs and more complex formulas. An example of how one can use an incremental computing library to value a portfolio in finance is &lt;a href=&quot;https://github.com/janushendersonassetallocation/loman/blob/master/examples/Example%20-%20Using%20Loman%20to%20Value%20a%20Portfolio.ipynb&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anchors is still a work in progress and I intend to bring its algorithm closer to the hybrid one in the Rust library it is based on, eventually.&lt;/p&gt;
&lt;p&gt;The hardest part of building this so far (conveniently ignoring the time I spent deciphering some C++ error messages) has been getting started: figuring out the API I wanted for the library and outlining the implementation details.  If you&#39;re thinking of building something similar, I hope this post makes it a little easier for you.&lt;/p&gt;
&lt;p&gt;Finally, part of my motivation for writing this post was to share the code and get feedback. So, if you go through the &lt;a href=&quot;https://github.com/oluwatimilehin/anchors&quot;&gt;code&lt;/a&gt; and have any suggestions you think I&#39;ll find interesting, please let me know either through the form below or by creating a pull request.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://lord.io/spreadsheets/&quot;&gt;How to Recalculate a Spreadsheet&lt;/a&gt; on &lt;a href=&quot;http://lord.io/&quot;&gt;lord.io&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lord/anchors&quot;&gt;lord/anchors&lt;/a&gt; - Rust&#39;s Anchors&#39; implementation, featuring a hybrid algorithm comprising dirty marking and topological sorting.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/janestreet/incremental&quot;&gt;janestreet/incremental&lt;/a&gt; - OCaml library for incremental computing which uses a topological sort.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.rs/adapton/0.3.31/adapton/&quot;&gt;adapton - Rust&lt;/a&gt; - Rust library for incremental computing using dirty marking.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/fsprojects/Incremental.NET&quot;&gt;fsprojects/Incremental.NET&lt;/a&gt; - F# library for incremental computing.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/oluwatimilehin/anchors&quot;&gt;Anchors: C++ library for Incremental Computing&lt;/a&gt; - Project repository containing (hopefully) well-documented code and more concrete code examples, including the &lt;a href=&quot;https://github.com/oluwatimilehin/anchors#a-quadratic-formula-calculator&quot;&gt;Quadratic Formula Calculator&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Many thanks to&lt;/em&gt; &lt;a href=&quot;https://uk.linkedin.com/in/oluwatobi-adeoye&quot;&gt;&lt;em&gt;Tobi Adeoye&lt;/em&gt;&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href=&quot;https://www.linkedin.com/in/tofunmiogungbaigbe/?originalSubdomain=uk&quot;&gt;&lt;em&gt;Tofunmi Ogungbaigbe&lt;/em&gt;&lt;/a&gt; &lt;em&gt;for providing feedback on an earlier draft of this article.&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Learning C++ from Java - Pointers and References</title>
		<link href="https://timilearning.com/posts/learning-cpp/pointers-and-references/"/>
		<updated>2021-12-23T15:53:49-00:00</updated>
		<id>https://timilearning.com/posts/learning-cpp/pointers-and-references/</id>
		<content type="html">&lt;p&gt;This is a continuation of the series on C++ topics I&#39;ve found interesting. You can read the earlier parts &lt;a href=&quot;https://timilearning.com/posts/learning-cpp/introduction&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://timilearning.com/posts/learning-cpp/header-files&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before I started learning C++, I had read about Java being pass-by-value rather than pass-by-reference, but I found it easier to think I was passing objects around by reference.&lt;/p&gt;
&lt;p&gt;This post begins by summarizing pointers and references in C++, before describing the different semantics for passing arguments to a function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#pointers&quot;&gt;Pointers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#passing-arguments-to-a-function&quot;&gt;Passing arguments to a function&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#pass-by-value&quot;&gt;Pass-by-Value&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#pass-by-reference&quot;&gt;Pass-by-Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#pass-by-address&quot;&gt;Pass-by-Address&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#an-aside&quot;&gt;An aside&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#pass-by-reference-or-pass-by-address&quot;&gt;Pass-by-Reference or Pass-by-Address&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#java-is-pass-by-value&quot;&gt;Java is Pass-by-Value&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;pointers&quot;&gt;Pointers &lt;a class=&quot;direct-link&quot; href=&quot;#pointers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A pointer in C++ is a variable that holds the address of another variable. We use the address-of(&lt;code&gt;&amp;amp;&lt;/code&gt;) operator to get the address of a variable, and can store that address in a pointer.&lt;/p&gt;
&lt;p&gt;For example, we can declare an integer and print its memory address, as shown below.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The address of the width variable is &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;widthPtr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// We use the asterisk to declare a pointer.&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The address of the width variable is &quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; widthPtr &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example, we print the address of the &lt;code&gt;width&lt;/code&gt; variable using the address-of operator directly and the &lt;code&gt;widthPtr&lt;/code&gt; pointer. This prints:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;The address of the width variable is 0x7ffcaf841c3c&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;The address of the width variable is 0x7ffcaf841c3c&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have declared a pointer, we may want to access the value stored at the address it holds. To do that, we use the indirection or dereference (&lt;code&gt;*&lt;/code&gt;) operator:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;widthPtr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The value of the width variable is &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The address of the width variable is: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; widthPtr &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;widthPtr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;93&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The value of the width variable is &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The value of the width variable accessed via the pointer is &quot;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;widthPtr &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which gives the output below:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;The value of the width variable is &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;The address of the width variable is: 0x7ffc882a0f0c&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;The value of the width variable is &lt;span class=&quot;token number&quot;&gt;93&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;The value of the width variable accessed via the pointer is &lt;span class=&quot;token number&quot;&gt;93&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It might confuse you to see the same asterisk &lt;code&gt;*&lt;/code&gt; operator used to both declare and dereference a pointer. To clarify this, a rule of thumb is you are declaring a pointer when you use the operator after specifying a type—as it is with declaring a regular variable—and dereferencing one when there&#39;s no type before it.&lt;/p&gt;
&lt;h3 id=&quot;references&quot;&gt;References &lt;a class=&quot;direct-link&quot; href=&quot;#references&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A reference variable is an alias to an existing variable. You use the ampersand &lt;code&gt;&amp;amp;&lt;/code&gt; symbol to declare a reference. For example, &lt;code&gt;widthRef&lt;/code&gt; is an alias to &lt;code&gt;width&lt;/code&gt; in the example below:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;widthRef &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the ampersand &lt;code&gt;&amp;amp;&lt;/code&gt; symbol means &amp;quot;reference to&amp;quot; and not &amp;quot;address of&amp;quot;. A rule of thumb here is the operator always means &amp;quot;reference to&amp;quot; when on the left-hand side of the equals sign and means &amp;quot;address of&amp;quot; when on the right.&lt;/p&gt;
&lt;p&gt;After creating a reference, you can use it to access the variable it is aliasing:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;widthRef &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The value of the width variable is &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    widthRef &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;93&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Notice the missing ampersand&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The value of the width variable is &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The value of the width variable accessed via the reference is &quot;&lt;/span&gt; &lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; widthRef &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which prints:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;The value of the width variable is &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;The value of the width variable is &lt;span class=&quot;token number&quot;&gt;93&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;The value of the width variable accessed via the reference is &lt;span class=&quot;token number&quot;&gt;93&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are some rules for using references which make them safer to use than pointers, such as that unlike pointers, they cannot hold a null value.  Also, you must always initialize a reference when you declare it and, once initialized, you cannot reassign it.&lt;/p&gt;
&lt;p&gt;Using an example from &lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/references/&quot;&gt;Learn C++&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; value1&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; value2&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;value1&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// okay, ref is now an alias for value1&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    ref &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// assigns 6 (the value of value2) to value1 -- does NOT change the reference!&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;passing-arguments-to-a-function&quot;&gt;Passing arguments to a function &lt;a class=&quot;direct-link&quot; href=&quot;#passing-arguments-to-a-function&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are three ways of passing arguments to C++ functions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pass-by-Value&lt;/li&gt;
&lt;li&gt;Pass-by-Reference&lt;/li&gt;
&lt;li&gt;Pass-by-Address&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To show these methods, I&#39;ll use a simple &lt;code&gt;Book&lt;/code&gt; class, which takes a title in its constructor and has functions to access the book title:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;string&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        d_title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string &lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; d_title&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string newTitle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        d_title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newTitle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string d_title&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;pass-by-value&quot;&gt;Pass-by-Value &lt;a class=&quot;direct-link&quot; href=&quot;#pass-by-value&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When you pass an argument by value, the function simply takes a copy of the argument. This means if you modify the parameter in the function, it will leave the original argument unchanged. For example:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;string&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;passValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Book b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;The Thing Around Your Neck&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    Book b1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Americanah&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b1 has the title: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; b1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;passValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;After calling passValue(), b1 has the title: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; b1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;passValue()&lt;/code&gt; takes a copy of &lt;code&gt;b1&lt;/code&gt; and modifies the copy, resulting in the output:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;b1 has the title: Americanah&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;After calling passValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, b1 has the title: Americanah&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use pass-by-value for simple data types like &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;float&lt;/code&gt;, where the cost of copying the argument is low.&lt;/p&gt;
&lt;h4 id=&quot;pass-by-reference&quot;&gt;Pass-by-Reference &lt;a class=&quot;direct-link&quot; href=&quot;#pass-by-reference&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;With pass-by-reference, you are using the parameter as an alias to the argument variable. This means you can change the value of the variable being referenced directly. For example:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;string&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;passRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Book &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;bk&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string newTitle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Half of a Yellow Sun&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    bk &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTitle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    Book b1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Americanah&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b1 has the title: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; b1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;passRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Notice how we pass b1 as normal&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;After calling passRef(), b1 has the title: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; b1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;passRef()&lt;/code&gt;, we are not reassigning &lt;code&gt;bk&lt;/code&gt; to reference a new variable—recall that we cannot reassign references. Instead, we are changing the value of the variable it is currently referencing, &lt;code&gt;b1&lt;/code&gt; in our example.&lt;/p&gt;
&lt;p&gt;This gives the output:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;b1 has the title: Americanah&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;After calling passRef&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, b1 has the title: Half of a Yellow Sun&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;pass-by-address&quot;&gt;Pass-by-Address &lt;a class=&quot;direct-link&quot; href=&quot;#pass-by-address&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;With this approach, you are passing the address of the argument rather than the argument value itself. You use a pointer as the function parameter to store the address, and can dereference the pointer to access the value at the address.&lt;/p&gt;
&lt;p&gt;When you pass-by-address, the compiler actually passes the address by value, i.e., the function only gets a copy of the address. This means if you reassign the parameter in the function, you are only telling it to point to a new memory address.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;string&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;passAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Book &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;bk&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    bk &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Half of a Yellow Sun&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// bk now points to a new address&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    Book b1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Americanah&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b1 has the title: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; b1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;passAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;b1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;After calling passAddress(), b1 has the title: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; b1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which produces:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;b1 has the title: Americanah&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;After calling passAddress&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, b1 has the title: Americanah&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example, we are assigning a new address to &lt;code&gt;bk&lt;/code&gt; in &lt;code&gt;passAddress&lt;/code&gt; and leaving the value of &lt;code&gt;b1&lt;/code&gt; untouched.&lt;/p&gt;
&lt;p&gt;If we want to access the members of &lt;code&gt;b1&lt;/code&gt; in &lt;code&gt;passAddress()&lt;/code&gt;, we can use the dereference(&lt;code&gt;*&lt;/code&gt;) operator or the shorthand arrow (&lt;code&gt;-&amp;gt;&lt;/code&gt;) operator:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;passAddressAlt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Book &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;bk&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bk has the title: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;bk&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bk has the title: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; bk&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h5 id=&quot;an-aside&quot;&gt;An aside &lt;a class=&quot;direct-link&quot; href=&quot;#an-aside&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;When you create an object in C++ using the &lt;code&gt;new&lt;/code&gt; keyword, you must explicitly deallocate it from the heap using the &lt;code&gt;delete&lt;/code&gt; keyword to prevent a memory leak.&lt;/p&gt;
&lt;p&gt;This means the &lt;code&gt;passAddress()&lt;/code&gt; function above will cause a memory leak, since there is no way to access the new object created in the function after it returns, but I&#39;m keeping things simple for demonstration.&lt;/p&gt;
&lt;p&gt;Modern C++ introduced &lt;a href=&quot;https://en.cppreference.com/book/intro/smart_pointers&quot;&gt;smart pointers&lt;/a&gt;, which can auto-delete objects not being referenced, but I&#39;m leaving that out in this post.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;pass-by-reference-or-pass-by-address&quot;&gt;Pass-by-Reference or Pass-by-Address &lt;a class=&quot;direct-link&quot; href=&quot;#pass-by-reference-or-pass-by-address&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For complex data types where copying may be too expensive, the choice between pass-by-reference and pass-by-address is not straightforward.&lt;/p&gt;
&lt;p&gt;Learn C++, for example, recommends sticking with pass-by-reference, but a more common recommendation I have found is to use pass-by-address when you are modifying the parameter in the function and pass by &lt;code&gt;const&lt;/code&gt; reference otherwise.&lt;/p&gt;
&lt;p&gt;By using the latter approach, you are making it more explicit that an argument may be modified.&lt;/p&gt;
&lt;h4 id=&quot;java-is-pass-by-value&quot;&gt;Java is Pass-by-Value &lt;a class=&quot;direct-link&quot; href=&quot;#java-is-pass-by-value&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In Java, you only pass arguments by value, but this has the same semantics as pass-by-address in C++.&lt;/p&gt;
&lt;p&gt;I mentioned earlier that with pass-by-address, you are actually passing the argument&#39;s address by value, i.e. passing a copy of the address. This is the same in Java: you are passing a copy of a variable&#39;s address.&lt;/p&gt;
&lt;p&gt;If you have the following method signature in your Java program:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;passAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is equivalent to the below in C++:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;passAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Book&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which is why if you implement the Java method like below, it will leave the original argument unchanged.&lt;/p&gt;
&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;passAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Purple Hibiscus&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/introduction-to-pointers/&quot;&gt;Introduction to pointers&lt;/a&gt; - Learn C++&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/references/&quot;&gt;Reference variables&lt;/a&gt; - Learn C++&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/passing-arguments-by-value/&quot;&gt;Passing arguments by value&lt;/a&gt; - Learn C++&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/passing-arguments-by-reference/&quot;&gt;Passing arguments by reference&lt;/a&gt; - Learn C++&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/passing-arguments-by-address/&quot;&gt;Passing arguments by address&lt;/a&gt; - Learn C++&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.javadude.com/articles/passbyvalue.htm&quot;&gt;Java is Pass-by-Value, Dammit!&lt;/a&gt; - &lt;a href=&quot;http://javadude.com/&quot;&gt;Javadude.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/40523/5430313&quot;&gt;Is Java &amp;quot;pass-by-reference&amp;quot; or &amp;quot;pass-by-value&amp;quot;&lt;/a&gt; - Stack Overflow answer&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Learning C++ from Java - Header files</title>
		<link href="https://timilearning.com/posts/learning-cpp/header-files/"/>
		<updated>2021-12-04T18:45:32-00:00</updated>
		<id>https://timilearning.com/posts/learning-cpp/header-files/</id>
		<content type="html">&lt;p&gt;This is a continuation of the series on C++ topics that I&#39;ve found interesting, coming from a Java background. You can read the first post &lt;a href=&quot;https://timilearning.com/posts/learning-cpp/introduction/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;ll start this post by describing forward declarations in C++ before talking about header files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#forward-declarations&quot;&gt;Forward declarations&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#working-with-multiple-files&quot;&gt;Working with multiple files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#multiple-declarations%2C-single-definition&quot;&gt;Multiple declarations, single definition&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#header-files&quot;&gt;Header files&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#header-guards&quot;&gt;Header guards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#header-files-and-linkage&quot;&gt;Header files and linkage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Say we write the program below to print the elements in a list:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; sizes&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sizes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Your integer list contains the numbers: &quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple program will not compile. My compiler produces the error:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;‘print’ was not declared &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; this scope&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; did you mean ‘printf’?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because the compiler, which parses code from the top down, needs to know about an identifier before it encounters the identifier&#39;s usage.&lt;/p&gt;
&lt;p&gt;In the above example, the compiler encounters &lt;code&gt;print()&lt;/code&gt;&#39;s usage in &lt;code&gt;main()&lt;/code&gt; before its declaration (and definition), and does not know what &lt;code&gt;print()&lt;/code&gt; is yet.&lt;/p&gt;
&lt;p&gt;There are two ways to fix this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reorder the function definitions so that &lt;code&gt;print()&lt;/code&gt; comes before &lt;code&gt;main()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Use a forward declaration, which I&#39;ll focus on.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;forward-declarations&quot;&gt;Forward declarations &lt;a class=&quot;direct-link&quot; href=&quot;#forward-declarations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Quoting &lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/forward-declarations/&quot;&gt;Learn C++&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A forward declaration allows us to tell the compiler about the existence of an identifier &lt;em&gt;before&lt;/em&gt; actually defining the identifier.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We forward declare a function by specifying its &lt;em&gt;prototype&lt;/em&gt;, which comprises the function&#39;s name, return type, and parameters. We can also forward declare variables and user-defined types.&lt;/p&gt;
&lt;p&gt;Rewriting the previous example to use a forward declaration:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// forward declaration of print()&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; sizes&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sizes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Your integer list contains the numbers: &quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler now knows about &lt;code&gt;print()&lt;/code&gt; before its usage in the &lt;code&gt;main()&lt;/code&gt; function, and all is perfect.&lt;/p&gt;
&lt;h4 id=&quot;working-with-multiple-files&quot;&gt;Working with multiple files &lt;a class=&quot;direct-link&quot; href=&quot;#working-with-multiple-files&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Forward declaration also applies when working with multiple files. We can split the previous example into two files:&lt;/p&gt;
&lt;p&gt;print.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Your integer list contains the numbers: &quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And main.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; sizes&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sizes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly, this program will not compile unless you forward declare &lt;code&gt;print()&lt;/code&gt; in main.cpp as before:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; sizes&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sizes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;multiple-declarations%2C-single-definition&quot;&gt;Multiple declarations, single definition &lt;a class=&quot;direct-link&quot; href=&quot;#multiple-declarations%2C-single-definition&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You can declare an identifier in different files across your program, but it can only have one definition. This is the &lt;a href=&quot;https://en.cppreference.com/w/cpp/language/definition&quot;&gt;&#39;One Definition Rule&#39;&lt;/a&gt; in C++.&lt;/p&gt;
&lt;p&gt;If you declare an identifier in a file but don&#39;t define it anywhere in your program, the compiler will compile the file, but the &lt;a href=&quot;https://timilearning.com/posts/learning-cpp/introduction/#linking&quot;&gt;linker&lt;/a&gt; will fail. For example, after removing the &lt;code&gt;print()&lt;/code&gt; definition in print.cpp above and attempting to build the program, I got the error:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;undefined reference to `print&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std::vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;int&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;header-files&quot;&gt;Header files &lt;a class=&quot;direct-link&quot; href=&quot;#header-files&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Extending the previous example, let&#39;s say we don&#39;t just want to &lt;code&gt;print()&lt;/code&gt; an integer list, but we also want to assign it a score based on how many even numbers it contains.&lt;/p&gt;
&lt;p&gt;We can rename print.cpp to container.cpp with the following content:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evenScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; evenCount&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;listItem &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;            evenCount&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; evenCount &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Your integer list contains the numbers: &quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then forward declare both functions in main.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evenScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; sizes&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sizes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The evenScore of the container with sizes is: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evenScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sizes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works fine, but imagine how boring it would be to forward declare all the functions we need if container.cpp had ten functions.&lt;/p&gt;
&lt;p&gt;Thankfully, C++ has header files which simplify this process. Header files have a &lt;code&gt;.h&lt;/code&gt; or &lt;code&gt;.hpp&lt;/code&gt; extension and you can declare all identifiers in a header file.&lt;/p&gt;
&lt;p&gt;We can extend our running example by splitting container.cpp into a header and source file:&lt;/p&gt;
&lt;p&gt;container.h:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evenScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use container.h by &lt;code&gt;#include&lt;/code&gt;-ing it in the source files:&lt;/p&gt;
&lt;p&gt;container.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;container.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evenScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; evenCount&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;listItem &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;            evenCount&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;evenCount &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Your list has the following content: &quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;main.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;container.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; sizes&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sizes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The evenScore of the container with sizes is: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evenScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sizes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;#include&lt;/code&gt; is a preprocessor directive that tells the &lt;a href=&quot;https://timilearning.com/posts/learning-cpp/introduction/#preprocessing&quot;&gt;preprocessor&lt;/a&gt; to paste the content of another file into the current file.  In our example, main.cpp and container.cpp will contain the declarations in container.h after the preprocessor runs.&lt;/p&gt;
&lt;p&gt;Note that I didn&#39;t need to include container.h in container.cpp, but I have seen it recommended that you include a header file in its matching source file and you can find an excellent demonstration of why &lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/header-files/#comment-398571&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;header-guards&quot;&gt;Header guards &lt;a class=&quot;direct-link&quot; href=&quot;#header-guards&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Say just for fun, we are writing a program to compare Tidal and Spotify with the following rubric:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Assign a score to each platform based on the average length of the top K most played songs on the site, where K is user-provided and must be at least 2.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We can introduce a new function in container.h to calculate the average value and define a global variable for the minimum value of K.&lt;/p&gt;
&lt;h5 id=&quot;container&quot;&gt;Container &lt;!-- omit in toc --&gt; &lt;a class=&quot;direct-link&quot; href=&quot;#container&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;container.h:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; minimumSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;container.cpp&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;container.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        total &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; listItem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Your list has the following content: &quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; listItem &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding new platform-specific header and source files:&lt;/p&gt;
&lt;h5 id=&quot;spotify&quot;&gt;Spotify &lt;!-- omit in toc --&gt; &lt;a class=&quot;direct-link&quot; href=&quot;#spotify&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;spotify.h:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;container.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; Spotify&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculateTopKScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;spotify.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spotify.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; Spotify&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculateTopKScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; minimumSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;//Assuming that k = 4 and after getting the data from Spotify, we have this list:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; songLengthsInSeconds&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.49&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5.53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.46&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.35&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;songLengthsInSeconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;tidal&quot;&gt;Tidal &lt;!-- omit in toc --&gt; &lt;a class=&quot;direct-link&quot; href=&quot;#tidal&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;tidal.h:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;container.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; Tidal&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculateTopKScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tidal.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tidal.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; Tidal&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculateTopKScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; minimumSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;//Assuming that k = 4 and after getting the data from Tidal, we have this list:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; songLengthsInSeconds&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.06&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6.44&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.07&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;songLengthsInSeconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these in place, we can write our main.cpp as:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;spotify.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tidal.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; spotifyScore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calculateTopKScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; tidalScore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tidal&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calculateTopKScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The top &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; Spotify songs have an average length of &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; spotifyScore&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;              &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; minutes, while Tidal songs have an average length of &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; tidalScore &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; minutes. \n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can you tell why this program will not compile?&lt;/p&gt;
&lt;p&gt;.&lt;br&gt;
.&lt;br&gt;
.&lt;/p&gt;
&lt;p&gt;I get the error below which says &lt;code&gt;minimumSize&lt;/code&gt; is redefined.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;In &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; included from tidal.h:1,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;                 from main.cpp:3:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;container.h:4:11: error: redefinition of ‘const int minimumSize’&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; const int minimumSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;           ^~~~~~~~~~~&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;In &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; included from spotify.h:1,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;                 from main.cpp:2:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;container.h:4:11: note: ‘const int minimumSize’ previously defined here&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; const int minimumSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;           ^~~~~~~~~~~&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because when the preprocessor runs, it &lt;code&gt;#include&lt;/code&gt;s the content of the spotify.h and tidal.h into main.cpp, and both header files &lt;code&gt;#include&lt;/code&gt; container.h.&lt;/p&gt;
&lt;p&gt;Since container.h contains a definition of &lt;code&gt;minimumSize&lt;/code&gt;, main.cpp will have two definitions of &lt;code&gt;minimumSize&lt;/code&gt; after the preprocessor completes, which violates the One Definition Rule.&lt;/p&gt;
&lt;p&gt;But if you rewrite container.h so that it contains the below, the program will compile fine.&lt;/p&gt;
&lt;p&gt;container.h:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;ifndef&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;CONTAINER_H &lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// Note that CONTAINER__H can be replaced with any unique name.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;CONTAINER_H&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; minimumSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The difference is container.h now contains a &lt;em&gt;header guard&lt;/em&gt;, which tells the preprocessor to first check if &lt;code&gt;CONTAINER_H&lt;/code&gt; is not defined in the current &lt;a href=&quot;https://timilearning.com/posts/learning-cpp/introduction/#preprocessing&quot;&gt;translation unit&lt;/a&gt; (&lt;code&gt;#ifndef&lt;/code&gt;), and only then should it define it and include the content of the header file.&lt;/p&gt;
&lt;p&gt;In main.cpp, when the preprocessor is processing container.h included in tidal.h, it will see that it has already defined &lt;code&gt;CONTAINER_H&lt;/code&gt; in the translation unit from when it processed spotify.h, and will not (re)include container.h&#39;s content.&lt;/p&gt;
&lt;p&gt;This simple example involves a variable, but header files may contain function definitions too and header guards help prevent multiple definitions of the functions.&lt;/p&gt;
&lt;p&gt;In summary, header guards prevent a translation unit from having multiple definitions of an identifier.&lt;/p&gt;
&lt;h4 id=&quot;header-files-and-linkage&quot;&gt;Header files and linkage &lt;a class=&quot;direct-link&quot; href=&quot;#header-files-and-linkage&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I wrote about linkage in the &lt;a href=&quot;https://timilearning.com/posts/learning-cpp/introduction/#linkage&quot;&gt;previous post&lt;/a&gt; and will give another example here.&lt;/p&gt;
&lt;p&gt;We can introduce a linking error in our music streaming application by adding the &lt;code&gt;extern&lt;/code&gt; keyword before the variable in container.h:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;ifndef&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;CONTAINER_H &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;CONTAINER_H&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; minimumSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I get an error saying &lt;code&gt;multiple definition of &#39;minimumSize&#39;&lt;/code&gt;. This is because the &lt;code&gt;minimumSize&lt;/code&gt; variable initially has internal linkage since it&#39;s a &lt;code&gt;const&lt;/code&gt; global variable, meaning each translation unit will get its copy of the variable.&lt;/p&gt;
&lt;p&gt;But by making it &lt;code&gt;extern&lt;/code&gt; and giving it external linkage, we&#39;re telling the linker that each usage of the variable refers to the same instance, and since the variable definition will appear in spotify.cpp and tidal.cpp, we are violating the One Definition Rule.&lt;/p&gt;
&lt;p&gt;We can maintain the external linkage property and fix the error by replacing the definition in the header file with a declaration, and defining the variable in only one of the source files we include the header in, as shown below:&lt;/p&gt;
&lt;p&gt;container.h:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;ifndef&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;CONTAINER_H&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;CONTAINER_H&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;vector&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; minimumSize&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tidal.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tidal.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; minimumSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; Tidal&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculateTopKScore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; minimumSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; songLengthsInSeconds&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.06&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6.44&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.07&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;average&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;songLengthsInSeconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is now only one definition of &lt;code&gt;minimumSize&lt;/code&gt; in the program and because it is &lt;code&gt;extern&lt;/code&gt;, any usage of the variable will refer to the value we defined.&lt;/p&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/333889/why-have-header-files-and-cpp-files&quot;&gt;Why have header files and .cpp files?&lt;/a&gt; - Stackoverflow question with useful answers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/forward-declarations/&quot;&gt;Forward declarations&lt;/a&gt; - Learn C++.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/programs-with-multiple-code-files/&quot; title=&quot;https://www.learncpp.com/cpp-tutorial/programs-with-multiple-code-files/&quot;&gt;Programs with multiple code files&lt;/a&gt; - Learn C++.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/header-files/&quot; title=&quot;https://www.learncpp.com/cpp-tutorial/header-files/&quot;&gt;Header files&lt;/a&gt; - Learn C++.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Learning C++ from Java -  Building, Namespaces, Linkage, and more</title>
		<link href="https://timilearning.com/posts/learning-cpp/introduction/"/>
		<updated>2021-11-13T19:35:49-00:00</updated>
		<id>https://timilearning.com/posts/learning-cpp/introduction/</id>
		<content type="html">&lt;p&gt;I recently had to learn C++ for work and have done most of that learning so far through &lt;a href=&quot;https://www.learncpp.com/&quot;&gt;Learn C++&lt;/a&gt;. This series of posts will highlight what I have found interesting about C++, especially given my Java background.&lt;/p&gt;
&lt;p&gt;Note that these posts are not meant to be tutorials on writing C++; I recommend visiting &lt;a href=&quot;https://www.learncpp.com/&quot;&gt;Learn C++&lt;/a&gt; if you want a thorough C++ tutorial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#building-a-c%2B%2B-program&quot;&gt;Building a C++ Program&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#preprocessing&quot;&gt;Preprocessing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#compilation&quot;&gt;Compilation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linking&quot;&gt;Linking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#initializing-variables&quot;&gt;Initializing Variables&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#copy-initialization&quot;&gt;Copy Initialization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#direct-initialization&quot;&gt;Direct Initialization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#list-initialization&quot;&gt;List Initialization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#value-initialization-and-zero-initialization&quot;&gt;Value Initialization and Zero Initialization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#default-initialization&quot;&gt;Default Initialization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#namespaces&quot;&gt;Namespaces&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#namespace-aliases&quot;&gt;Namespace Aliases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linkage&quot;&gt;Linkage&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#linkage-is-not-scope&quot;&gt;Linkage is not scope&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#storage-duration&quot;&gt;Storage Duration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-%27static%27-keyword&quot;&gt;The &#39;static&#39; keyword&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;building-a-c%2B%2B-program&quot;&gt;Building a C++ Program &lt;a class=&quot;direct-link&quot; href=&quot;#building-a-c%2B%2B-program&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A C++ program comprises one or more source files and header files, and the steps to create an executable from source code are preprocessing, compilation, and linking.&lt;/p&gt;
&lt;h4 id=&quot;preprocessing&quot;&gt;Preprocessing &lt;a class=&quot;direct-link&quot; href=&quot;#preprocessing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This involves handling all  &lt;em&gt;preprocessor directives&lt;/em&gt;.  Preprocessor directives are instructions for a preprocessor that tell it to perform text manipulation tasks. An example is the &lt;code&gt;#include&lt;/code&gt; directive, which tells the preprocessor to include the contents of a header file into the current file.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p&gt;The output of the preprocessor is one or more &lt;em&gt;translation units&lt;/em&gt;.  A translation unit is a C++ source file after the preprocessor has included the contents of its header file.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;h4 id=&quot;compilation&quot;&gt;Compilation &lt;a class=&quot;direct-link&quot; href=&quot;#compilation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This is where the compiler converts each translation unit into an object file containing machine code. These object files cannot be run yet but can be stored for reuse later on.&lt;/p&gt;
&lt;h4 id=&quot;linking&quot;&gt;Linking &lt;a class=&quot;direct-link&quot; href=&quot;#linking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This is the final stage, where a &lt;em&gt;linker&lt;/em&gt; combines all the object files to form an executable program. This stage involves linking any needed library code and resolving all cross-file dependencies.&lt;/p&gt;
&lt;p&gt;C++ compilers typically come bundled with separate programs to perform all three steps.&lt;/p&gt;
&lt;h3 id=&quot;initializing-variables&quot;&gt;Initializing Variables &lt;a class=&quot;direct-link&quot; href=&quot;#initializing-variables&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are different ways to initialize a C++ variable and the difference between these methods is more significant when initializing complex types; they have a similar effect when initializing primitive types.&lt;/p&gt;
&lt;p&gt;This section will focus primarily on initializing primitive types, with a discussion on complex types to come in a later post.&lt;/p&gt;
&lt;h4 id=&quot;copy-initialization&quot;&gt;Copy Initialization &lt;a class=&quot;direct-link&quot; href=&quot;#copy-initialization&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Copy initialization takes place when you initialize a variable using an equals sign.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; width  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the program copies the value on the right-hand side of the equals into the address of the variable on the left.&lt;/p&gt;
&lt;p&gt;Copying can be an expensive operation for large objects, but it is efficient for primitive types.&lt;/p&gt;
&lt;h4 id=&quot;direct-initialization&quot;&gt;Direct Initialization &lt;a class=&quot;direct-link&quot; href=&quot;#direct-initialization&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Direct initialization is initialization using non-empty parentheses.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Direct initialization and copy initialization do the same thing for primitive types, but have different behaviours for class objects, which you can learn more about &lt;a href=&quot;https://stackoverflow.com/a/1051468/5430313&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;list-initialization&quot;&gt;List Initialization &lt;a class=&quot;direct-link&quot; href=&quot;#list-initialization&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;List initialization (also called uniform initialization or brace initialization) can occur in both direct initialization and copy initialization contexts as &lt;em&gt;direct-list-initialization&lt;/em&gt; and &lt;em&gt;copy-list-initialization&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Direct-list-initialization.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Copy-list-initialization.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One difference between using list or brace initialization and the other forms of initialization for primitive types is that list initialization is stricter. Quoting &lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/variable-assignment-and-initialization/&quot;&gt;Learn C++&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Brace initialization has the added benefit of disallowing “narrowing” conversions. This means that if you try to use brace initialization to initialize a variable with a value it can not safely hold, the compiler will throw a warning or an error.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So if you attempt to initialize a variable like &lt;code&gt;int speed{43.1}&lt;/code&gt;, list initialization will throw an error, while copy and direct initialization will simply drop the fractional part.&lt;/p&gt;
&lt;p&gt;Unlike direct and copy initialization, you can also use list initialization to populate containers (&lt;em&gt;collections&lt;/em&gt; in Java) with elements during initialization like:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; widths &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;vector&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; heights&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;value-initialization-and-zero-initialization&quot;&gt;Value Initialization and Zero Initialization &lt;a class=&quot;direct-link&quot; href=&quot;#value-initialization-and-zero-initialization&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Value initialization takes place when you initialize a variable with empty braces. For primitive types, this will initialize the variable with a zero value equivalent for the type.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// The value is 0.0.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// The value is 0.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;default-initialization&quot;&gt;Default Initialization &lt;a class=&quot;direct-link&quot; href=&quot;#default-initialization&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Default initialization takes place when you declare a variable with no initializer.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// The value is undefined.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The C++ standard does not define what the values of &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; in the above example should be; it leaves it up to the compiler implementation. In many compilers, the values will be whatever garbage is in the memory address allocated for the variables.&lt;/p&gt;
&lt;h3 id=&quot;namespaces&quot;&gt;Namespaces &lt;a class=&quot;direct-link&quot; href=&quot;#namespaces&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Namespaces in C++ help to prevent naming collisions by providing scope to identifiers. They are similar to Java packages in that they both prevent naming collisions, but you can nest namespaces in C++ and can declare multiple namespaces in a single C++ file, among &lt;a href=&quot;https://stackoverflow.com/a/41504984/5430313&quot;&gt;other&lt;/a&gt; differences.&lt;/p&gt;
&lt;p&gt;For example, if you define a namespace like&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; Math &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; x&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can call the &lt;code&gt;add&lt;/code&gt; function by prefixing it with its namespace &lt;code&gt;Math::add(1,2)&lt;/code&gt;, and the compiler won&#39;t confuse it with an &lt;code&gt;add&lt;/code&gt; function defined in a separate namespace.&lt;/p&gt;
&lt;p&gt;You can also declare functions and other identifiers in C++ outside an explicit namespace. Such identifiers become part of the global namespace of your program. Using the above example, if you define an &lt;code&gt;add&lt;/code&gt; function in the global namespace, you can refer to it as &lt;code&gt;::add(3,4)&lt;/code&gt; or just &lt;code&gt;add(3,4)&lt;/code&gt; if there is no conflicting &lt;code&gt;add()&lt;/code&gt; function defined.&lt;/p&gt;
&lt;p&gt;C++ also lets you declare the same namespace in multiple files, provided there&#39;s only one definition of each identifier in the namespace. The compiler will group the declarations together in the linking phase.&lt;/p&gt;
&lt;h4 id=&quot;namespace-aliases&quot;&gt;Namespace Aliases &lt;a class=&quot;direct-link&quot; href=&quot;#namespace-aliases&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I mentioned earlier that you can nest namespaces in C++, so it&#39;s not uncommon to see code like:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;School&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Subject&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Maths&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Numbers&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Arithmetic&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;34&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, I exaggerate and I hope it is uncommon, but my point is namespaces can be deeply nested and C++ provides a convenient way to refer to nested namespaces through &lt;em&gt;namespace aliases.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can define a namespace alias for the above example like:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; MathsArithmetic &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; School&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Subject&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Maths&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Numbers&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Arithmetic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And invoke the add function using:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MathsArithmetic&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;345&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;cout&lt;/code&gt; is a function that belongs to the &lt;code&gt;std&lt;/code&gt; namespace.&lt;/p&gt;
&lt;h3 id=&quot;linkage&quot;&gt;Linkage &lt;a class=&quot;direct-link&quot; href=&quot;#linkage&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Linkage is a property of an identifier that specifies whether it is visible outside its translation unit.&lt;/p&gt;
&lt;p&gt;If an identifier has &lt;em&gt;internal linkage&lt;/em&gt;, it is only visible to the linker in its translation unit. &lt;code&gt;const&lt;/code&gt; global variables have internal linkage by default.&lt;/p&gt;
&lt;p&gt;Quoting &lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/internal-linkage/&quot;&gt;Learn C++&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An identifier’s linkage determines whether other declarations of that name refer to the same object or not...This means that if two files have identically named identifiers with internal linkage, those identifiers will be treated as independent.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For example, if I have two source files, &lt;code&gt;fruit.cpp&lt;/code&gt; and &lt;code&gt;vegetable.cpp&lt;/code&gt; containing the following definitions:&lt;/p&gt;
&lt;p&gt;fruit.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;string&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string favouriteFruit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;carrot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; favouriteFruit &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;vegetable.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;string&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string favouriteFruit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;carrot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printFruit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; favouriteFruit &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because &lt;code&gt;const&lt;/code&gt; global variables have internal linkage, there will be no issues here during the linking phase. Each translation unit will get its own copy of &lt;code&gt;favouriteFruit&lt;/code&gt;, avoiding any conflicts.&lt;/p&gt;
&lt;p&gt;To make an identifier have external linkage, use the &lt;code&gt;extern&lt;/code&gt; keyword.&lt;/p&gt;
&lt;p&gt;An identifier with &lt;em&gt;external linkage&lt;/em&gt; is visible from any translation unit in the program. All functions and non-const global variables implicitly have external linkage.&lt;/p&gt;
&lt;p&gt;Using the previous example,&lt;/p&gt;
&lt;p&gt;fruit.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;string&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string favouriteFruit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;carrot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; favouriteFruit &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;vegetable.cpp:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;string&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;iostream&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string favouriteFruit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;carrot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printFruit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;cout &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; favouriteFruit &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, both definitions of &lt;code&gt;favouriteFruit&lt;/code&gt; have external linkage and will cause a linking error because of the multiple definitions available to the linker, in violation of the &lt;a href=&quot;https://en.cppreference.com/w/cpp/language/definition&quot;&gt;one definition rule&lt;/a&gt;. Use the &lt;code&gt;static&lt;/code&gt; keyword to make an identifier with external linkage have internal linkage.&lt;/p&gt;
&lt;p&gt;Local variables have no linkage, which means all declarations of local variables with the same name refer to different objects.&lt;/p&gt;
&lt;p&gt;The next post on header files will include a broader discussion on linkage with more usage examples, and I recommend reading &lt;a href=&quot;http://www.goldsborough.me/c/c++/linker/2016/03/30/19-34-25-internal_and_external_linkage_in_c++/&quot;&gt;this&lt;/a&gt; article if you want to learn more before then.&lt;/p&gt;
&lt;h4 id=&quot;linkage-is-not-scope&quot;&gt;Linkage is not scope &lt;a class=&quot;direct-link&quot; href=&quot;#linkage-is-not-scope&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;While scope and linkage may seem similar, they mean different things. An identifier&#39;s scope defines where it is visible within its translation unit, while linkage determines whether declarations of the same name in other translation units refer to the same identifier or not.&lt;/p&gt;
&lt;p&gt;An &lt;a href=&quot;https://www.ibm.com/docs/en/i/7.4?topic=reference-scope-linkage&quot;&gt;article&lt;/a&gt; from IBM also distinguishes them like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Scope and linkage are distinguishable in that scope is for the benefit of the compiler, whereas linkage is for the benefit of the linker. During the translation of a source file to object code, the compiler keeps track of the identifiers that have external linkage and eventually stores them in a table within the object file. The linker is thereby able to determine which names have external linkage, but is unaware of those with internal or no linkage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;storage-duration&quot;&gt;Storage Duration &lt;a class=&quot;direct-link&quot; href=&quot;#storage-duration&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All variables in a C++ program have a &lt;em&gt;storage duration,&lt;/em&gt; which determines the rules for when the program creates and destroys them. Two forms of this duration are &lt;em&gt;automatic&lt;/em&gt; and &lt;em&gt;static&lt;/em&gt; duration.&lt;/p&gt;
&lt;p&gt;When a variable has automatic duration, it means the program allocates its storage at the point of the variable&#39;s definition and deallocates it when the program exits the enclosing code block. Local variables have automatic duration by default.&lt;/p&gt;
&lt;p&gt;For a variable with static duration, its storage is allocated when the program starts and deallocated when the program ends. Only one instance of a variable with static duration exists. Global variables have static duration.&lt;/p&gt;
&lt;p&gt;You can also use the &lt;code&gt;static&lt;/code&gt; keyword to change the duration of a local variable to static duration. When you make a local variable static, the program only initializes the variable the first time it encounters the initialization. Subsequent calls to its enclosing function will reuse the already initialized instance.&lt;/p&gt;
&lt;p&gt;Variables can also have &lt;em&gt;dynamic duration&lt;/em&gt;, which means the program creates and destroys them on programmer request, as in dynamically allocated variables created with the &lt;code&gt;new&lt;/code&gt; keyword.&lt;/p&gt;
&lt;h3 id=&quot;the-&#39;static&#39;-keyword&quot;&gt;The &#39;static&#39; keyword &lt;a class=&quot;direct-link&quot; href=&quot;#the-&#39;static&#39;-keyword&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So far, I&#39;ve mentioned the &lt;code&gt;static&lt;/code&gt; keyword as both a way to denote internal linkage and static storage duration. This might be confusing, so note that the &lt;code&gt;static&lt;/code&gt; keyword in C++ can appear in three contexts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When used in declaring a &lt;em&gt;variable&lt;/em&gt;, the &lt;code&gt;static&lt;/code&gt; keyword acts as a &lt;a href=&quot;https://en.cppreference.com/w/cpp/language/storage_duration&quot;&gt;storage class specifier&lt;/a&gt; and gives the variable both static duration and internal linkage.&lt;/li&gt;
&lt;li&gt;When used with a &lt;em&gt;function&lt;/em&gt; that&#39;s not a member of a class, the &lt;code&gt;static&lt;/code&gt; keyword gives it internal linkage.&lt;/li&gt;
&lt;li&gt;For class member variables and functions, as in Java, making them &lt;code&gt;static&lt;/code&gt; means we can use them without creating an instance of the class.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I went into this thinking C++ would be mostly like Java except with pointers and references, and I have been only been proven wrong so far. Though they bear some similarities, especially in their syntax, I&#39;ve been surprised by how different they are and I&#39;m looking forward to exploring that further in subsequent posts.&lt;/p&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/variable-assignment-and-initialization/&quot;&gt;Variable assignment and initialization&lt;/a&gt; - Learn C++.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/cpp/cpp/initializers?view=msvc-160&quot;&gt;Initializers&lt;/a&gt; - Microsoft C++ Docs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/language/initialization&quot;&gt;Initialization&lt;/a&gt; - &lt;a href=&quot;http://cppreference.com/&quot;&gt;cppreference.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/introduction-to-the-compiler-linker-and-libraries/&quot;&gt;Introduction to the compiler, linker, and libraries&lt;/a&gt; - Learn C++.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/6264256/5430313&quot;&gt;How does the compilation/linking process work?&lt;/a&gt; - From Stack Overflow.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.goldsborough.me/c/c++/linker/2016/03/30/19-34-25-internal_and_external_linkage_in_c++/&quot;&gt;Internal and External Linkage in C++&lt;/a&gt; by Peter Goldsborough.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/language/storage_duration&quot;&gt;Storage class specifiers&lt;/a&gt; - &lt;a href=&quot;http://xn--cppreference-ny9f.com&quot;&gt;cpp​reference.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 20 -  Blockstack</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-20-blockstack/"/>
		<updated>2020-12-23T12:15:46-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-20-blockstack/</id>
		<content type="html">&lt;p&gt;The final post in this lecture series is about Blockstack. Blockstack is a network for building decentralized applications based on blockchain. I find the idea of decentralized applications appealing because of its promise to give users more ownership and control of their data.&lt;/p&gt;
&lt;p&gt;Blockstack is also interesting as it&#39;s a non-cryptocurrency use of blockchain, which I covered in the &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-19-bitcoin&quot;&gt;previous post&lt;/a&gt;. I&#39;ll start this post with an overview of how a decentralized application might work, before describing Blockstack&#39;s approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#decentralization&quot;&gt;Decentralization&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#a-decentralized-architecture&quot;&gt;A decentralized architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#a-decentralized-application&quot;&gt;A decentralized application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#decentralization-can-be-painful&quot;&gt;Decentralization can be painful&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#blockstack&quot;&gt;Blockstack&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#naming&quot;&gt;Naming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-blockchain&quot;&gt;The Blockchain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-peer-network&quot;&gt;The Peer Network&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#storage&quot;&gt;Storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#putting-them-all-together&quot;&gt;Putting them all together&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;decentralization&quot;&gt;Decentralization &lt;a class=&quot;direct-link&quot; href=&quot;#decentralization&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some of the most popular applications today like Gmail, Facebook, and Twitter are run by companies which own and manage their users&#39; data and expose an interface for users to access their data. These apps are &lt;em&gt;centralized&lt;/em&gt; in that the companies that run them store and manage all the user data.&lt;/p&gt;
&lt;p&gt;While this model has been very successful for both the companies and users, it has come with its downsides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Companies can use their users&#39; data for &lt;a href=&quot;https://techcrunch.com/2019/01/29/facebook-project-atlas/&quot;&gt;nefarious&lt;/a&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/Facebook%E2%80%93Cambridge_Analytica_data_scandal&quot;&gt;reasons&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Employees of these companies can &lt;a href=&quot;https://www.theguardian.com/technology/2019/nov/06/twitter-spy-saudi-arabia-workers-charged&quot;&gt;snoop&lt;/a&gt; on private user data.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Most users have to go through the application&#39;s UI to access their data, and they can only do what the UI supports with their data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For these reasons and more, there has been a trend towards building decentralized applications which move ownership and agency of data back into users&#39; hands.&lt;/p&gt;
&lt;h3 id=&quot;a-decentralized-architecture&quot;&gt;A decentralized architecture &lt;a class=&quot;direct-link&quot; href=&quot;#a-decentralized-architecture&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In centralized applications, there is a tight coupling between the application code and the way they store data. For example, the Twitter app knows how to interact with Twitter&#39;s databases. But in a decentralized app, we can separate the app code from user data.&lt;/p&gt;
&lt;p&gt;In this architecture, we can have a storage service that is independent of the applications that interact with it. This service will store data on a per-user basis instead of a per-app basis, and each user on the network can own and control their data.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/decentralized-arch.png&quot; alt=&quot;Figure 1: A decentralized architecture.&quot;&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt; Figure 1: A decentralized architecture. &lt;/p&gt;
&lt;p&gt;In designing this architecture, the storage service must meet the following requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;General-purpose&lt;/strong&gt;. Similar to the file system on your computer, it must have an API that allows multiple applications to interact with it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud-based&lt;/strong&gt;, so users can access their data from anywhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fully controllable by a user&lt;/strong&gt;. It must support mechanisms for securing the data and controlling who can access it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Supports sharing between users&lt;/strong&gt; for apps where one user might read another user&#39;s information.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This architecture will provide the following benefits to users:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Better ownership and control over their data and how it&#39;s used.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Better security and data privacy, assuming app owners implement end-to-end encryption.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Improved ability for users to switch between similar applications, since data storage is independent of the applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;a-decentralized-application&quot;&gt;A decentralized application &lt;a class=&quot;direct-link&quot; href=&quot;#a-decentralized-application&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With this architecture, using an application like Facebook will involve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Running the Facebook app, which will have no associated servers, on your computer.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The Facebook app reading from and writing to your data store.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The Facebook application reading from your friends&#39; data stores to display their information on your feed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key thing here is that the application running on your computer contains all the logic it needs to interact directly with a general-purpose storage service, with no servers involved.&lt;/p&gt;
&lt;p&gt;This is how many downloaded applications on your computer work, in that they interact with your local file system without needing to talk to a server.&lt;/p&gt;
&lt;h3 id=&quot;decentralization-can-be-painful&quot;&gt;Decentralization can be painful &lt;a class=&quot;direct-link&quot; href=&quot;#decentralization-can-be-painful&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This decentralized architecture comes with its limitations for both users and developers. It can be significantly more challenging for developers to build decentralized apps, especially since a per-user general-purpose storage service is less flexible than a dedicated database.&lt;/p&gt;
&lt;p&gt;Users may not want to manage the security of their data, especially when it involves more complex security mechanisms. There&#39;s also the social challenge of convincing users to even consider using decentralized apps, especially since the current centralized architecture works so well.&lt;/p&gt;
&lt;h2 id=&quot;blockstack&quot;&gt;Blockstack &lt;a class=&quot;direct-link&quot; href=&quot;#blockstack&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Blockstack is an open-source approach to building a decentralized internet, using blockchain as the underlying infrastructure.  Similar to the architecture discussed so far, each user on the Blockstack network has their private data store, and applications running on the network interact with these data stores.&lt;/p&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/blockstack-2017.pdf&quot;&gt;2017 paper&lt;/a&gt; which this lecture is based on, the authors built Blockstack with three design goals:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Decentralized Naming &amp;amp; Discovery&lt;/strong&gt;: End-users should be able to (a) register and use human-readable names and (b) discover network resources mapped to human-readable names without trusting any remote parties.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Decentralized Storage&lt;/strong&gt;: End-users should be able to use decentralized storage systems where they can store their data without revealing it to any remote parties.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Comparable Performance&lt;/strong&gt;: The end-to-end performance of the new architecture (including name/resource lookups, storage access, etc.) should be comparable to the traditional internet with centralized services.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To achieve these goals, Blockstack&#39;s architecture comprises the different components shown below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/blockstack-architecture.png&quot; alt=&quot;Figure 2: Overview of the Blockstack architecture.&quot;&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt; Figure 2: Overview of the Blockstack architecture.&lt;/p&gt;
&lt;p&gt;The paper&#39;s authors further divide these components into layers. I&#39;ll describe these layers and how they all fit together soon.&lt;/p&gt;
&lt;h3 id=&quot;naming&quot;&gt;Naming &lt;a class=&quot;direct-link&quot; href=&quot;#naming&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Names are important in Blockstack for identifying users, applications, and domains.  A Blockstack name can map to a user&#39;s public key, the location of their data store, an IP address, etc.&lt;/p&gt;
&lt;p&gt;When designing Blockstack, there were three properties that the authors desired for a name:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Human-readable&lt;/strong&gt;: Instead of using a hash to identify users, Blockstack uses human-readable names to provide a good user experience.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Globally unique&lt;/strong&gt;: There should be only one owner of the name in the network.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Decentralized allocation&lt;/strong&gt;: There should be no central service in charge of allocating names.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;According to the paper, before the invention of blockchains, it was only possible to get two of these three properties at a time. This is a computer science limitation called &lt;a href=&quot;https://en.wikipedia.org/wiki/Zooko%27s_triangle&quot;&gt;Zooko&#39;s triangle&lt;/a&gt;. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Email addresses&lt;/em&gt; are unique and human-readable but not decentralized as the company you register with controls the namespace.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Public keys&lt;/em&gt; are unique and decentralized as users can generate them without a central service, but they are not human readable.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The names on your contact list are decentralized and human-readable, but not unique.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The next section will cover how a blockchain helps achieve these properties.&lt;/p&gt;
&lt;h3 id=&quot;the-blockchain&quot;&gt;The Blockchain &lt;a class=&quot;direct-link&quot; href=&quot;#the-blockchain&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Blockstack uses a blockchain layer to get all the three desired properties of names. This layer comprises two components: the &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-19-bitcoin/#the-bitcoin-blockchain&quot;&gt;Bitcoin blockchain&lt;/a&gt; and a &lt;em&gt;virtualchain&lt;/em&gt;, which work together to form the Blockchain Naming System (BNS). The BNS replaces &lt;a href=&quot;https://www.cloudflare.com/learning/dns/what-is-dns/#&quot;&gt;DNS&lt;/a&gt; in the network, except that there are no central root servers involved.&lt;/p&gt;
&lt;p&gt;Claiming a name in BNS requires Bitcoin transactions, which Blockstack embeds with information about the name. Since the Bitcoin blockchain produces an ordered chain of blocks, we can determine who claimed a name first and ensure that names are unique. Using the blockchain to claim names also means that allocation is decentralized by design.&lt;/p&gt;
&lt;p&gt;For example, if you&#39;re claiming a new Blockstack name, the associated Bitcoin transaction will contain the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Your desired name.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your public key.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The hash of a BNS &lt;em&gt;zone file&lt;/em&gt;. Blockstack creates a zone file for each name in the network and this file contains the routing information for that name, i.e., what resource the name points to.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/blockstack-blockchain.png&quot; alt=&quot;Figure 3: The blockchain layer.&quot;&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt; Figure 3: The Blockchain layer.&lt;/p&gt;
&lt;p&gt;The virtualchain component sits on the Bitcoin blockchain and parses the transaction records to create &lt;em&gt;name records&lt;/em&gt; from the information on the Blockchain. It then stores these name records in a name database, which each peer on the network has a copy of. With this, users can look up the information for a name without having to search the underlying Bitcoin blockchain.&lt;/p&gt;
&lt;h3 id=&quot;the-peer-network&quot;&gt;The Peer Network &lt;a class=&quot;direct-link&quot; href=&quot;#the-peer-network&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Blockstack uses a peer network called &lt;em&gt;Atlas&lt;/em&gt; for users to discover routing information on the network. Atlas enables Blockstack to separate the task of discovering where data is stored from the actual storage of data, allowing for multiple storage providers to coexist.&lt;/p&gt;
&lt;p&gt;Each peer stores a table of &lt;em&gt;zone records&lt;/em&gt;. A zone record contains a zone file and its hash. For each name record on the virtual chain, there is a corresponding zone record in the local database.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/blockstack-atlas.png&quot; alt=&quot;Figure 4: Peer Network.&quot;&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt; Figure 4: Peer Network.&lt;/p&gt;
&lt;p&gt;Any new peers joining the network will communicate with existing ones to get up-to-date information about the zone records.&lt;/p&gt;
&lt;h3 id=&quot;storage&quot;&gt;Storage &lt;a class=&quot;direct-link&quot; href=&quot;#storage&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The final layer is the storage layer called Gaia, which enables users to interact with existing cloud storage providers like Dropbox, Google Drive, and Amazon S3. When a user creates an identity on the blockchain, Blockstack associates that identity with a corresponding data store in Gaia.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/blockstack-storage.png&quot; alt=&quot;Figure 5: Storage.&quot;&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt; Figure 5: Storage&lt;/p&gt;
&lt;p&gt;Gaia stores data as a key-value store and users can choose what storage providers they want to use. It provides a uniform API for applications to access user data regardless of the storage provider they use.&lt;/p&gt;
&lt;p&gt;Before a user writes to their store in Gaia, they encrypt and sign the data with their cryptographic keys. Thus, even though data is stored with existing cloud storage providers, they have no visibility into the data.&lt;/p&gt;
&lt;h3 id=&quot;putting-them-all-together&quot;&gt;Putting them all together &lt;a class=&quot;direct-link&quot; href=&quot;#putting-them-all-together&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Quoting the paper, if a Blockstack application wants to look up data for a name, it works as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lookup the &lt;em&gt;name&lt;/em&gt; in the virtualchain to get the (&lt;em&gt;name, hash&lt;/em&gt;) pair.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Lookup the &lt;em&gt;hash(name)&lt;/em&gt; in the Atlas network to get the respective zone file (all peers in the Atlas network have the full replica of all zone files).&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Get the storage backend URI from the zone file and lookup the URI to connect to the storage backend.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Read the data (decrypt it if needed and if you have the access rights) and verify the respective signature or hash&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Blockstack is a system in use today, and you can learn about some decentralized apps that have been built &lt;a href=&quot;https://www.app.co/&quot;&gt;here&lt;/a&gt;. I&#39;ve also left out some details about Blockstack in this post, and I recommend reading the sites linked in the next section to learn more about its implementation. This post is mainly an exploration of how the internet could be different and perhaps better, using Blockstack as an example.&lt;/p&gt;
&lt;p&gt;But blockchain-based apps are not the only approach to building decentralized applications. &lt;a href=&quot;https://crdt.tech/&quot;&gt;CRDTs&lt;/a&gt; are another approach being actively explored today, and you can find a great overview &lt;a href=&quot;https://www.inkandswitch.com/local-first.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Overall, I&#39;m intrigued by the decentralization vision for building applications and while it&#39;s still some way off being fully realized, the ongoing research is promising.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-blockstack.txt&quot;&gt;Lecture 20: Blockstack&lt;/a&gt; - MIT 6.824 Lecture Notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/blockstack-faq.txt&quot;&gt;Blockstack FAQ&lt;/a&gt; - Additional material from 6.824.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/blockstack-2017.pdf&quot;&gt;Blockstack: A New Internet for Decentralized Applications&lt;/a&gt; - Blockstack Technical Whitepaper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.blockstack.org/how-blockstack-works&quot;&gt;How Blockstack works&lt;/a&gt; - Official Blockstack documentation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wired.com/story/the-decentralized-internet-is-here-with-some-glitches/&quot;&gt;The Decentralized Internet Is Here, With Some Glitches&lt;/a&gt; - Wired article on Blockstack by Tom Simonite.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tokeneconomy.co/breaking-down-blockstack-whitepaper-review-3c828788f3e9&quot;&gt;Breaking Down Blockstack  - Whitepaper Review&lt;/a&gt; by Nick Neuman.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.inkandswitch.com/local-first.html&quot;&gt;Local-first software&lt;/a&gt; by Ink &amp;amp; Switch.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://withblue.ink/2020/11/12/maybe-we-shouldnt-want-a-fully-decentralized-web.html&quot;&gt;Maybe we shouldn&#39;t want a fully decentralized web&lt;/a&gt; by Alessandro Segala.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 19 -  Bitcoin</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-19-bitcoin/"/>
		<updated>2020-12-11T20:55:59-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-19-bitcoin/</id>
		<content type="html">&lt;p&gt;Following the lecture on &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-18-certificate-transparency/&quot;&gt;Certificate Transparency&lt;/a&gt;, we are exploring Bitcoin, another open system comprising mutually untrustworthy components.&lt;/p&gt;
&lt;p&gt;Bitcoin is a digital currency for making online payments. I&#39;ll start this post by making a case for digital currencies, before describing Bitcoin and how it solves the double-spending problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#digital-currencies&quot;&gt;Digital Currencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#bitcoin&quot;&gt;Bitcoin&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#limitations-of-the-model-so-far&quot;&gt;Limitations of the model so far&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#an-attacker-can-steal-a-private-key&quot;&gt;An attacker can steal a private key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-current-owner-can-double-spend&quot;&gt;The current owner can double-spend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#addressing-double-spend&quot;&gt;Addressing double-spend&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#publishing-to-a-log&quot;&gt;Publishing to a log&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-bitcoin-blockchain&quot;&gt;The Bitcoin blockchain&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#adding-a-new-block&quot;&gt;Adding a new block&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#validation-checks&quot;&gt;Validation Checks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#temporary-double-spending-is-possible&quot;&gt;Temporary double-spending is possible&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#scenario-%231&quot;&gt;Scenario #1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#scenario-%232&quot;&gt;Scenario #2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#peers-will-abandon-the-shorter-branch&quot;&gt;Peers will abandon the shorter branch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#faq&quot;&gt;FAQ&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#where-are-new-coins-from%3F&quot;&gt;Where are new coins from?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#can-an-attacker-change-an-existing-block-in-the-middle-of-the-blockchain%3F&quot;&gt;Can an attacker change an existing block in the middle of the blockchain?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#can-an-attacker-start-a-fork-from-an-old-block%3F&quot;&gt;Can an attacker start a fork from an old block?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#resources&quot;&gt;Resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;digital-currencies&quot;&gt;Digital Currencies &lt;a class=&quot;direct-link&quot; href=&quot;#digital-currencies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Majority of e-commerce activities today rely on trusted third parties like banks and other financial institutions to process payments. These third parties offer some protection from fraud at the cost of increased transaction fees.&lt;/p&gt;
&lt;p&gt;But with digital currencies like Bitcoin, two people can make payments directly without a trusted third party involved. These payments rely on other methods to prevent fraud, such as cryptographic proofs. Digital currencies offer several advantages, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lower transaction fees&lt;/strong&gt; from not needing a third party involved.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fewer risks for merchants&lt;/strong&gt;: Transactions are irreversible, which protects merchants from losses caused by fraud or fraudulent chargebacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payment freedom&lt;/strong&gt;: You can make and receive payments from anywhere in the world.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Digital currencies, however, come with their technical and social challenges. One major technical challenge is in solving the double-spending problem, i.e., &lt;em&gt;how does one prevent a digital coin from being spent more than once&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;With traditional currencies, we can rely on banks to prevent double-spending, but different digital currencies have their unique ways of tackling this problem.&lt;/p&gt;
&lt;p&gt;This post will largely focus on Bitcoin&#39;s approach.&lt;/p&gt;
&lt;h2 id=&quot;bitcoin&quot;&gt;Bitcoin &lt;a class=&quot;direct-link&quot; href=&quot;#bitcoin&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bitcoin is a decentralized digital currency for making payments. &lt;em&gt;Decentralized&lt;/em&gt; here means there is no single authority or entity like a central bank on the Bitcoin network. The network is run by many &lt;em&gt;peers&lt;/em&gt;, which are computers that collaborate to make any necessary decisions. Anyone can add a peer to the network.&lt;/p&gt;
&lt;p&gt;The Bitcoin network comprises coins, each owned by someone. A coin is a chain of &lt;em&gt;transaction records&lt;/em&gt;, where each record represents each time an owner transferred the coin to a new owner as payment. The latest transaction record in the chain shows the coin&#39;s current owner.&lt;/p&gt;
&lt;p&gt;Each coin owner has a public/private key pair which the network uses to verify the integrity of transactions. I&#39;ll go over how that works soon, but you can read the &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-18-certificate-transparency/&quot;&gt;previous post on Certificate Transparency&lt;/a&gt; or &lt;a href=&quot;https://www.cloudflare.com/learning/ssl/how-does-public-key-encryption-work/&quot;&gt;this article on public-key cryptography&lt;/a&gt; for more detail.&lt;/p&gt;
&lt;p&gt;When the current owner of a coin wants to transfer the coin to a new owner, they create a transaction record which contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The public key of the coin&#39;s new owner.&lt;/li&gt;
&lt;li&gt;A hash of the previous transaction record in the chain.&lt;/li&gt;
&lt;li&gt;A signature of the above hash signed with the current owner&#39;s private key.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This information allows the new owner to verify that it received the coin from the right owner. To illustrate this, if a user Y owns a coin that they received from user X, the latest transaction record in the coin will look like:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/transaction-record-small.png&quot; alt=&quot;Figure 1: A sample transaction record.&quot;&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt; Figure 1: Let the latest transaction be T&lt;sub&gt;1&lt;/sub&gt;, the record will then contain the hash for T&lt;sub&gt;0&lt;/sub&gt;. &lt;/p&gt;
&lt;p&gt;If user Y then transfers the same coin to another user Z in a new transaction, T&lt;sub&gt;2&lt;/sub&gt;, the coin will have a chain which now includes a new transaction record:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/transaction-small.png&quot; alt=&quot;Figure 2: User Y transferring a coin to user Z&quot;&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt; Figure 2: User Y transferring a coin to user Z &lt;/p&gt;
&lt;p&gt;User Z will verify that the coin actually belongs to user Y by checking if the public key in T&lt;sub&gt;1&lt;/sub&gt; matches the private key signature in T&lt;sub&gt;2&lt;/sub&gt;.&lt;/p&gt;
&lt;h3 id=&quot;limitations-of-the-model-so-far&quot;&gt;Limitations of the model so far &lt;a class=&quot;direct-link&quot; href=&quot;#limitations-of-the-model-so-far&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;an-attacker-can-steal-a-private-key&quot;&gt;An attacker can steal a private key &lt;a class=&quot;direct-link&quot; href=&quot;#an-attacker-can-steal-a-private-key&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As shown in Figure 2, the current owner of a coin uses their private key to sign the next transaction. If an attacker steals the private key from the owner&#39;s computer, they can spend the coin. This is a &lt;a href=&quot;https://www.qredo.com/blog/proofofkeys-7-ways-private-keys-have-been-compromised-and-how-you-can-protect-yourself&quot;&gt;real possibility&lt;/a&gt; and is a hard problem to solve.&lt;/p&gt;
&lt;h4 id=&quot;the-current-owner-can-double-spend&quot;&gt;The current owner can double-spend &lt;a class=&quot;direct-link&quot; href=&quot;#the-current-owner-can-double-spend&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In the previous example, there&#39;s nothing stopping user Y from spending the same coin for both user Z and another user Q. User Y can create two transactions with the same coin using the hash of the previous record.&lt;/p&gt;
&lt;p&gt;Trusted third parties like banks shine here since they can protect a payee from a double-spending payer. But if Bitcoin is to operate without a third party, a payee needs to know that the previous owner of a coin did not sign any earlier transactions.&lt;/p&gt;
&lt;h3 id=&quot;addressing-double-spend&quot;&gt;Addressing double-spend &lt;a class=&quot;direct-link&quot; href=&quot;#addressing-double-spend&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Similar to Certificate Transparency, we could introduce a log which all peers must publish transactions to, ensuring that all the transactions in the network are visible to all peers. This log must have the same &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-18-certificate-transparency/#everyone-must-see-the-same-logs&quot;&gt;requirements&lt;/a&gt; as a certificate log:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Everyone must see the same log in the same order.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;No one should be able to un-publish a transaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all the transactions are visible to all peers and no peer can delete a transaction, a peer will detect when a coin has been previously spent on an earlier transaction.&lt;/p&gt;
&lt;p&gt;Using the above double-spending example where a user Y attempts to spend the same coin for users Z and Q, these requirements will ensure that if Y-&amp;gt;Z happened before Y-&amp;gt;Q, then:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;User Z will see Y-&amp;gt;Z came before Y-&amp;gt;Q and will accept Y-&amp;gt;Z.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;User Q will see Y-&amp;gt;Z came before Y-&amp;gt;Q and will reject Y-&amp;gt;Q.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;publishing-to-a-log&quot;&gt;Publishing to a log &lt;a class=&quot;direct-link&quot; href=&quot;#publishing-to-a-log&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One challenge with this log approach is in determining what gets published to the log and in what order. For example, we could have a central log server or a leader that decides the order of transactions like &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-6-7-fault-tolerance-raft&quot;&gt;Raft&lt;/a&gt;, but this goes against Bitcoin&#39;s decentralization goal.&lt;/p&gt;
&lt;p&gt;Another way to manage the log is to send new transactions to all peers and have them vote on which transaction to append to the log, with the majority winning the vote. To determine the majority, we could count one vote per IP address, but an attacker can forge IP addresses to claim a majority and vote multiple times. The impact of this will be that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When user Z asks, the attacker&#39;s majority says, &amp;quot;Y-&amp;gt;Z is in the log before Y-&amp;gt;Q&amp;quot;&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When user Q asks, the attacker&#39;s majority says, &amp;quot;Y-&amp;gt;Q is in the log before Y-&amp;gt;Z&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bitcoin addresses the double-spending problem by using a &lt;em&gt;blockchain,&lt;/em&gt; and I&#39;ll describe that next.&lt;/p&gt;
&lt;h2 id=&quot;the-bitcoin-blockchain&quot;&gt;The Bitcoin blockchain &lt;a class=&quot;direct-link&quot; href=&quot;#the-bitcoin-blockchain&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Bitcoin blockchain is a sequence of blocks that acts as a public ledger containing all the transactions on every coin in the network. Each block is identified with a hash and contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The hash of the previous block in the chain.&lt;/li&gt;
&lt;li&gt;A set of transactions.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;nonce&lt;/em&gt;, which I&#39;ll explain soon.&lt;/li&gt;
&lt;/ul&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/blockchain.png&quot; alt=&quot;Figure 3: A blockchain is a sequence of blocks.&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 3: Blockchain representation. &lt;/p&gt;
&lt;p&gt;Each peer in the network has a complete copy of the chain. When a peer wants to add a new block to the chain, it broadcasts the block to all the peers. Any new transactions also get flooded to all the peers. All the blocks and transactions on the Bitcoin network are publicly visible &lt;a href=&quot;https://www.blockchain.com/explorer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When you make a payment, the payee won&#39;t accept it until the transaction is in the blockchain. And since all transactions are in the blockchain, the payee will find out if the coin has been spent before.&lt;/p&gt;
&lt;p&gt;The challenge posed in the previous section was on determining what gets added to the log. The next section will cover Bitcoin&#39;s approach.&lt;/p&gt;
&lt;h3 id=&quot;adding-a-new-block&quot;&gt;Adding a new block &lt;a class=&quot;direct-link&quot; href=&quot;#adding-a-new-block&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a peer receives new transactions, it collects them into a block. Before it can add the block to the blockchain, it needs to do actual CPU work. This is called a &lt;em&gt;proof-of-work&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To explain this, assume we have a peer S with an unpublished block containing a list of transactions and the previous block&#39;s hash. S needs to create a hash to identify the unpublished block using its contents, and that involves solving a hard computational puzzle. The puzzle is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given that peer S has a list of transactions(let&#39;s call this &lt;em&gt;l&lt;/em&gt;) and the previous block&#39;s hash (&lt;em&gt;hp&lt;/em&gt;), S must find a value &lt;em&gt;x&lt;/em&gt; such that when it applies a hash function to the combination of l, hp, and x, it gets an output that begins with a long run of zeros.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Stated another way, S must find a value &lt;em&gt;x&lt;/em&gt; such that:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; hash(l + hp + x) = 000000000000000...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This value of &lt;em&gt;x&lt;/em&gt; is known as the &lt;em&gt;nonce&lt;/em&gt; and the difficult process of finding this nonce is called &lt;em&gt;mining&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The exact number of leading zeros required in the output varies with the speed with which peers generate new blocks. If the peers are generating new blocks too quickly, the network will make the proof-of-work more difficult by increasing the number of leading zeros required.&lt;/p&gt;
&lt;p&gt;Finding this proof-of-work for a block is a costly operation as it requires major CPU power, and so this system essentially limits peers to one vote per CPU.&lt;/p&gt;
&lt;p&gt;It takes 10 minutes on average for a peer to mine a new block, which means that the parties involved in a transaction have to wait for about 10 minutes before it appears on the blockchain.&lt;/p&gt;
&lt;p&gt;A peer broadcasts a block to all peers after finding its proof of work.&lt;/p&gt;
&lt;h4 id=&quot;validation-checks&quot;&gt;Validation Checks &lt;a class=&quot;direct-link&quot; href=&quot;#validation-checks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a peer receives a new block, it validates the block by checking that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The block&#39;s hash has the required number of leading zeros.&lt;/li&gt;
&lt;li&gt;The previous block&#39;s hash exists in the chain. If the previous block doesn&#39;t exist, the peer will request it from the network.&lt;/li&gt;
&lt;li&gt;All the transactions in the block are valid.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The peer validates each transaction by checking that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;No other transaction has spent the same previous transaction (Recall that a transaction record contains a hash of the previous transaction).&lt;/li&gt;
&lt;li&gt;The transaction&#39;s signature is by the private key of the public key in the previous transaction, as described earlier.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the block is valid, the peer shows its acceptance by working on creating the next block in the chain—using the accepted block&#39;s hash as the previous hash.&lt;/p&gt;
&lt;h3 id=&quot;temporary-double-spending-is-possible&quot;&gt;Temporary double-spending is possible &lt;a class=&quot;direct-link&quot; href=&quot;#temporary-double-spending-is-possible&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;scenario-%231&quot;&gt;Scenario #1 &lt;a class=&quot;direct-link&quot; href=&quot;#scenario-%231&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One possible scenario in the Bitcoin network is that two peers A and B mining the next block in the chain find the nonce at the same time and broadcast the block to all peers, but because of network issues, some peers accept the block from peer A before B&#39;s block, while others accept B&#39;s block first.&lt;/p&gt;
&lt;p&gt;This causes a &lt;em&gt;fork&lt;/em&gt; on the blockchain where it now has two branches.&lt;/p&gt;
&lt;h4 id=&quot;scenario-%232&quot;&gt;Scenario #2 &lt;a class=&quot;direct-link&quot; href=&quot;#scenario-%232&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Another possible scenario is that a peer sends one transaction to a subset of peers and another one to a different subset using the same coin.&lt;/p&gt;
&lt;p&gt;For example, a peer Y could tell some peers about a transaction Y-&amp;gt;Z and others about Y-&amp;gt;Q, which both use the same coin. This will create a fork as illustrated below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/fork-small.png&quot; alt=&quot;Figure 4: There is a fork caused by B6 having two successors. &quot;&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt; Figure 4: There is a fork caused by B6 having two successors. &lt;/p&gt;
&lt;h4 id=&quot;peers-will-abandon-the-shorter-branch&quot;&gt;Peers will abandon the shorter branch &lt;a class=&quot;direct-link&quot; href=&quot;#peers-will-abandon-the-shorter-branch&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a peer receives two different successors to the same block, it will start working on the first one it received but save the other branch in case it gets longer. A branch will get longer when a peer finds the next proof-of-work.&lt;/p&gt;
&lt;p&gt;If a peer is working on a branch and sees that another branch has gotten longer, it will abandon its current branch and switch to the longer one.  This will also cause any transactions on the shorter branch to get abandoned.&lt;/p&gt;
&lt;p&gt;During this period where different peers are seeing different branches of the chain, an attacker will be able to double-spend a coin. But what makes Bitcoin work is that the shorter branch will eventually get abandoned and only one transaction will remain on the blockchain.&lt;/p&gt;
&lt;p&gt;This possibility of a fork is why careful Bitcoin clients wait until there are a few successor blocks (typically six) to the one that contains their transaction before believing it was successful. If a block has many successor blocks, it is unlikely that a dubious fork will overtake it.&lt;/p&gt;
&lt;h3 id=&quot;faq&quot;&gt;FAQ &lt;a class=&quot;direct-link&quot; href=&quot;#faq&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;where-are-new-coins-from%3F&quot;&gt;Where are new coins from? &lt;a class=&quot;direct-link&quot; href=&quot;#where-are-new-coins-from%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a peer mines a block and the other peers accept it, the peer gets a 6.25-bitcoin reward. This is an incentive for people to operate bitcoin peers.&lt;/p&gt;
&lt;h4 id=&quot;can-an-attacker-change-an-existing-block-in-the-middle-of-the-blockchain%3F&quot;&gt;Can an attacker change an existing block in the middle of the blockchain? &lt;a class=&quot;direct-link&quot; href=&quot;#can-an-attacker-change-an-existing-block-in-the-middle-of-the-blockchain%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An attacker may want to do this so they can delete a spend of their coin from the blockchain and spend it again.&lt;/p&gt;
&lt;p&gt;The Bitcoin network prevents this by the fact that the block&#39;s hash will change if you delete a transaction, and so the previous hash in the next block will be different, which the peers will detect.&lt;/p&gt;
&lt;h4 id=&quot;can-an-attacker-start-a-fork-from-an-old-block%3F&quot;&gt;Can an attacker start a fork from an old block? &lt;a class=&quot;direct-link&quot; href=&quot;#can-an-attacker-start-a-fork-from-an-old-block%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If an attacker wants to double-spend a coin, they may start a fork from the block that precedes the one with the first spend of the coin and mine new blocks until the forked branch is the longest branch of the chain.&lt;/p&gt;
&lt;p&gt;For this to be successful, the attacker must have enough CPU power to come from behind and mine blocks faster than all the honest peers.&lt;/p&gt;
&lt;p&gt;If the attacker can create the longest branch, everyone will switch to it and so the attacker can double-spend a coin. But if an attacker has that much CPU power to mine blocks faster than all the honest peers, they might as well use it to generate new coins instead of reusing an old one.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&#39;s a lot more to learn about Bitcoin and I&#39;ve offered a one-sided view so far, but my goal here is to present an idea of how it works. Some downsides of using Bitcoin are that the proof-of-work takes too much power and the 10-minute confirmation wait is too long, among &lt;a href=&quot;https://cs.stanford.edu/people/eroberts/courses/cs181/projects/2010-11/DigitalCurrencies/disadvantages/index.html&quot;&gt;other points&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In summary, having an auditable public ledger is a great idea, especially in building open systems. Extra points if there are incentives in place to keep people honest.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources &lt;a class=&quot;direct-link&quot; href=&quot;#resources&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/bitcoin.pdf&quot;&gt;Bitcoin: A Peer-to-Peer Electronic Cash System&lt;/a&gt; - Original Bitcoin paper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://michaelnielsen.org/ddi/how-the-bitcoin-protocol-actually-works/&quot;&gt;How the Bitcoin protocol actually works&lt;/a&gt; by Michael Nielsen.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.blockchain.com/explorer&quot;&gt;Blockchain Explorer&lt;/a&gt; - View the latest blocks and transactions in the network.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.bitcoin.it/wiki/Proof_of_work&quot;&gt;Proof of work&lt;/a&gt; - Further explanation of the Proof of work and what makes it difficult.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-bitcoin.txt&quot;&gt;Lecture 19: Bitcoin &lt;/a&gt; - MIT 6.824 lecture notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/bitcoin-faq.txt&quot;&gt;Bitcoin FAQ&lt;/a&gt; - Additional material from 6.824.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Last updated on December 12, 2020.&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 18 - Certificate Transparency</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-18-certificate-transparency/"/>
		<updated>2020-12-02T00:05:30-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-18-certificate-transparency/</id>
		<content type="html">&lt;p&gt;This lecture is about building systems out of mutually untrustworthy components—using the Web as a case study. The systems we have seen so far are closed systems for which we have assumed that all the participants are trustworthy. But in an open system like the Web where anyone can take part, and there is no universally trusted authority, trust and security are top-level issues to address.&lt;/p&gt;
&lt;p&gt;A fundamental challenge in building open systems is verifying the identity of each component involved. We can frame that challenge as each computer in the system asking: &lt;em&gt;Am I talking to the right computer?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Certificate Transparency (CT) aims to help answer this question, but before going into CT, I&#39;ll give a brief tour of the evolution of security on the Web.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#without-https-%28man-in-the-middle-attacks%29&quot;&gt;Without HTTPS (Man-in-the-middle attacks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#https&quot;&gt;HTTPS&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#how-https-works&quot;&gt;How HTTPS works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#certificate-authorities-protect-us...&quot;&gt;Certificate Authorities protect us...&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#...but-they-can-go-rogue&quot;&gt;...But they can go rogue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#can-we-have-an-online-database-of-valid-certificates%3F&quot;&gt;Can we have an online database of valid certificates?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#certificate-transparency&quot;&gt;Certificate Transparency&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#certificate-logs&quot;&gt;Certificate Logs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#monitors&quot;&gt;Monitors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#auditors&quot;&gt;Auditors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#everyone-must-see-the-same-logs&quot;&gt;Everyone must see the same logs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;without-https-(man-in-the-middle-attacks)&quot;&gt;Without HTTPS (Man-in-the-middle attacks) &lt;a class=&quot;direct-link&quot; href=&quot;#without-https-(man-in-the-middle-attacks)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A man-in-the-middle attack happens when a third party intercepts a connection between a user and an application, as illustrated in the figure below&lt;sup&gt;&lt;a name=&quot;1&quot;&gt;&lt;a href=&quot;#1-link&quot;&gt;[1]&lt;/a&gt;&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/man-in-the-middle-attack.png&quot; alt=&quot;Figure 1: Man-in-the-middle attack.&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 1: Man-in-the-middle attack. &lt;/p&gt;
&lt;p&gt;When an attacker intercepts an HTTP connection, they can read and change any packets being sent over the network. These packets may contain any information ranging from passwords to bank details to other private information that a user does not intend for an intruder.&lt;/p&gt;
&lt;h2 id=&quot;https&quot;&gt;HTTPS &lt;a class=&quot;direct-link&quot; href=&quot;#https&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;HTTPS was invented to make communication over the internet more secure. It takes the original HTTP protocol and adds a layer of security known as to as SSL/TLS. I&#39;ll refer to this layer as TLS for the rest of the post.&lt;/p&gt;
&lt;p&gt;With TLS (Transport Layer Security), you only send and receive encrypted data over the network, and only a secret key agreed on by your computer and the site you&#39;re visiting can decrypt this data. Thus, while an attacker can still intercept your HTTPS connection, they cannot make sense of the transmitted packets.&lt;/p&gt;
&lt;h3 id=&quot;how-https-works&quot;&gt;How HTTPS works &lt;a class=&quot;direct-link&quot; href=&quot;#how-https-works&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;TLS is based on public-key cryptography, which means here that the server has a public/private key pair.  The server exposes the public key and keeps the private key &lt;em&gt;private&lt;/em&gt;. When a client encrypts data using the server&#39;s public key, only the private key can decrypt it.&lt;/p&gt;
&lt;p&gt;The first step in enabling HTTPS for a server is to get a &lt;em&gt;certificate&lt;/em&gt; from a &lt;em&gt;Certificate Authority&lt;/em&gt; (CA). This certificate is an ID for the server that contains its domain name, information about its owners, the server&#39;s public key, the CA&#39;s identity, and a digital signature signed by the CA.&lt;/p&gt;
&lt;p&gt;At a high level, when your browser connects to an HTTPS server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The server responds with its certificate to prove its identity to the browser.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Your browser then checks the validity of this certificate using the digital signature from the CA. I&#39;ll describe how this works soon.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;After verifying the certificate, your browser will generate a random key and encrypt it using the server&#39;s public key. It will then send this encrypted key to the server as a challenge. The challenge is for the server to prove that it has the private key equivalent for the public key by decrypting the encrypted message.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the server decrypts this random key, it means each party is happy that they are talking to the right party and they agree to use the key for subsequent communication.&lt;/p&gt;
&lt;p&gt;These steps make up the &lt;em&gt;TLS handshake&lt;/em&gt;. After the handshake is complete, both parties encrypt HTTP requests and responses using the key they agreed on, which only the other party can decrypt.&lt;/p&gt;
&lt;h3 id=&quot;certificate-authorities-protect-us...&quot;&gt;Certificate Authorities protect us... &lt;a class=&quot;direct-link&quot; href=&quot;#certificate-authorities-protect-us...&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Without the digital signature from a Certificate Authority, anyone can create a certificate falsely claiming to be, say, &#39;&lt;a href=&quot;http://netflix.com/&quot;&gt;netflix.com&lt;/a&gt;&#39; and get your browser to trust them. Note that your browser will trust them as long as the fake server can decrypt a message it encrypts with the server&#39;s public key. To prevent this, there are few authorized CAs which may issue certificates.&lt;/p&gt;
&lt;p&gt;Like servers, CAs also have a public/private key pair. When a CA issues a certificate, it encrypts the certificate&#39;s content with its private key and uses the encrypted text as the digital signature for the certificate. Anyone can decrypt the signature using the CA&#39;s public key.&lt;/p&gt;
&lt;p&gt;Each browser comes with a pre-installed list of public keys of all CAs it trusts. When your browser receives a server&#39;s certificate, it first checks its list for whether it trusts the issuing CA before decrypting the digital signature using the CA&#39;s public key. If the decrypted content matches the certificate, your browser is sure that a valid CA issued the certificate and continues the TLS handshake.&lt;/p&gt;
&lt;h3 id=&quot;...but-they-can-go-rogue&quot;&gt;...But they can go rogue &lt;a class=&quot;direct-link&quot; href=&quot;#...but-they-can-go-rogue&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, CAs can get compromised or go rogue and end up issuing &amp;quot;bogus&amp;quot; certificates, i.e., a CA may issue the certificate for a domain name to the wrong owner. This has &lt;a href=&quot;https://threatpost.com/what-you-need-know-about-diginotar-hack-090211/75611/&quot;&gt;happened&lt;/a&gt; &lt;a href=&quot;https://threatpost.com/malaysian-ca-digicert-revokes-certs-weak-keys-mozilla-moves-revoke-trust-110311/75847/&quot;&gt;before&lt;/a&gt;. Since any CA can issue a certificate for any domain name, the least secure CA limits the overall security of the certificate mechanism.&lt;/p&gt;
&lt;p&gt;Thus, while HTTPS can increase our confidence that we are talking to the right computers, it is not enough.&lt;/p&gt;
&lt;h3 id=&quot;can-we-have-an-online-database-of-valid-certificates%3F&quot;&gt;Can we have an online database of valid certificates? &lt;a class=&quot;direct-link&quot; href=&quot;#can-we-have-an-online-database-of-valid-certificates%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To limit the effect of a bogus certificate, what would be ideal is if our browsers could somehow detect and reject bogus certificates. One way this could work is if there is a database of all the valid certificates in existence that our browsers could query. This comes with several questions, though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Given that there&#39;s no single authority that the entire world trusts, who would run this database?&lt;/li&gt;
&lt;li&gt;How do we decide who owns a domain name?&lt;/li&gt;
&lt;li&gt;How do we handle situations where people change their CAs, renew their certificates or lose their private key and have to request a new one? These will all look like a second certificate for an existing domain name.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Certificate Transparency is an approach to answering these questions, and we&#39;ll look at it next.&lt;/p&gt;
&lt;h2 id=&quot;certificate-transparency&quot;&gt;Certificate Transparency &lt;a class=&quot;direct-link&quot; href=&quot;#certificate-transparency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Certificate Transparency (CT) is a system for making the existence of all certificates publicly available to domain owners, CAs, and browsers. This way, when a rogue CA issues a certificate for &#39;&lt;a href=&quot;http://netflix.com/&quot;&gt;netflix.com&lt;/a&gt;&#39; to the wrong person, the certificate is immediately visible to the right owners for them to act on it.&lt;/p&gt;
&lt;p&gt;CT works by introducing three components to the certificate system: certificate logs, monitors, and auditors.&lt;/p&gt;
&lt;h4 id=&quot;certificate-logs&quot;&gt;Certificate Logs &lt;a class=&quot;direct-link&quot; href=&quot;#certificate-logs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Certificate logs contain an append-only record of certificates. Anyone can add a certificate to the logs, though only certificate authorities do this typically. When a server issues a new certificate, it must add it to the logs. Anyone can also query a log to verify that it contains a certificate.&lt;/p&gt;
&lt;p&gt;Certificate logs are hosted on a group of servers spread over the world and can be managed independently by a CA or any interested party.&lt;/p&gt;
&lt;h4 id=&quot;monitors&quot;&gt;Monitors &lt;a class=&quot;direct-link&quot; href=&quot;#monitors&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Monitors are servers that periodically check the log servers for if a CA has issued any suspicious certificates for the domain names they are aware of. They are hosted by organizations which manage a set of domain names. For example, a company like Netflix could host their monitors and run periodic checks on the certificate logs for if any suspicious certificates exist &#39;&lt;a href=&quot;http://netflix.com/&quot;&gt;netflix.com&lt;/a&gt;&#39;.&lt;/p&gt;
&lt;p&gt;When a monitor detects a suspicious certificate, I believe there is a manual step involved where a human checks whether the certificate is actually OK or was wrongly issued. There is a certificate revocation process to get rid of bad certificates.&lt;/p&gt;
&lt;h4 id=&quot;auditors&quot;&gt;Auditors &lt;a class=&quot;direct-link&quot; href=&quot;#auditors&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An auditor runs in a web browser and checks whether the certificate it receives from a server has been registered in the certificate logs.&lt;/p&gt;
&lt;p&gt;These components work together to bring openness to the SSL certificate system. Quoting the lecture notes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If browsers and monitors see the same log, and monitors raise an alarm if there&#39;s a bogus cert in the log, and browsers require that each cert they use is in the log, then browsers can feel safe using any cert that&#39;s in the log.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that auditors and monitors also communicate with each other to exchange information about the logs.&lt;/p&gt;
&lt;h3 id=&quot;everyone-must-see-the-same-logs&quot;&gt;Everyone must see the same logs &lt;a class=&quot;direct-link&quot; href=&quot;#everyone-must-see-the-same-logs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For certificate transparency to work, there are two critical requirements for the logs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No deletion:&lt;/strong&gt;  This requirement prevents a situation where a log server claims that a bogus certificate is in the log and shows it to the browser, but then the log operator deletes it from the log before the monitor can detect it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No equivocation:&lt;/strong&gt;  All parties must see the same log content, otherwise, a log server could show browsers a log with the bogus certificate, and show the monitor a log without the certificate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these requirements, if a CA issues a bogus certificate for a domain name, it must add the certificate to the log. And since a log operator can&#39;t delete it, the domain name&#39;s owner will eventually see it. But meeting this requirement is difficult because, like CAs, log operators can also get compromised and may even conspire with malicious CAs.&lt;/p&gt;
&lt;p&gt;To show that it isn&#39;t violating any of the requirements, a certificate log must be able to prove two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;That a particular certificate is in the log.&lt;/li&gt;
&lt;li&gt;That if it is showing a version with new certificates added, that version is consistent with the previous version. Proving this confirms that a log operator has modified no certificates in the log and the log has never been branched or forked.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It does this by storing the certificates in a &lt;a href=&quot;https://research.swtch.com/tlog&quot;&gt;Merkle Tree&lt;/a&gt; data structure. I won&#39;t go into the details of that here, but I recommend reading &lt;a href=&quot;https://www.certificate-transparency.org/log-proofs-work&quot;&gt;this&lt;/a&gt; post if you&#39;re interested in that.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The key property of Certificate Transparency is that everyone sees the same logs. By doing this, users can detect when CAs have issued bogus certificates for their domain name, and browsers can be confident that any certificates in the log are approved by their owners—which means they are talking to the right servers.&lt;/p&gt;
&lt;p&gt;Finally, note that Certificate Transparency does not completely prevent the effect of bogus certificates. There might be a window where the certificates may dupe a browser before the monitors can detect them. What CT offers, though, is a system for quicker detection of these certificates, which will reduce their effect.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;1-link&quot;&gt; &lt;a href=&quot;#1&quot;&gt;[1]&lt;/a&gt;: Image lifted from &lt;a href=&quot;https://www.imperva.com/learn/application-security/man-in-the-middle-attack-mitm/&quot;&gt;this&lt;/a&gt; post by Imperva. &lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-ct.txt&quot;&gt;Lecture 18: Certificate Transparency&lt;/a&gt; - MIT 6.824 lecture notes&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/ct-faq.txt&quot;&gt;Certificate Transparency FAQ&lt;/a&gt; - Additional material from 6.824&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.thesslstore.com/blog/man-in-the-middle-attack-2&quot;&gt;Executing a Man-in-the-Middle Attack in just 15 Minutes&lt;/a&gt; by Patrick Nohe&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://robertheaton.com/2014/03/27/how-does-https-actually-work/&quot;&gt;How does SSL actually work?&lt;/a&gt; by Robert Heaton&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.certificate-transparency.org/what-is-ct&quot;&gt;What is Certificate Transparency?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.certificate-transparency.org/how-ct-works&quot;&gt;How Certificate Transparency Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://research.swtch.com/tlog&quot;&gt;Transparent Logs for Skeptical Clients&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.certificate-transparency.org/log-proofs-work&quot;&gt;How Log Proofs Work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 17 - Causal Consistency, COPS</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-17-cops/"/>
		<updated>2020-11-23T23:40:24-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-17-cops/</id>
		<content type="html">&lt;p&gt;In studying distributed systems, I&#39;ve come across systems like &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-13-spanner&quot;&gt;Spanner&lt;/a&gt;, which incurs additional latency for strong consistency, and &lt;a href=&quot;https://aws.amazon.com/dynamodb/&quot;&gt;DynamoDB&lt;/a&gt;, which sacrifices strong consistency for low latency in responding to requests. This latency vs consistency tradeoff is one that many systems have to make, and &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/cops.pdf&quot;&gt;COPS&lt;/a&gt;—this lecture&#39;s focus—is no exception.&lt;/p&gt;
&lt;p&gt;What the COPS (Cluster of Order-Preserving Servers) system offers, though,  is a geo-replicated database with a consistency model that&#39;s closer to strong consistency while offering performance similar to low latency databases. This consistency model is called &lt;em&gt;causal+ consistency&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;COPS provides low latency querying by directing clients&#39; requests to their local data centres and ensuring that these requests (both reads and writes) can proceed without waiting for or talking to other data centres. This is in contrast to a system like Spanner, where at least one other data centre must acknowledge writes.&lt;/p&gt;
&lt;p&gt;The rest of this post will describe the causal+ consistency model and how COPS works.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#causal%2B-consistency&quot;&gt;Causal+ Consistency&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#causal-consistency&quot;&gt;Causal Consistency&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#potential-causality&quot;&gt;Potential Causality&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#convergent-conflict-handling&quot;&gt;Convergent Conflict Handling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#causal%2B-vs-eventual-and-external-consistency&quot;&gt;Causal+ vs Eventual and External Consistency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cops&quot;&gt;COPS&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#overview&quot;&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cops-clients-maintain-a-context&quot;&gt;COPS clients maintain a context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#lamport-timestamps-provide-a-global-order&quot;&gt;Lamport timestamps provide a global order&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#writing-values-in-cops&quot;&gt;Writing values in COPS&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#writes-to-the-local-cluster&quot;&gt;Writes to the local cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#write-replication-between-clusters.&quot;&gt;Write replication between clusters.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#reading-values-in-cops&quot;&gt;Reading values in COPS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#limitations&quot;&gt;Limitations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;causal%2B-consistency&quot;&gt;Causal+ Consistency &lt;a class=&quot;direct-link&quot; href=&quot;#causal%2B-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Causal+ consistency combines &lt;em&gt;causal consistency&lt;/em&gt; with &lt;em&gt;convergent conflict handling&lt;/em&gt;. I&#39;ll describe those next.&lt;/p&gt;
&lt;h3 id=&quot;causal-consistency&quot;&gt;Causal Consistency &lt;a class=&quot;direct-link&quot; href=&quot;#causal-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Two operations are causally related if we can say that one &lt;em&gt;happened before&lt;/em&gt; the other. Any system that implements causal consistency guarantees it will preserve this order across all replicas. If an operation &lt;em&gt;a&lt;/em&gt; happens before an operation &lt;em&gt;b,&lt;/em&gt; no replica should see the effect of operation &lt;em&gt;b&lt;/em&gt; before it has seen the effect of operation &lt;em&gt;a&lt;/em&gt;. Here, we say operation &lt;em&gt;b&lt;/em&gt; is causally dependent on operation &lt;em&gt;a&lt;/em&gt; or &lt;em&gt;a&lt;/em&gt; is a dependency of &lt;em&gt;b&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For example, let&#39;s assume we&#39;re building an e-commerce application and considering a merchant Ade and a customer Seyi.  Here, Ade is trying to share a new item in her inventory with Seyi, which involves the following sequence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, Ade uploads the item onto the platform and then adds it to her online inventory.&lt;/li&gt;
&lt;li&gt;Seyi then checks Ade&#39;s inventory, expecting to see the new item added.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Under causal consistency, if the inventory has a reference to the new item, then Seyi must be able to see the item. Weaker consistency models may not guarantee this. In eventually consistent systems, the operations to upload the item and then add it to the inventory may get reordered during replication and lead to a situation where Seyi sees a reference to the item but not the item itself.&lt;/p&gt;
&lt;h4 id=&quot;potential-causality&quot;&gt;Potential Causality &lt;a class=&quot;direct-link&quot; href=&quot;#potential-causality&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;More formally, the paper mentions three rules that the authors used to define potential causality between operations, denoted &lt;code&gt;-&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Execution Thread&lt;/strong&gt;. If a and b are two operations in a single thread of execution, then a &lt;code&gt;-&amp;gt;&lt;/code&gt; b if operation a happens before operation b.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gets From&lt;/strong&gt;. If a is a put operation and b is a get operation that returns the value written by a, then a &lt;code&gt;-&amp;gt;&lt;/code&gt; b.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transitivity&lt;/strong&gt;. For operations a, b, and c, if a &lt;code&gt;-&amp;gt;&lt;/code&gt; b and b &lt;code&gt;-&amp;gt;&lt;/code&gt; c, then a &lt;code&gt;-&amp;gt;&lt;/code&gt; c&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;The execution in Figure 1 illustrates these rules.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/causality.png&quot; alt=&quot;Figure 1 - Graph showing the causal relationship between operations at a replica. An edge from a to b indicates that a happened before b, or b depends on a.&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 1 - Graph showing the causal relationship between operations at a replica. An edge from a to b shows that a happened before b, or b depends on a.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;execution thread&lt;/em&gt; rule gives &lt;code&gt;get(y)=2&lt;/code&gt; -&amp;gt; &lt;code&gt;put(x,4)&lt;/code&gt;; the &lt;em&gt;gets from&lt;/em&gt; rule gives &lt;code&gt;put(y,2)&lt;/code&gt; -&amp;gt; &lt;code&gt;get(y)=2&lt;/code&gt;; and the &lt;em&gt;transitivity&lt;/em&gt; rule gives &lt;code&gt;put(y,2)&lt;/code&gt; -&amp;gt; &lt;code&gt;put(x,4)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you&#39;re wondering how a system can determine causal relationships between operations at different replicas, you&#39;ll find out in a &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-17-cops/#lamport-timestamps-provide-a-global-order&quot;&gt;later section.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;convergent-conflict-handling&quot;&gt;Convergent Conflict Handling &lt;a class=&quot;direct-link&quot; href=&quot;#convergent-conflict-handling&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Causal consistency does not order concurrent operations. We say that two operations are concurrent if we cannot tell that one happened before the other. A system can replicate two unrelated &lt;code&gt;put&lt;/code&gt; operations in any order, but when there are concurrent &lt;code&gt;put&lt;/code&gt; operations to the same key, we say they &lt;em&gt;conflict&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Quoting the &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/cops.pdf&quot;&gt;lecture paper&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Conflicts are undesirable for two reasons. First, because they are unordered by causal consistency, conflicts allow replicas to diverge forever. For instance, if a is &lt;code&gt;put(x,1)&lt;/code&gt; and b is &lt;code&gt;put(x,2)&lt;/code&gt;, then causal consistency allows one replica to forever return 1 for x and another replica to forever return 2 for x.&lt;/p&gt;
&lt;p&gt;Second, conflicts may represent an exceptional condition that requires special handling. For example, in a shopping cart application, if two people logged in to the same account concurrently add items to their cart, the desired result is to end up with both items in the cart.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Convergent conflict handling requires that a causal+ system handles all conflicting &lt;code&gt;puts&lt;/code&gt; in the same way across all replicas through a handler function &lt;em&gt;h.&lt;/em&gt;  The &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-5/#last-write-wins-(discarding-concurrent-writes)&quot;&gt;last-writer-wins&lt;/a&gt; rule is commonly used in handler functions to ensure that replicas eventually converge.&lt;/p&gt;
&lt;h3 id=&quot;causal%2B-vs-eventual-and-external-consistency&quot;&gt;Causal+ vs Eventual and External Consistency &lt;a class=&quot;direct-link&quot; href=&quot;#causal%2B-vs-eventual-and-external-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With the above properties, causal+ consistency differs from eventual consistency in that an eventually consistent system may not preserve the causal order of operations, leaving clients to deal with the inconsistencies that may arise.&lt;/p&gt;
&lt;p&gt;Also, unlike in &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-13-spanner/#spanner-guarantees-external-consistency&quot;&gt;external consistency&lt;/a&gt;, which always returns the most up-to-date version, a causal+ system may return stale versions of a value. What causal+ guarantees though is that those stale values are consistent with a causal order of operations.&lt;/p&gt;
&lt;p&gt;Let&#39;s now look at COPS, a system which implements this causal+ consistency model.&lt;/p&gt;
&lt;h2 id=&quot;cops&quot;&gt;COPS &lt;a class=&quot;direct-link&quot; href=&quot;#cops&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;direct-link&quot; href=&quot;#overview&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;COPS (Cluster of Order-Preserving Servers) is a geo-replicated key-value storage system that guarantees causal+ consistency. It comprises two software components: &lt;em&gt;a client library&lt;/em&gt; and the &lt;em&gt;key-value store&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Each data centre involved has a local COPS cluster which maintains its copy of the entire dataset. A COPS &lt;em&gt;client&lt;/em&gt; is an application that uses the client library to interact with the key-value store. Clients interact only with their local COPS cluster running in the same data centre.&lt;/p&gt;
&lt;p&gt;COPS shards the stored data across the nodes in a cluster, with each key belonging to a &lt;em&gt;primary&lt;/em&gt; node in each cluster. This primary node receives the writes for a key. After a write completes, the primary node in the local cluster replicates it to the primary nodes in the other clusters.&lt;/p&gt;
&lt;p&gt;Each key also has &lt;em&gt;versions&lt;/em&gt;, which represent different values for that key. COPS guarantees that once a replica has returned a version of a key, the replica will only return that version or a causally later version in subsequent requests.&lt;/p&gt;
&lt;h3 id=&quot;cops-clients-maintain-a-context&quot;&gt;COPS clients maintain a context &lt;a class=&quot;direct-link&quot; href=&quot;#cops-clients-maintain-a-context&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each client maintains a &lt;em&gt;context&lt;/em&gt; to represent the order of its operations. Think of this context as a list that holds items. After each operation, a client adds an item to its context. The order of these items in the list captures the dependencies between versions.&lt;/p&gt;
&lt;p&gt;This works in line with the &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-17-cops/#potential-causality&quot;&gt;earlier section&lt;/a&gt; on potential causality. Using this context, a client can compute the dependencies for a version.&lt;/p&gt;
&lt;h3 id=&quot;lamport-timestamps-provide-a-global-order&quot;&gt;Lamport timestamps provide a global order &lt;a class=&quot;direct-link&quot; href=&quot;#lamport-timestamps-provide-a-global-order&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is easy for a COPS client to determine the order of operations on a key in a local cluster based on its context, but when there are concurrent operations to the same key in different clusters—a conflict—we need another way to determine that order.&lt;/p&gt;
&lt;p&gt;COPS uses &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-1/#lamport-timestamps&quot;&gt;Lamport timestamps&lt;/a&gt; to derive a &lt;em&gt;global order&lt;/em&gt; over all writes for each key. With Lamport timestamps, all the replicas will agree on which operation happened before the other.&lt;/p&gt;
&lt;h3 id=&quot;writing-values-in-cops&quot;&gt;Writing values in COPS &lt;a class=&quot;direct-link&quot; href=&quot;#writing-values-in-cops&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;writes-to-the-local-cluster&quot;&gt;Writes to the local cluster &lt;a class=&quot;direct-link&quot; href=&quot;#writes-to-the-local-cluster&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a client calls &lt;code&gt;put&lt;/code&gt; for a key, the library computes the dependencies for that key based on its context and sends that information to the local primary storage node. This storage node will not commit the key&#39;s value &lt;em&gt;until&lt;/em&gt; the COPS cluster has written all the computed dependencies.&lt;/p&gt;
&lt;p&gt;After committing the value, the primary storage node assigns it a unique version number using a Lamport timestamp and immediately returns that number to the client.&lt;/p&gt;
&lt;p&gt;By not waiting for the replication to complete, COPS eliminates most of the latency incurred by systems with stronger consistency guarantees.&lt;/p&gt;
&lt;h4 id=&quot;write-replication-between-clusters.&quot;&gt;Write replication between clusters. &lt;a class=&quot;direct-link&quot; href=&quot;#write-replication-between-clusters.&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The primary storage node asynchronously replicates a write to the other clusters after committing a write locally. The node includes information about the write&#39;s dependencies when replicating it.&lt;/p&gt;
&lt;p&gt;When a node in another cluster receives this write, the node checks if the local nodes in its clusters have satisfied all the dependencies. The receiving node does this by issuing a &lt;em&gt;dependency check&lt;/em&gt; request to the local nodes responsible for those dependencies.&lt;/p&gt;
&lt;p&gt;If a local node has not written the dependency value, it blocks the request until it writes the value. Otherwise, it will respond immediately.&lt;/p&gt;
&lt;p&gt;In summary, COPS guarantees causal+ consistency by computing the dependencies of a write, and not committing the write in a cluster until the cluster has committed all the dependencies.&lt;/p&gt;
&lt;h3 id=&quot;reading-values-in-cops&quot;&gt;Reading values in COPS &lt;a class=&quot;direct-link&quot; href=&quot;#reading-values-in-cops&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;COPS also satisfies reads in the local cluster. A COPS client can specify whether they want to read the latest version of a key or a specific older one. When the client library receives the response for a read, it adds the operation to its context to capture potential causality (See &amp;quot;Execution Thread&amp;quot; and &amp;quot;Gets From&amp;quot; &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-17-cops/#potential-causality&quot;&gt;above&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&quot;limitations&quot;&gt;Limitations &lt;a class=&quot;direct-link&quot; href=&quot;#limitations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While causal+ consistency is a popular research idea, it has some limitations. Two major ones are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It cannot capture external causal dependencies. The classic example for this is a phone call: if I do action A, call my friend on another continent to tell her about A, and then she does action B, a system will not capture the causal link between A and B.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Managing conflicts can be difficult, especially when last-writer-wins isn&#39;t sufficient.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The authors don&#39;t compare COPS with other systems in terms of performance or ease of programming in its evaluation section, which I found surprising given that the central thesis is that COPS has a better tradeoff between ease of programming and performance.&lt;/p&gt;
&lt;p&gt;I&#39;ve also left out some details about COPS here around fault tolerance and how it handles transactions, but I hope you&#39;ve gotten a good idea of causal+ consistency and how one might implement it. I recommend reading the paper linked below if you want to know more.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/cops.pdf&quot;&gt;Don’t Settle for Eventual: Scalable Causal Consistency for Wide-Area Storage with COPS&lt;/a&gt; - Original 2011 paper on COPS.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-cops.txt&quot;&gt;Causal Consistency, COPS&lt;/a&gt; - MIT 6.824 lecture notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.usenix.org/system/files/login/articles/08_lloyd_41-43_online.pdf&quot;&gt;A Short Primer on Causal Consistency&lt;/a&gt; - Wyatt Lloyd, Michael J. Freedman, Michael Kaminsky, and David G. Andersen.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 16 - Scaling Memcache at Facebook</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-16-memcache-at-facebook/"/>
		<updated>2020-11-07T21:30:55-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-16-memcache-at-facebook/</id>
		<content type="html">&lt;p&gt;This lecture is about building systems at scale. The associated &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/memcache-fb.pdf&quot;&gt;2013 paper&lt;/a&gt; from Facebook doesn&#39;t present any new ideas per se, but I found it interesting to see how some ideas this course has covered so far on replication, partitioning and consistency play out in such a large scale system.&lt;/p&gt;
&lt;p&gt;The paper is about how Facebook uses memcached as a building block for a distributed key-value store. &lt;a href=&quot;https://www.memcached.org/&quot;&gt;Memcached&lt;/a&gt; is an in-memory data store used for caching. Many applications today benefit from its quick response times and simple API.&lt;/p&gt;
&lt;p&gt;In this post, I&#39;ll describe how a website&#39;s architecture might evolve to cope with increasing load, before describing Facebook&#39;s use of memcached to support the world&#39;s largest social network.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#memcached-at-facebook&quot;&gt;Memcached at Facebook&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#architecture&quot;&gt;Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#in-a-cluster%3A-latency-and-load&quot;&gt;In a cluster: Latency and Load&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#reducing-latency&quot;&gt;Reducing latency&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#memcache-clients-parallelise-and-batch-requests&quot;&gt;Memcache clients parallelise and batch requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#udp-for-reads-and-tcp-for-writes&quot;&gt;UDP for reads and TCP for writes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#memcache-clients-implement-flow-control&quot;&gt;Memcache clients implement flow control&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#reducing-load&quot;&gt;Reducing load&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#leases-and-stale-sets&quot;&gt;Leases and stale sets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#leases-and-thundering-herds&quot;&gt;Leases and thundering herds&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#in-a-region%3A-replication&quot;&gt;In a region: Replication&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#regional-invalidation&quot;&gt;Regional Invalidation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#regional-pools&quot;&gt;Regional Pools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cold-cluster-warmup&quot;&gt;Cold Cluster Warmup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#across-regions%3A-consistency&quot;&gt;Across Regions: Consistency&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#writes-from-a-primary-region&quot;&gt;Writes from a primary region&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#writes-from-a-secondary-region&quot;&gt;Writes from a secondary region&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;evolution-of-web-architectures&quot;&gt;Evolution of web architectures &lt;a class=&quot;direct-link&quot; href=&quot;#evolution-of-web-architectures&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Say you&#39;re building a website for users to upload pictures to. At the start, you might have your application code, web server, and database running on the same as shown below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/memcache-simple.png&quot; alt=&quot;Figure 1 - Evolution of a web architecture: simple, single machine running the application code, web server, and database server.&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 1 - Evolution of a web architecture: simple, single machine running the application code, web server, and database server.&lt;/p&gt;
&lt;p&gt;But as you get more users, the load on your server increases and the application code will likely take too much of the CPU time. Your solution might be to get more CPU power for your application by running a bunch of &lt;em&gt;frontend servers&lt;/em&gt;, which will host the web server and the application code while connecting to a single database server as in Figure 2. Connecting to a single database server gives you the advantage of being certain that all your users will see the same data, even though their requests are served by different frontend servers.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/memcache-multiple-fe.png&quot; alt=&quot;Figure 2 - Evolution of a web architecture: multiple frontend servers to one database server&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 2 - Evolution of a web architecture: multiple frontend servers to one database server.&lt;/p&gt;
&lt;p&gt;As your application grows, the single database server might become overloaded as it can receive requests from an unlimited number of frontend servers. You may address this by adding multiple database servers and &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6/&quot;&gt;sharding&lt;/a&gt; your data over those servers as shown in Figure 3. This comes with its challenges—especially around sharding the data efficiently, managing the membership of the different database servers, and running &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-12-distributed-transactions/&quot;&gt;distributed transactions&lt;/a&gt;—but it could work as a solution to the problem.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/memcache-multiple-db.png&quot; alt=&quot;Figure 3 - Evolution of a web architecture: multiple frontend servers to multiple database servers&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 3 - Evolution of a web architecture: multiple frontend servers to multiple database servers.&lt;/p&gt;
&lt;p&gt;But databases are slow. Reading data from disk can be up to 80x slower than reading data stored in memory. As your application&#39;s user base skyrockets, one way to reduce this latency in database requests is by adding a cache between your frontend servers and the database servers. With this setup, read requests will first go to the cache and only redirect to the database layer when there&#39;s a cache miss.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/memcache-cache.png&quot; alt=&quot;Figure 4 - Evolution of a web architecture: inserting a cache between the frontend and the database&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 4 - Evolution of a web architecture: inserting a cache between the frontend and the database.&lt;/p&gt;
&lt;p&gt;Maintaining a cache is hard, though. You must keep the cache in sync with the database and work out how to prevent cache misses from overloading the database servers.&lt;/p&gt;
&lt;p&gt;This architecture in Figure 4 is similar to Facebook&#39;s setup, and the rest of this post will be on Facebook&#39;s memcache architecture and how they maintain a cache effectively.&lt;/p&gt;
&lt;h1 id=&quot;memcached-at-facebook&quot;&gt;Memcached at Facebook &lt;a class=&quot;direct-link&quot; href=&quot;#memcached-at-facebook&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Facebook uses memcached to reduce the read load on their databases. Facebook&#39;s workload is dominated by reads and memcached prevents them from hitting the database for every request. They use memcached as a &lt;a href=&quot;https://tanzu.vmware.com/content/blog/an-introduction-to-look-aside-vs-inline-caching-patterns&quot;&gt;look-aside cache&lt;/a&gt;. This means that when a web server needs data, it first attempts to fetch the data from the cache. If the value is not in the cache, the web server will fetch the data from the database and then populate the cache with the data.&lt;/p&gt;
&lt;p&gt;For writes, the web server will send the new value for a key to the database and then send another request to the cache to delete the key. Subsequent reads for the key will fetch the latest data from the database.&lt;/p&gt;
&lt;p&gt;This is illustrated below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/memcache-look-aside.png&quot; alt=&quot;Figure 5 - Memcache as a demand-filled look-aside cache. The left half illustrates the read path for a web server on a cache miss. The right half illustrates the write path.&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 5 - Memcache as a demand-filled look-aside cache. The left half illustrates the read path for a web server on a cache miss. The right half illustrates the write path.&lt;/p&gt;
&lt;p&gt;Note that the paper uses &lt;em&gt;memcached&lt;/em&gt; to refer to the open source library and &lt;em&gt;memcache&lt;/em&gt; to refer to the distributed system built on top of memcached at Facebook. I&#39;ll use memcache for the rest of this post.&lt;/p&gt;
&lt;h2 id=&quot;architecture&quot;&gt;Architecture &lt;a class=&quot;direct-link&quot; href=&quot;#architecture&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Facebook&#39;s architecture comprises multiple web, memcache, and database servers. A collection of web and memcache servers make up a &lt;em&gt;frontend cluster&lt;/em&gt;, and multiple frontend clusters make up a data centre. These data centres are called &lt;em&gt;regions&lt;/em&gt; in the paper. The frontend clusters in a region share the same storage cluster. Facebook replicates clusters in different regions around the world, designating one region as the primary and the others as secondary regions.&lt;/p&gt;
&lt;p&gt;The architecture diagram below illustrates these components:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/memcache-architecture.png&quot; alt=&quot;Figure 6 - Overall architecture consisting of one primary region that contains multiple frontend clusters and replicates data to the secondary region.&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 6 - Overall architecture &lt;/p&gt;
&lt;p&gt;Each layer in this architecture comes with its set of challenges, and I&#39;ll cover those next.&lt;/p&gt;
&lt;h2 id=&quot;in-a-cluster%3A-latency-and-load&quot;&gt;In a cluster: Latency and Load &lt;a class=&quot;direct-link&quot; href=&quot;#in-a-cluster%3A-latency-and-load&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For performance in a cluster, the designers of this system focused on two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reducing the latency of memcache&#39;s response.&lt;/li&gt;
&lt;li&gt;Reducing the load on the database when there&#39;s a cache miss.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;reducing-latency&quot;&gt;Reducing latency &lt;a class=&quot;direct-link&quot; href=&quot;#reducing-latency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Facebook&#39;s efforts to reduce latency in a cluster focused on optimising the memcache client. The memcache client runs on each web server and interacts with the memcache servers in its frontend cluster. This client is responsible for request routing, request batching, error handling, serialization, and so on.  Next, let&#39;s see some optimizations made in the client.&lt;/p&gt;
&lt;h4 id=&quot;memcache-clients-parallelise-and-batch-requests&quot;&gt;Memcache clients parallelise and batch requests &lt;a class=&quot;direct-link&quot; href=&quot;#memcache-clients-parallelise-and-batch-requests&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Each web server constructs a directed acyclic graph (DAG) of all the data dependencies it needs for a page. The memcache client then uses this DAG to batch and fetch the required keys concurrently from the memcache servers. This reduces the number of network round trips needed to load a Facebook page.&lt;/p&gt;
&lt;h4 id=&quot;udp-for-reads-and-tcp-for-writes&quot;&gt;UDP for reads and TCP for writes &lt;a class=&quot;direct-link&quot; href=&quot;#udp-for-reads-and-tcp-for-writes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Memcache clients use UDP for &lt;em&gt;get&lt;/em&gt; requests and TCP for &lt;em&gt;set&lt;/em&gt; and &lt;em&gt;delete&lt;/em&gt; requests to the servers. I&#39;ve written about the differences between UDP and TCP in an &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#aside-tcp-vs-udptransmission-control-protocol-vs-user-datagram-protocol&quot;&gt;earlier post&lt;/a&gt;, but what&#39;s relevant here is that using UDP reduces the latency and overhead compared to TCP, which makes it less reliable for transmission.&lt;/p&gt;
&lt;p&gt;When the UDP implementation detects that packets are dropped or received out of order (using sequence numbers), it returns an error to the client which treats the operation as a cache miss. But unlike a standard cache miss (i.e. one not related to dropped packets) which will redirect to the database and then populate the cache, the web server will not attempt to populate the cache with the fetched data. This avoids putting extra load on a potentially overloaded network or server.&lt;/p&gt;
&lt;h4 id=&quot;memcache-clients-implement-flow-control&quot;&gt;Memcache clients implement flow control &lt;a class=&quot;direct-link&quot; href=&quot;#memcache-clients-implement-flow-control&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Facebook partitions data across hundreds of memcache servers in a cluster using &lt;a href=&quot;https://en.wikipedia.org/wiki/Consistent_hashing&quot;&gt;consistent hashing&lt;/a&gt;.  Thus, a web server may need to communicate with many memcache servers to satisfy a user&#39;s request for a page. This leads to the problem of &lt;em&gt;incast congestion.&lt;/em&gt; The paper describes this problem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When a client requests a large number of keys, the responses can overwhelm components such as rack and cluster switches if those responses arrive all at once.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Memcache clients address this by using a &lt;a href=&quot;http://progtutorials.tripod.com/sliding_window.htm#:~:text=Sliding%20Window%20Mechanism%20in%20Data%20Transmission&amp;amp;text=Sliding%20window%20is%20a%20flow,and%20receive%20frames%20in%20order.&quot;&gt;sliding window mechanism&lt;/a&gt; similar to TCP&#39;s to limit the number of outstanding requests. A client can make a limited number of requests at a time and will send the next one only when it has received a response from an in-flight one.&lt;/p&gt;
&lt;h3 id=&quot;reducing-load&quot;&gt;Reducing load &lt;a class=&quot;direct-link&quot; href=&quot;#reducing-load&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As I wrote earlier, Facebook uses memcache to reduce the read load on their databases. But when data is missing from the cache, the web servers must send requests to these databases. Facebook had to take extra care when designing the system to prevent the databases from getting overloaded when there are many cache misses. They use a mechanism called &lt;em&gt;leases&lt;/em&gt; to address two key problems: &lt;em&gt;stale sets&lt;/em&gt; and &lt;em&gt;thundering herds&lt;/em&gt;.&lt;/p&gt;
&lt;h4 id=&quot;leases-and-stale-sets&quot;&gt;Leases and stale sets &lt;a class=&quot;direct-link&quot; href=&quot;#leases-and-stale-sets&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A stale set occurs when a web server sets an out-of-date value for a key in memcache. This can happen when concurrent updates to a key get reordered. For example, let&#39;s consider this scenario to illustrate a stale set with two clients, C1 and C2.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  key &#39;k&#39; not in cache
  C1 get(k), misses
  C1 reads v1 from DB as the value of k
    C2 writes k = v2 in DB
    C2 delete(k)  (recall that any DB writes will invalidate key in cache)
  C1 set(k, v1)
  now mc has stale data, since delete(k) has already happened
  will stay stale indefinitely until k is next written
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Leases prevent this problem. When a client encounters a cache miss for a key, the memcache server will give it a &lt;em&gt;lease&lt;/em&gt; to set data into the cache after reading it from the DB. This lease is a 64-bit token bound to the key. When the client wants to set the data in the cache, it must provide this lease token for memcache to verify. &lt;em&gt;But&lt;/em&gt;, when memcache receives a delete request for the key, it will invalidate any existing lease tokens for that key.&lt;/p&gt;
&lt;p&gt;Therefore, in the above scenario, C1 will get a lease from mc which C2&#39;s &lt;em&gt;delete()&lt;/em&gt; will invalidate. This will lead to memcache ignoring C1&#39;s &lt;em&gt;set&lt;/em&gt;. Note that this key will be missing from the cache and the next reader has to fetch the latest data from the DB.&lt;/p&gt;
&lt;h4 id=&quot;leases-and-thundering-herds&quot;&gt;Leases and thundering herds &lt;a class=&quot;direct-link&quot; href=&quot;#leases-and-thundering-herds&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A thundering herd happens when many clients try to read the data for an invalidated key. When this happens, the clients will all have to send their requests to the database servers, which may get overloaded.&lt;/p&gt;
&lt;p&gt;To prevent the thundering herd problem, memcache servers give leases only once every 10 seconds per key. If another client request for a key comes in within 10 seconds of the lease being issued, the request will have to wait. The idea is that the first client with a lease would have successfully set the data in the cache during the 10 seconds window, and so the waiting clients will read from the cache on retry.&lt;/p&gt;
&lt;h2 id=&quot;in-a-region%3A-replication&quot;&gt;In a region: Replication &lt;a class=&quot;direct-link&quot; href=&quot;#in-a-region%3A-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As Facebook&#39;s load increased, they could have scaled their system by adding more memcache and web servers to a frontend cluster and further partitioning the keyset. However, this has two major limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Incast congestion will get worse as the number of memcache servers increases, since a client has to talk to more servers.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Partitioning by itself does not help much if a key is very popular, as a single server will need to handle all the requests for that key. In cases like this, replicating the data helps so we can share the load among different servers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Facebook scaled this system by creating multiple replicas of a cluster within a &lt;em&gt;region&lt;/em&gt; which share common storage. These clusters are called &lt;em&gt;frontend clusters&lt;/em&gt;, with each cluster made up of web and memcache servers. This method addresses both limitations described above and provides an extra benefit: having smaller clusters instead of a single large one gives them more independent failure domains. They can lose a frontend cluster and still continue operating normally.&lt;/p&gt;
&lt;p&gt;What&#39;s interesting to me here is that there is no special replication protocol to ensure that the clusters in a region have the same data. Their thinking here is that if they randomly route users&#39; requests to any available frontend cluster, they&#39;ll all eventually have the same data.&lt;/p&gt;
&lt;p&gt;Let&#39;s now see some optimizations made within a region.&lt;/p&gt;
&lt;h4 id=&quot;regional-invalidation&quot;&gt;Regional Invalidation &lt;a class=&quot;direct-link&quot; href=&quot;#regional-invalidation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To keep memcache content in the different frontend clusters consistent with the database, the storage cluster sends invalidations to the memcache servers after a write from a web server. As an optimization, when a web server changes data in the storage cluster, it must also send the invalidations to the memcache servers in its local frontend cluster.&lt;/p&gt;
&lt;p&gt;The paper points out that this guarantees &lt;a href=&quot;https://medium.com/@avik.das/scalability-concepts-read-after-write-consistency-3ff70b71e1d1&quot;&gt;read-after-write consistency&lt;/a&gt; for a single user request. My understanding here is that they randomly route each user&#39;s request to a frontend cluster in a region, but that routing is consistent across all the user&#39;s subsequent requests.&lt;/p&gt;
&lt;p&gt;The storage cluster batches the changes and sends them to a set of dedicated servers in each frontend cluster. These dedicated servers then unpack the changes and route the invalidations to the right memcache servers. This mechanism results in fewer packets than if the storage cluster was sending each invalidation for a key directly to the memcache server holding that key.&lt;/p&gt;
&lt;h4 id=&quot;regional-pools&quot;&gt;Regional Pools &lt;a class=&quot;direct-link&quot; href=&quot;#regional-pools&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Items in a dataset which are accessed infrequently and have large sizes rarely need to be replicated. For those keys, there is an optimization in place to store only one copy per region.&lt;/p&gt;
&lt;p&gt;Facebook stores these keys in a &lt;em&gt;regional pool&lt;/em&gt;, which contains a set of memcache servers that are shared by multiple frontend clusters. This is more memory efficient than over-replicating items with a low access rate.&lt;/p&gt;
&lt;h4 id=&quot;cold-cluster-warmup&quot;&gt;Cold Cluster Warmup &lt;a class=&quot;direct-link&quot; href=&quot;#cold-cluster-warmup&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a new frontend cluster is being brought online, any requests to it will result in a cache miss, and this could lead to overloading the database. Facebook has a mechanism called &lt;em&gt;Cold Cluster Warmup&lt;/em&gt; to mitigate this.&lt;/p&gt;
&lt;p&gt;Quoting the paper to describe the solution:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cold Cluster Warmup mitigates this by allowing clients in the “cold cluster” (i.e. the frontend cluster that has an empty cache) to retrieve data from the “warm cluster” (i.e. a cluster that has caches with normal hit rates) rather than the persistent storage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;across-regions%3A-consistency&quot;&gt;Across Regions: Consistency &lt;a class=&quot;direct-link&quot; href=&quot;#across-regions%3A-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Facebook deploys regions across geographic locations worldwide. This has a few advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web servers can be closer to users, which reduces the latency in responding to requests.&lt;/li&gt;
&lt;li&gt;Better fault tolerance since we can withstand natural disasters or power failures in one region.&lt;/li&gt;
&lt;li&gt;New locations can provide cheaper power and other economic benefits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Facebook designates one region to hold the primary database which all writes must go to, and the other regions to contain read-only replicas. They use MySQL&#39;s replication mechanism to keep the replica databases in sync with the primary. The key challenge here is in keeping the data in memcache consistent with the primary database, which may be in another region.&lt;/p&gt;
&lt;p&gt;With the information stored on Facebook—friend lists, status, posts, likes, photos—it is not critical for users to always see fresh data. Users will typically tolerate seeing slightly stale data for these things. Thus, Facebook&#39;s setup allows for users in a secondary region to see slightly stale data for the sake of better performance. The goal here, though, is to reduce that window of staleness and ensure that the data across all regions is eventually consistent.&lt;/p&gt;
&lt;p&gt;There are two major considerations here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When writes come from a primary region.&lt;/li&gt;
&lt;li&gt;When writes come from a secondary region.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;writes-from-a-primary-region&quot;&gt;Writes from a primary region &lt;a class=&quot;direct-link&quot; href=&quot;#writes-from-a-primary-region&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This follows the mechanism described earlier. Writes go directly to the storage cluster in the region, which then replicates it to the secondary regions. Clients in secondary regions may read stale data for any key modified here if there is a lag in replicating the changes to those regions.&lt;/p&gt;
&lt;h3 id=&quot;writes-from-a-secondary-region&quot;&gt;Writes from a secondary region &lt;a class=&quot;direct-link&quot; href=&quot;#writes-from-a-secondary-region&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Consider the following race that can happen when a client C1 updates the database from a secondary region:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  Key k starts with value v1
  C1 is in a secondary region
  C1 updates k=v2 in primary DB
  C1 delete(k)  (in local region)
  C1 get(k), miss
  C1 reads local DB  -- sees v1, not v2!
  later, v2 arrives from primary DB (replication lag)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p&gt;This violates the read-after-write consistency guarantee, and Facebook prevents this scenario by using &lt;em&gt;remote markers&lt;/em&gt;.  With this mechanism, when a web server in a secondary region wants to update data that affects a key &lt;em&gt;k&lt;/em&gt;, it must:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set a remote marker &lt;em&gt;r&lt;sub&gt;k&lt;/sub&gt;&lt;/em&gt; in the regional pool. Think of &lt;em&gt;r&lt;sub&gt;k&lt;/sub&gt;&lt;/em&gt; as a memcache record that represents extra information for key &lt;em&gt;k&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Send the write to the primary region and include &lt;em&gt;r&lt;sub&gt;k&lt;/sub&gt;&lt;/em&gt; in the request, so that the primary knows to invalidate &lt;em&gt;r&lt;sub&gt;k&lt;/sub&gt;&lt;/em&gt; when it replicates the write.&lt;/li&gt;
&lt;li&gt;Delete &lt;em&gt;k&lt;/em&gt; in the local cluster.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By doing this, the web server&#39;s next request for &lt;em&gt;k&lt;/em&gt; will result in a cache miss, after which it will check the regional pool to find &lt;em&gt;r&lt;sub&gt;k&lt;/sub&gt;&lt;/em&gt;. If &lt;em&gt;r&lt;sub&gt;k&lt;/sub&gt;&lt;/em&gt; exists, it means the data in the local region is stale and the server will direct the read to the primary region. Otherwise, it will read from the local region.&lt;/p&gt;
&lt;p&gt;Here, Facebook trades additional latency when there&#39;s a cache miss for a lower probability of reading stale data.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I&#39;ve often thought of using caches primarily to reduce the latency in a system, but this lecture has been an eye-opener in also thinking of caches as being vital for throughput survival. I&#39;ve left out some bits from the paper on fault tolerance and single server improvements, which I&#39;ll encourage you to read up on. Also, the paper doesn&#39;t say much about this, but I&#39;ll be interested in learning more about what pages are cached on Facebook.&lt;/p&gt;
&lt;p&gt;I suspect that this paper is severely outdated and a &lt;a href=&quot;https://www.usenix.org/system/files/osdi20-shi.pdf&quot;&gt;recent paper&lt;/a&gt; from Facebook makes me believe that memcache is no longer used there (this is subject to confirmation, though), but the ideas in here are still very relevant.&lt;/p&gt;
&lt;h1 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/memcache-fb.pdf&quot;&gt;Scaling Memcache at Facebook&lt;/a&gt; - Original paper from Facebook.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-memcached.txt&quot;&gt;Scaling Memcache at Facebook&lt;/a&gt; - MIT 6.824 lecture notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://engineering.fb.com/core-data/scaling-memcached-at-facebook/&quot;&gt;Scaling memcached at Facebook&lt;/a&gt; - Post on Facebook&#39;s engineering blog.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 15 - Spark</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-15-spark/"/>
		<updated>2020-10-16T21:03:01-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-15-spark/</id>
		<content type="html">&lt;p&gt;In the first lecture of this series, I wrote about &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-1-mapreduce/&quot;&gt;MapReduce&lt;/a&gt; as a distributed computation framework. MapReduce partitions the input data across worker nodes, which process data in two stages: &lt;em&gt;map&lt;/em&gt; and &lt;em&gt;reduce.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;While MapReduce was innovative, it came with some limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Running iterative operations like &lt;a href=&quot;https://www.geeksforgeeks.org/page-rank-algorithm-implementation/&quot;&gt;PageRank&lt;/a&gt; in MapReduce involves chaining multiple MapReduce jobs together. Since a MapReduce job writes its output to disk, these sequential operations require a high disk I/O and have high latency.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Similarly, interactive queries where a user runs multiple ad-hoc queries on the same subset of data need to fetch data from the disk for each query.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MapReduce&#39;s API is restricted. Programmers must represent each computation task as a map-reduce operation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In essence, MapReduce is inefficient for applications that reuse intermediate results across multiple computations. Researchers at UC Berkeley invented Spark as a more efficient framework for executing such applications. It deals with these limitations by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Caching intermediate data in the main memory to reduce disk I/O.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generalizing the MapReduce model into a more flexible model with support for more operations than just &lt;em&gt;map&lt;/em&gt; and &lt;em&gt;reduce&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#resilient-distributed-datasets-%28rdds%29&quot;&gt;Resilient Distributed Datasets (RDDs)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#spark-programming-interface&quot;&gt;Spark Programming Interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#representing-rdds&quot;&gt;Representing RDDs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#fault-tolerance&quot;&gt;Fault Tolerance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;resilient-distributed-datasets-(rdds)&quot;&gt;Resilient Distributed Datasets (RDDs) &lt;a class=&quot;direct-link&quot; href=&quot;#resilient-distributed-datasets-(rdds)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the heart of Spark is the &lt;em&gt;Resilient Distributed Datasets (RDDs)&lt;/em&gt; abstraction. RDDs enable programmers to perform in-memory computations on large clusters in a fault-tolerant manner. Quoting the &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/zaharia-spark.pdf&quot;&gt;original paper&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;RDDs are fault-tolerant, parallel data structures that let users explicitly persist intermediate results in memory, control their partitioning to optimize data placement, and manipulate them using a rich set of operators.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We can create RDDs from operations on either data on disk or other RDDs. These operations on other RDDs are called &lt;em&gt;transformations.&lt;/em&gt; Examples of transformations are &lt;em&gt;map&lt;/em&gt;, &lt;em&gt;filter&lt;/em&gt;, and &lt;em&gt;join.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Instead of holding the materialized data in memory, an RDD keeps track of all the transformations that have led to its current state, i.e. how the RDD was derived from other datasets. These transformations are called a &lt;em&gt;lineage.&lt;/em&gt; By tracking the lineage of RDDs, we save memory and can reconstruct an RDD after a failure.&lt;/p&gt;
&lt;p&gt;There&#39;s another class of operations in Spark called &lt;em&gt;actions&lt;/em&gt;. Until we call an action, invoking transformations in Spark only creates the lineage graph. Actions are what cause the computation to execute. Examples of actions are &lt;em&gt;count&lt;/em&gt; and &lt;em&gt;collect&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at the following example of a Spark program from the paper.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  lines = spark.textFile(&amp;quot;hdfs://...&amp;quot;)
  errors = lines.filter(_.startsWith(&amp;quot;ERROR&amp;quot;))
  errors.cache()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On line 1, we create an RDD backed by a file in &lt;a href=&quot;https://en.wikipedia.org/wiki/Apache_Hadoop#HDFS&quot;&gt;HDFS&lt;/a&gt; while line 2 derives a filtered RDD from it. Line 3 asks that the program stores the filtered RDD in memory for reuse across computations.&lt;/p&gt;
&lt;p&gt;We can then perform further transformations on the RDD and use their results as shown below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  // Count errors mentioning MySQL:
  errors.filter(_.contains(&amp;quot;MySQL&amp;quot;)).count()

  // Return the time fields of errors mentioning HDFS as an array
  // (assuming time is field number 3 in a tab-separated format):
  errors.filter(_.contains(&amp;quot;HDFS&amp;quot;))
        .map(_.split(’\t’)(3))
        .collect()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is only after the first action— &lt;em&gt;count&lt;/em&gt;—runs that Spark will execute the previous operations and store the partitions of the &lt;em&gt;errors&lt;/em&gt; RDD in memory. Note that the first RDD, &lt;em&gt;lines&lt;/em&gt;, is not loaded into RAM. This helps to save space as the error messages that we need may only be a small fraction of the data.&lt;/p&gt;
&lt;p&gt;Figure 1 below shows the lineage graph for the RDDs in our third query.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/spark-lineage-graph.png&quot; alt=&quot;Figure 1: Lineage graph for the third query in our example. Starts with a &#39;lines&#39; box with an arrow pointing to an &#39;errors&#39; box, with an error pointing to an &#39;HDFS errors&#39; box, with a last arrow to a &#39;time fields&#39; box. Boxes represent RDDs and arrows represent transformations. &quot;&gt; &lt;/p&gt;
&lt;p&gt;In the query, we started with &lt;em&gt;errors,&lt;/em&gt; which is an RDD based on a filter of &lt;em&gt;lines&lt;/em&gt;. We then applied two further transformations, &lt;em&gt;filter&lt;/em&gt; and &lt;em&gt;map&lt;/em&gt;, to yield our final RDD. If we lose any of the partitions of &lt;em&gt;errors&lt;/em&gt;, Spark can rebuild it by applying a filter on only the corresponding partitions of lines.&lt;/p&gt;
&lt;p&gt;This is already more efficient than MapReduce as Spark forwards data directly from one transformation to the next and can reuse intermediate data without involving the disk.&lt;/p&gt;
&lt;p&gt;Note that users can control how the programs stores RDDs. We can choose whether we want an RDD to use in-memory storage or persist it to disk. We can also specify that an RDD&#39;s elements should be partitioned across machines based on a key in each record.&lt;/p&gt;
&lt;h3 id=&quot;spark-programming-interface&quot;&gt;Spark Programming Interface &lt;a class=&quot;direct-link&quot; href=&quot;#spark-programming-interface&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Spark code, as shown in the previous example, runs in a &lt;em&gt;driver&lt;/em&gt; machine which manages the execution and data flow to &lt;em&gt;worker&lt;/em&gt; machines. The driver defines the RDDs, invokes actions on them, and tracks their lineage. The worker machines store RDD partitions in the RAM across operations. The figure below illustrates this.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/spark-runtime.png&quot; alt=&quot;Figure 2: Spark runtime. The user&#39;s driver program launches multiple workers, which read data blocks from a distributed file system and can persist computed RDD partitions in memory. &quot;&gt; &lt;/p&gt;
&lt;p&gt;Spark supports a wide range of operations on RDDs. You can find a full list of that &lt;a href=&quot;https://spark.apache.org/docs/latest/api/java/index.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;representing-rdds&quot;&gt;Representing RDDs &lt;a class=&quot;direct-link&quot; href=&quot;#representing-rdds&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Spark implementation represents an RDD through an interface that exposes five pieces of information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A set of &lt;em&gt;partitions&lt;/em&gt;, which are atomic chunks of the dataset.&lt;/li&gt;
&lt;li&gt;Data placement of the RDD.&lt;/li&gt;
&lt;li&gt;A set of &lt;em&gt;dependencies&lt;/em&gt; on parent RDDs.&lt;/li&gt;
&lt;li&gt;A function for computing the dataset based on its parents.&lt;/li&gt;
&lt;li&gt;Metadata about its partitioning scheme.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The table below describes these pieces.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/spark-rdd-interface.png&quot; alt=&quot;Interface used to represent RDDs in Spark.&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Table 1 - Interface used to represent RDDs in Spark.&lt;/p&gt;
&lt;p&gt;The paper classifies dependencies into two types: &lt;em&gt;narrow&lt;/em&gt; and &lt;em&gt;wide&lt;/em&gt; dependencies. A parent RDD and a child RDD have a narrow dependency between them if at most one partition of the child RDD uses each partition of the parent RDD. For example, the result of a &lt;em&gt;map&lt;/em&gt; operation on a parent RDD is a child RDD with the same partitions as the parent.&lt;/p&gt;
&lt;p&gt;But with wide dependencies, multiple child partitions may depend on a single partition of a parent RDD. An example of this is in a &lt;em&gt;groupByKey&lt;/em&gt; operation, which needs to look at data from all the partitions in the parent RDD since we must consider all the records for a key.&lt;/p&gt;
&lt;p&gt;The paper explains the reasons for this distinction:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;First, narrow dependencies allow for pipelined execution on one cluster node, which can compute all the parent partitions. For example, one can apply a &lt;em&gt;map&lt;/em&gt; followed by a &lt;em&gt;filter&lt;/em&gt; on an element-by-element basis. In contrast, wide dependencies require data from all parent partitions to be available and to be shuffled across the nodes using a MapReducelike operation.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p&gt;Second, recovery after a node failure is more efficient with a narrow dependency, as only the lost parent partitions need to be recomputed, and they can be recomputed in parallel on different nodes. In contrast, in a lineage graph with wide dependencies, a single failed node might cause the loss of some partition from all the ancestors of an RDD, requiring a complete re-execution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;fault-tolerance&quot;&gt;Fault Tolerance &lt;a class=&quot;direct-link&quot; href=&quot;#fault-tolerance&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a machine crashes, we lose its memory and computation state. The Spark driver then re-runs the transformations on the crashed machine&#39;s partitions on other machines. For narrow dependencies, the driver only needs to re-execute lost partitions.&lt;/p&gt;
&lt;p&gt;With wide dependencies, since recomputing a failed partition requires information from all the partitions, we need to re-execute all the partitions from start—even the ones that didn&#39;t fail. Spark supports checkpoints to HDFS to speed up this process. By doing this, the driver only needs the recompute along the lineage from the latest checkpoint.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the evaluation performed by the paper&#39;s authors, Spark performed up to 20x faster than Hadoop for iterative applications and sped up a real-world data analytics report by 40x. But Spark isn&#39;t perfect. For one, RAM is expensive, and our computation may require a huge RAM for processing in-memory. You can learn about other limitations &lt;a href=&quot;https://www.whizlabs.com/blog/apache-spark-limitations/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Overall, Spark&#39;s reuse of data in-memory and its wider set of operations make it an improvement over MapReduce for expressivity and performance.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/zaharia-spark.pdf&quot;&gt;Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing&lt;/a&gt; - Original paper from UC Berkeley.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-spark.txt&quot;&gt;Lecture 15: Spark&lt;/a&gt; - MIT 6.824 lecture notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://spark.apache.org/docs/latest/api.html&quot;&gt;Spark API Documentation.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 14 - Optimistic Concurrency Control</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-14-occ/"/>
		<updated>2020-10-06T22:00:31-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-14-occ/</id>
		<content type="html">&lt;p&gt;This lecture on optimistic concurrency control is based on a &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/farm-2015.pdf&quot;&gt;2015 paper&lt;/a&gt; from Microsoft Research describing a system called FaRM. FaRM (Fast Remote Memory) is a main memory computing platform that provides distributed transactions with strict serializability, high performance, durability and high availability.&lt;/p&gt;
&lt;p&gt;FaRM takes advantage of two hardware trends to provide these guarantees:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Using &lt;a href=&quot;https://en.wikipedia.org/wiki/Remote_direct_memory_access&quot;&gt;Remote Direct Memory Access&lt;/a&gt; (RDMA) for faster communication between servers.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An inexpensive approach to providing &lt;a href=&quot;https://en.wikipedia.org/wiki/Non-volatile_random-access_memory&quot;&gt;Non-volatile Random Access Memory (NVRAM)&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this post, I&#39;ll explain how FaRM uses these techniques to perform faster and yield greater throughput than &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-13-spanner/&quot;&gt;Google Spanner&lt;/a&gt; for simple transactions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#overview&quot;&gt;Overview&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#performance-optimizations&quot;&gt;Performance Optimizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#non-volatile-ram&quot;&gt;Non-volatile RAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#networking&quot;&gt;Networking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#can-we-bypass-the-cpu%3F&quot;&gt;Can we bypass the CPU?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#optimistic-and-pessimistic-concurrency-control&quot;&gt;Optimistic and pessimistic concurrency control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#farm-machines-maintain-logs&quot;&gt;FaRM machines maintain logs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how-transactions-work&quot;&gt;How transactions work&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#execute&quot;&gt;Execute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#lock&quot;&gt;Lock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#commit-primaries&quot;&gt;Commit primaries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#validate&quot;&gt;Validate&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#lock-%2B-validate-%3D-strict-serializability&quot;&gt;Lock + Validate = Strict Serializability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#examples&quot;&gt;Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#fault-tolerance---commit-backups&quot;&gt;Fault Tolerance - Commit backups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#truncate&quot;&gt;Truncate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;direct-link&quot; href=&quot;#overview&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The FaRM database is intended for a setup where all the servers are managed in the same data centre. It partitions data into &lt;em&gt;regions&lt;/em&gt; stored over many servers and uses &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-4-primary-backup-replication/&quot;&gt;primary/backup replication&lt;/a&gt; to replicate these regions on one primary and &lt;em&gt;f&lt;/em&gt; backup replicas. With this configuration, a region with &lt;em&gt;f&lt;/em&gt; + 1 replicas can tolerate &lt;em&gt;f&lt;/em&gt; failures.&lt;/p&gt;
&lt;p&gt;FaRM uses &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-8-zookeeper/&quot;&gt;ZooKeeper&lt;/a&gt; as its configuration manager, which manages the primary and backup servers allocation for each region.&lt;/p&gt;
&lt;h3 id=&quot;performance-optimizations&quot;&gt;Performance Optimizations &lt;a class=&quot;direct-link&quot; href=&quot;#performance-optimizations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At a high level, some optimizations and tradeoffs that FaRM makes to provide its guarantees are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The data must fit in the total RAM, so there are no disk reads.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It makes use of non-volatile DRAM, so there are no disk writes under normal operations.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FaRM&#39;s transaction and replication protocol take advantage of one-sided RDMA for better performance. RDMA means a server can have rapid access to another server&#39;s RAM while bypassing its CPU.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FaRM also supports fast user-level access to the Network Interface Card (NIC).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s now learn about these optimizations in greater detail.&lt;/p&gt;
&lt;h3 id=&quot;non-volatile-ram&quot;&gt;Non-volatile RAM &lt;a class=&quot;direct-link&quot; href=&quot;#non-volatile-ram&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A FaRM server stores data in its RAM, which eliminates the I/O bottleneck in reading from and writing data to disk. For context, it takes about 200ns to write to RAM, while SSD and hard drive writes take about 100μs and 10ms respectively &lt;sup&gt;&lt;a name=&quot;1&quot;&gt;&lt;a href=&quot;#1-link&quot;&gt;[1]&lt;/a&gt;&lt;/a&gt;&lt;/sup&gt;. Writing to RAM is roughly 500x faster than writing to SSD, and we gain a lot of performance from this optimization.&lt;/p&gt;
&lt;p&gt;But by storing data in RAM, we lose the guarantee that our data will survive a power failure. FaRM provides this guarantee by attaching its servers to a distributed UPS, which makes the data durable. When the power goes out, the distributed UPS saves the RAM&#39;s content to an SSD using the energy from its battery. This operation may take a few minutes, after which the server shuts down. When the server is restarted, FaRM loads the saved image on the SSD back into memory. Another approach to making RAM durable is to use &lt;a href=&quot;https://en.wikipedia.org/wiki/NVDIMM&quot;&gt;NVDIMMs&lt;/a&gt;, but the distributed UPS mechanism used by FaRM is cheaper.&lt;/p&gt;
&lt;p&gt;Note that this distributed UPS arrangement only works in the event of a power failure. Other faults such as CPU, memory, hardware errors, and bugs in FaRM, can cause a server to crash without persisting the RAM content. FaRM copes with this by storing data on more than one replica. When a server crashes, FaRM copies the data from the RAM of one of the failed server&#39;s replicas to another machine to ensure that there are always &lt;em&gt;f&lt;/em&gt; + 1 copies available, where &lt;em&gt;f&lt;/em&gt; represents the number of failures we can tolerate.&lt;/p&gt;
&lt;p&gt;By eliminating the bottleneck in accessing the disk, we can now focus on two other performance bottlenecks: network and CPU.&lt;/p&gt;
&lt;h3 id=&quot;networking&quot;&gt;Networking &lt;a class=&quot;direct-link&quot; href=&quot;#networking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At a high level, communication between two computers on a network is as shown in Figure 1 below &lt;sup&gt;&lt;a name=&quot;2&quot;&gt;&lt;a href=&quot;#2-link&quot;&gt;[2]&lt;/a&gt;&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/farm-cpu-bottleneck.png&quot; alt=&quot;Machine A communicates with Machine B through the OS/Kernel layer and Network Interface Card &quot;&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 1 - Setup for RPC between computers on a network&lt;/p&gt;
&lt;p&gt;In this setup, we see that communication between two machines A and B on a network goes through the kernel of both machines. This kernel consists of different layers through which messages must pass.&lt;/p&gt;
&lt;p&gt;Figure 2 below shows this in more detail.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/farm-networking.png&quot; alt=&quot;Image showing data flow in a network. Data buffer -&gt; Socket -&gt; TCP/UDP -&gt; IP -&gt; Ethernet -&gt; Network Adapter&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 2 - Networking stack. Message must pass through all the different layers.&lt;/p&gt;
&lt;p&gt;This stack requires a lot of expensive CPU operations—system calls, interrupts, and communication between the different layers—to transmit messages from one computer to another, and these slow the performance of network operations.&lt;/p&gt;
&lt;p&gt;FaRM uses two ideas to improve this networking performance:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Kernel bypass&lt;/strong&gt;: Here, the application interacts directly with the NIC without making system calls or involving the kernel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One-sided RDMA&lt;/strong&gt;: With one-sided RDMA, a server can read from or write to the memory of a remote computer without involving its CPU.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Figure 3 highlights these ideas. In the figure, we see that the CPU of Machine A communicates with the NIC without the kernel involved, and the NIC of Machine B bypasses the CPU to access the RAM.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/farm-rdma.png&quot; alt=&quot;Using RDMA and Kernel Bypass for communication between two machines&quot;&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 3 - RDMA and Kernel Bypass&lt;/p&gt;
&lt;p&gt;These optimizations made in FaRM to reduce the storage, network, and CPU usage solve most of the performance bottlenecks found in many applications today.&lt;/p&gt;
&lt;h3 id=&quot;can-we-bypass-the-cpu%3F&quot;&gt;Can we bypass the CPU? &lt;a class=&quot;direct-link&quot; href=&quot;#can-we-bypass-the-cpu%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The transaction protocols we have seen in the systems discussed in earlier lectures like &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-13-spanner/&quot;&gt;Spanner&lt;/a&gt; require active server participation. They need the CPU to answer questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is a database record locked?&lt;/li&gt;
&lt;li&gt;Which is the latest version of a record?&lt;/li&gt;
&lt;li&gt;Is a write committed yet?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But one-sided RDMA bypasses the CPU, so it is not immediately compatible with these protocols. How then can we ensure consistency while avoiding the CPU?&lt;/p&gt;
&lt;p&gt;The rest of this post details FaRM&#39;s approach to solving this challenge.&lt;/p&gt;
&lt;h3 id=&quot;optimistic-and-pessimistic-concurrency-control&quot;&gt;Optimistic and pessimistic concurrency control &lt;a class=&quot;direct-link&quot; href=&quot;#optimistic-and-pessimistic-concurrency-control&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To recap an &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-12-distributed-transactions/#concurrency-control&quot;&gt;earlier post&lt;/a&gt;, two classes of concurrency control exist for transactions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pessimistic:&lt;/strong&gt; When a transaction wants to read or write an object, it first attempts to acquire a lock. If the attempt is successful, it must hold that lock until it commits or aborts. Otherwise, the transaction must wait until any conflicting transaction releases it lock on the object.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optimistic:&lt;/strong&gt; Here, we can run a transaction that reads and writes objects without locking until commit time. The commit stage requires validating that no other transactions changed the data in a way that conflicted with ours. If there&#39;s a conflict, our transaction gets aborted. Otherwise, the database commits the writes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;FaRM uses Optimistic Concurrency Control (OCC) which reduces the need for active server participation when executing transactions.&lt;/p&gt;
&lt;h3 id=&quot;farm-machines-maintain-logs&quot;&gt;FaRM machines maintain logs &lt;a class=&quot;direct-link&quot; href=&quot;#farm-machines-maintain-logs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Before going into the details of how transactions work, note that each FaRM machine maintains logs which they use as either transaction logs or message queues. These logs are used to communicate between sender-receiver pairs in a FaRM cluster. The sender appends records to the log using one-sided RDMA writes, which the receiver acknowledges via its NIC without involving the CPU. The receiver processes records by periodically polling the head of its log.&lt;/p&gt;
&lt;h2 id=&quot;how-transactions-work&quot;&gt;How transactions work &lt;a class=&quot;direct-link&quot; href=&quot;#how-transactions-work&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;FaRM supports reading and writing multiple objects within a transaction, and guarantees &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-13-spanner/#spanner-guarantees-external-consistency&quot;&gt;strict serializability&lt;/a&gt; of all successfully committed transactions.&lt;/p&gt;
&lt;p&gt;Each object or record in the database has a 64-bit version number which FaRM uses for concurrency control and an address representing its location in a server.&lt;/p&gt;
&lt;p&gt;Any application thread can start a transaction and this thread then becomes the transaction coordinator. This transaction coordinator communicates directly with replicas and backups as shown in the figure below.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/farm-commit.png&quot; alt=&quot;FaRM commit protocol with a coordinator C, primaries on P1; P2; P3, and backups on B1; B2; B3. P1 and P2 are read and written. P3 is only read. We use dashed lines for RDMA reads, solid ones for RDMA writes, dotted ones for hardware acks, and rectangles for object data&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 4 - FaRM commit protocol with a coordinator C, primaries on P&lt;sub&gt;1&lt;/sub&gt;; P&lt;sub&gt;2&lt;/sub&gt;; P&lt;sub&gt;3&lt;/sub&gt;, and backups on B&lt;sub&gt;1&lt;/sub&gt;; B&lt;sub&gt;2&lt;/sub&gt;; B&lt;sub&gt;3&lt;/sub&gt;. P&lt;sub&gt;1&lt;/sub&gt; and P&lt;sub&gt;2&lt;/sub&gt; are read and written. P&lt;sub&gt;3&lt;/sub&gt; is only read. We use dashed lines for RDMA reads, solid ones for RDMA writes, dotted ones for hardware acks, and rectangles for object data.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;h3 id=&quot;execute&quot;&gt;Execute &lt;a class=&quot;direct-link&quot; href=&quot;#execute&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the Execute phase, the transaction coordinator uses one-sided RDMA to read objects and buffers changes to objects locally. These reads and writes happen without locking any objects— &lt;em&gt;optimism.&lt;/em&gt; The coordinator always sends the reads for an object to the primary copy of the containing region. For primaries and backups on the same machine as the coordinator, it reads and writes to the log using local memory accesses instead of RDMA.&lt;/p&gt;
&lt;p&gt;During transaction execution, the coordinator keeps track of the addresses and versions of all the objects it has accessed. It uses these in later phases.&lt;/p&gt;
&lt;p&gt;After executing the specified operations within a transaction, FaRM starts the commit process using the steps discussed next. Note that I&#39;ll detail them in a different order from Figure 4, but it will make sense why.&lt;/p&gt;
&lt;h3 id=&quot;lock&quot;&gt;Lock &lt;a class=&quot;direct-link&quot; href=&quot;#lock&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The first step in committing a transaction that changes one or more objects is that the coordinator sends a LOCK record to the primary of each written object. This LOCK record contains the transaction ID, the IDs of all the regions with objects written by the transaction, and addresses, versions, and new values of all objects written by the transaction that the destination is primary for. The coordinator uses RDMA to append this record to the log at each primary.&lt;/p&gt;
&lt;p&gt;When a primary receives this record, it attempts to lock each object at its specified version. This involves using an atomic &lt;a href=&quot;https://en.wikipedia.org/wiki/Compare-and-swap&quot;&gt;compare-and-swap&lt;/a&gt; operation on the high-order bit of its version number—which represents the &amp;quot;lock&amp;quot; flag. The primary then sends back a message to the coordinator reporting whether all the lock attempts were successful. Locking will fail if any of these conditions is met:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Another transaction has locked the object.&lt;/li&gt;
&lt;li&gt;The current version of the object differs from what the transaction read and sent in its LOCK record.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A failed lock attempt at any primary will cause the coordinator to abort the transaction by writing an ABORT record to all the primaries so they can release any held locks, after which the coordinator returns an error to the application.&lt;/p&gt;
&lt;h3 id=&quot;commit-primaries&quot;&gt;Commit primaries &lt;a class=&quot;direct-link&quot; href=&quot;#commit-primaries&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If all the primaries report successful lock attempts, then the coordinator will decide to commit the transaction. It broadcasts this decision to the primaries by appending a COMMIT-PRIMARY record to primaries&#39; logs. This COMMIT-PRIMARY record contains the ID of the transaction to commit. A primary processes the record by copying the new value of each object in the LOCK record for the transaction into memory, incrementing the object&#39;s version number, and clearing the object&#39;s lock flag.&lt;/p&gt;
&lt;p&gt;The coordinator does not wait for the primaries to process the log entry before it reports success to the client, it only needs an RDMA acknowledgement from one primary that the entry has been received and stored in the NVRAM. We&#39;ll see why this is safe later.&lt;/p&gt;
&lt;h3 id=&quot;validate&quot;&gt;Validate &lt;a class=&quot;direct-link&quot; href=&quot;#validate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The commit steps so far describe FaRM&#39;s protocol when a transaction modifies an object. But in read-only transactions, or for objects that are read but not written by a transaction, the coordinator does not write any LOCK records.&lt;/p&gt;
&lt;p&gt;The coordinator performs commit validation in these scenarios by using one-sided RDMA reads to re-fetch each object&#39;s version number and status of its lock flag. If the lock flag is set or the version number has changed since the transaction read it, the coordinator aborts the transaction.&lt;/p&gt;
&lt;p&gt;This optimization avoids holding locks in read-only transactions, which speeds up their execution.&lt;/p&gt;
&lt;h4 id=&quot;lock-%2B-validate-%3D-strict-serializability&quot;&gt;Lock + Validate = Strict Serializability &lt;a class=&quot;direct-link&quot; href=&quot;#lock-%2B-validate-%3D-strict-serializability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Under non-failure conditions, the locking and validation steps guarantee strict serializability of all committed FaRM transactions. They ensure that the result of executing concurrent transactions is the same as if they are executed one after the other, and the order that produces the result is consistent with real time.&lt;/p&gt;
&lt;p&gt;Specifically, the steps guarantee that for a transaction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If there were no conflicting transactions, the object versions it read won&#39;t have changed.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If there were conflicting transactions, it will see a changed version number or a lock on an object it accessed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s see how this plays out using some examples from the lecture.&lt;/p&gt;
&lt;h4 id=&quot;examples&quot;&gt;Examples &lt;a class=&quot;direct-link&quot; href=&quot;#examples&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In this first example, we have two simultaneous transactions, T1 and T2.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  T1:  Rx  Ly  Vx  Cy
  T2:  Ry  Lx  Vy  Cx

(Where L and V represent the Lock and Validate stages)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the LOCKs in both operations will succeed while the VALIDATEs will fail. The validation for x will fail in T1 because its lock bit has been set by T2 and Vy will fail in T2 because y has been locked by T1. Both transactions will then abort, which satisfies our desired guarantee.&lt;/p&gt;
&lt;p&gt;This next example has a similar setup.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; T1:  Rx  Ly  Vx      Cy
 T2:  Ry          Lx  Vy  Cx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example, T1 will commit while T2 will abort, since T2&#39;s Vy will see either T1&#39;s lock on y or a higher version of y.&lt;/p&gt;
&lt;h3 id=&quot;fault-tolerance---commit-backups&quot;&gt;Fault Tolerance - Commit backups &lt;a class=&quot;direct-link&quot; href=&quot;#fault-tolerance---commit-backups&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, the protocol described above is not enough to guarantee serializability when there are system failures.&lt;/p&gt;
&lt;p&gt;Recall that the coordinator needs acknowledgement from only one primary for the COMMIT-PRIMARY record before its reports success to clients. This means that a committed write could be visible as soon as the COMMIT-PRIMARY is sent, since that primary will write the data and unlock the objects involved.&lt;/p&gt;
&lt;p&gt;Thus, we could have a scenario where a transaction fails after it has committed on one primary but before committing on the others. This violates transaction atomicity, which requires that if a transaction succeeds on one machine, it must succeed on all the others.&lt;/p&gt;
&lt;p&gt;FaRM achieves this atomicity through the use of COMMIT-BACKUP records. While LOCK records tell the primaries the new values, COMMIT-BACKUP records give the same information to the backups.&lt;/p&gt;
&lt;p&gt;The key thing to note is that &lt;em&gt;before&lt;/em&gt; sending the COMMIT-PRIMARY records, the coordinator sends a COMMIT-BACKUP record to the backups of the primaries involved in a transaction and waits for acknowledgement that they are persisted on all the backups. This COMMIT-BACKUP record contains the same data as the LOCK record for a transaction.&lt;/p&gt;
&lt;p&gt;By also waiting for acknowledgement that a COMMIT-PRIMARY record has been stored from at least one primary, the coordinator ensures that at least one commit record survives any &lt;em&gt;f&lt;/em&gt; failures for transactions reported as committed to the application.&lt;/p&gt;
&lt;h3 id=&quot;truncate&quot;&gt;Truncate &lt;a class=&quot;direct-link&quot; href=&quot;#truncate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The coordinator truncates the logs at primaries and backups after receiving acknowledgements from all the primaries that they have stored a commit record.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;FaRM is innovative in its use of optimistic concurrency control to allow fast one-sided RDMA reads while providing strict serializability for distributed transactions. I recommended reading the &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-13-spanner/&quot;&gt;Spanner&lt;/a&gt; post for an alternative approach to distributed transactions.&lt;/p&gt;
&lt;p&gt;As far as I know, though, FaRM is still a research prototype and has not been deployed in any production systems so far. For all of its performance optimizations, FaRM is limited because its data must fit in the total RAM and replication occurs only within a data centre. Its API is also restrictive, as it does not support SQL operations like Spanner does.&lt;/p&gt;
&lt;p&gt;I&#39;ve also omitted some implementation details here on failure detection and reconfiguring the FaRM instance after recovery, but I hope you&#39;ve learned enough to pique your interest in the topic.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;1-link&quot;&gt; &lt;a href=&quot;#1&quot;&gt;[1]&lt;/a&gt;: &lt;a href=&quot;https://www.theregister.com/2016/04/21/storage_approaches_memory_speed_with_xpoint_and_storageclass_memory/&quot;&gt;Storage with the speed of memory? XPoint, XPoint, that&#39;s our plan&lt;/a&gt; &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;2-link&quot;&gt; &lt;a href=&quot;#2&quot;&gt;[2]&lt;/a&gt;: &lt;a href=&quot;https://www.mimuw.edu.pl/~iwanicki/courses/ds/2017/presentations/group-1/05_Lysiak.pdf&quot;&gt;Lecture Notes on Distributed Systems(Fall 2017)&lt;/a&gt; by Jacek Lysiak &lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/farm-2015.pdf&quot;&gt;No compromises: distributed transactions with consistency, availability, and performance&lt;/a&gt; - Original FaRM paper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-farm.txt&quot;&gt;Lecture 14: FaRM, Optimistic Concurrency Control&lt;/a&gt; - MIT 6.824 lecture notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cs.dartmouth.edu/~sergey/netreads/path-of-packet/Network_stack.pdf&quot;&gt;Path of a Packet in the Linux Kernel Stack&lt;/a&gt; by Ashwin Kumar Chimata.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#serializable-snapshot-isolation&quot;&gt;Serializable Snapshot Isolation&lt;/a&gt; - Earlier post on an OCC method of transaction isolation.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 13 - Spanner</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-13-spanner/"/>
		<updated>2020-09-12T12:30:49-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-13-spanner/</id>
		<content type="html">&lt;p&gt;Unlike many other databases that either choose not to support distributed transactions at all or opt for weaker consistency models, Spanner is an example of a distributed database that supports &lt;a href=&quot;https://timilearning.com/posts/consistency-models/&quot;&gt;externally consistent&lt;/a&gt; distributed transactions.&lt;/p&gt;
&lt;p&gt;This post will cover how Google Spanner implements a fault-tolerant two-phase commit protocol and how its novel TrueTime API enables it to guarantee external consistency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#overview&quot;&gt;Overview&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#spanner-partitions-data-into-paxos-groups&quot;&gt;Spanner partitions data into Paxos groups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#spanner-guarantees-external-consistency&quot;&gt;Spanner guarantees external consistency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#external-consistency-is-challenging-to-provide&quot;&gt;External consistency is challenging to provide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#spanner%27s-read-write-transactions-use-locks&quot;&gt;Spanner&#39;s read-write transactions use locks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#read-only-transactions&quot;&gt;Read-only transactions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#why-not-read-the-latest-committed-values%3F&quot;&gt;Why not read the latest committed values?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#snapshot-isolation&quot;&gt;Snapshot Isolation&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#what-if-the-clocks-on-the-replicas-are-not-synchronized%3F&quot;&gt;What if the clocks on the replicas are not synchronized?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#truetime&quot;&gt;TrueTime&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#truetime-models-the-uncertainty-in-clocks&quot;&gt;TrueTime models the uncertainty in clocks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#start-rule&quot;&gt;Start rule&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#commit-wait&quot;&gt;Commit Wait&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#using-the-rules&quot;&gt;Using the rules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;direct-link&quot; href=&quot;#overview&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;spanner-partitions-data-into-paxos-groups&quot;&gt;Spanner partitions data into Paxos groups &lt;a class=&quot;direct-link&quot; href=&quot;#spanner-partitions-data-into-paxos-groups&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Spanner partitions data across many servers in data centres spread all over the world. It manages the replication of a partition using Paxos, and each partition belongs to a Paxos group. Paxos is like &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-6-7-fault-tolerance-raft/&quot;&gt;Raft&lt;/a&gt; in that each Paxos group has a leader, and the leader replicates a log of operations to its followers. Each replica in a Paxos group is typically in a different data centre.&lt;/p&gt;
&lt;p&gt;This setup is great for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6/&quot;&gt;Partitioning&lt;/a&gt; the data means we can increase the total throughput via parallelism.&lt;/li&gt;
&lt;li&gt;Data centres fail independently and so Spanner can continue to serve clients even after a data centre failure.&lt;/li&gt;
&lt;li&gt;With global replication, clients can read data faster by going to the replicas closest to them.&lt;/li&gt;
&lt;li&gt;Paxos only requires a majority of the replicas to respond and can tolerate slow or distant replicas.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;spanner-guarantees-external-consistency&quot;&gt;Spanner guarantees external consistency &lt;a class=&quot;direct-link&quot; href=&quot;#spanner-guarantees-external-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&#39;ve written about external consistency in &lt;a href=&quot;https://timilearning.com/posts/consistency-models/&quot;&gt;another post&lt;/a&gt;, but to summarize, external consistency is the &amp;quot;gold standard&amp;quot; for isolation levels in distributed databases.&lt;/p&gt;
&lt;p&gt;Serializability is often referred to as the gold standard, but while serializability guarantees that the result of concurrent transactions will be the same as if they had executed in some serial order, external consistency (sometimes referred to as &lt;em&gt;strict serializability&lt;/em&gt;) imposes what that order must be. It guarantees that the order chosen will be one that is consistent with the real time execution of those transactions.&lt;/p&gt;
&lt;p&gt;Using the example from the linked post where we execute two transactions, T1 and T2, in a database. Let&#39;s assume that the database replicates the data across two machines A and B, which are both allowed to process transactions.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/t1-t2.PNG&quot; alt=&quot;Sample Transactions T&lt;sub&gt;1&lt;/sub&gt;  and T&lt;sub&gt;2&lt;/sub&gt; where T&lt;sub&gt;1&lt;/sub&gt;  has steps a11, a12, a13 and T&lt;sub&gt;2&lt;/sub&gt; has a21, a22 and a23&quot;&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Sample Transactions T&lt;sub&gt;1&lt;/sub&gt; and T&lt;sub&gt;2&lt;/sub&gt; &lt;/p&gt;
&lt;p&gt;A client issues transaction T&lt;sub&gt;1&lt;/sub&gt; to machine A which performs the operations a11, a12 and a13. After T&lt;sub&gt;1&lt;/sub&gt; completes, another client issues transaction T&lt;sub&gt;2&lt;/sub&gt; to machine B. Under serializability, it would be valid for transaction T&lt;sub&gt;2&lt;/sub&gt; to not see the latest value of Y written by T&lt;sub&gt;1&lt;/sub&gt;, even though T&lt;sub&gt;2&lt;/sub&gt; started after T&lt;sub&gt;1&lt;/sub&gt; completed.&lt;/p&gt;
&lt;p&gt;This is because the order where T&lt;sub&gt;2&lt;/sub&gt; executes before T&lt;sub&gt;1&lt;/sub&gt; is one of the two possible serial schedules for these transactions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sa: {a11, a12, a13, a21, a22, a23}&lt;br&gt;
Sb: {a21, a22, a23, a11, a12, a13}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This scenario could happen if the database uses asynchronous replication, and the writes from T&lt;sub&gt;1&lt;/sub&gt; have not yet replicated to machine B.&lt;/p&gt;
&lt;p&gt;External consistency guarantees that if transaction T&lt;sub&gt;1&lt;/sub&gt; commits before transaction T&lt;sub&gt;2&lt;/sub&gt; starts, then T&lt;sub&gt;2&lt;/sub&gt; will see the effects of T&lt;sub&gt;1&lt;/sub&gt; regardless of what replica it executes on.&lt;/p&gt;
&lt;p&gt;There is no difference between serializability and external consistency in a single-node database. By committing a transaction on a single-node database, it becomes visible to every transaction that starts after it.&lt;/p&gt;
&lt;h3 id=&quot;external-consistency-is-challenging-to-provide&quot;&gt;External consistency is challenging to provide &lt;a class=&quot;direct-link&quot; href=&quot;#external-consistency-is-challenging-to-provide&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One reason distributed databases are partitioned and replicated in data centres spread across multiple locations is to allow clients to make requests to the local replicas closest to them, which yields better performance. However, this arrangement makes it harder to provide strong consistency guarantees, as it means that reads of local replicas must always return fresh data. The challenge here is that a local replica may not have been involved in a Paxos majority, which means it won&#39;t reflect the latest Paxos writes.&lt;/p&gt;
&lt;p&gt;The protocols needed to provide external consistency have performed poorly in terms of availability, latency, and throughput when implemented, which is why many database designers with an arrangement similar to Spanner&#39;s have either chosen not to support distributed transactions or to provide weaker guarantees than external consistency.&lt;/p&gt;
&lt;p&gt;Spanner tackles this problem by making clients specify whether a transaction is read-only or read-write before executing and making optimizations tailored to each case. The rest of this post will explain how it does that.&lt;/p&gt;
&lt;h2 id=&quot;spanner&#39;s-read-write-transactions-use-locks&quot;&gt;Spanner&#39;s read-write transactions use locks &lt;a class=&quot;direct-link&quot; href=&quot;#spanner&#39;s-read-write-transactions-use-locks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Read-write transactions that involve only one Paxos group or partition use &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-12-distributed-transactions/#two-phase-locking&quot;&gt;two-phase locking&lt;/a&gt; for external consistency. The client issues writes to the leader of the Paxos group, which then manages the locks.&lt;/p&gt;
&lt;p&gt;For transactions that involve multiple Paxos groups, Spanner uses the two-phase commit protocol with long-held locks to guarantee that read-write transactions provide external consistency. In the &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-12-distributed-transactions/#two-phase-commit&quot;&gt;previous discussion&lt;/a&gt; of the two-phase commit protocol, we saw that one of its downsides is how &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-12-distributed-transactions/#the-coordinator-is-a-bottleneck&quot;&gt;the coordinator can be a bottleneck&lt;/a&gt;. I highlighted in the summary that if there is a coordinator failure, there is the risk of the transaction participants being stuck in a waiting state. Spanner solves this problem by combining two-phase commit with the Paxos algorithm. It replicates the transaction coordinator and participants into Paxos groups so it can automatically elect a new coordinator on failure, and the protocol is more fault-tolerant.&lt;/p&gt;
&lt;p&gt;The client buffers the write operations that occur in a transaction until it is ready to commit. At commit time, it chooses a leader of one of the Paxos groups to act as the transaction coordinator and sends a prepare message with the buffered writes and the identity of the coordinator to the leaders of the other participant groups. Each participant leader then acquires writes locks and performs the specified operations before responding to the coordinator with the status of its mini-transaction.&lt;/p&gt;
&lt;p&gt;The client also issues reads within read-write transactions to the leader replica of the relevant group, which acquires read locks and reads the most recent data. One of the other limitations of two-phase commit highlighted in the previous lecture is its proneness to deadlocks. Spanner uses the &lt;a href=&quot;http://www.mathcs.emory.edu/~cheung/Courses/554/Syllabus/8-recv+serial/deadlock-woundwait.html&quot;&gt;wound-wait locking rule&lt;/a&gt; to avoid deadlocks when reading data.&lt;/p&gt;
&lt;p&gt;By holding locks on the data and ensuring that only the Paxos leader of the partition it belongs to can read or write the data, read-write transactions in Spanner are externally consistent.&lt;/p&gt;
&lt;p&gt;Now, if Spanner only supported read-write transactions, then it would be fine to leave it with this protocol. But Spanner was built for a workload dominated by read-only transactions. The next section will cover how Spanner&#39;s protocol for read-only transactions achieves 10x latency improvements over read-write transactions.&lt;/p&gt;
&lt;h2 id=&quot;read-only-transactions&quot;&gt;Read-only transactions &lt;a class=&quot;direct-link&quot; href=&quot;#read-only-transactions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Spanner makes two optimizations to achieve greater performance in read-only transactions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;It does not hold locks or use the two-phase commit protocol to serve requests.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clients can issue reads involving multiple Paxos groups to the follower replicas of the Paxos groups, not just the leaders. This means that a client can read data from their local replica.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But these optimizations make it more difficult to provide external consistency. For example, a read request may be sent to a stale replica which may violate external consistency if it returns its latest committed value.&lt;/p&gt;
&lt;p&gt;Something else to note is that reads in a read-only transaction only see the values that had been committed &lt;em&gt;before&lt;/em&gt; the transaction started, even if a R/W transaction updates those values and commits while the transaction is running.&lt;/p&gt;
&lt;h3 id=&quot;why-not-read-the-latest-committed-values%3F&quot;&gt;Why not read the latest committed values? &lt;a class=&quot;direct-link&quot; href=&quot;#why-not-read-the-latest-committed-values%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It might seem like transactions must read the latest committed values to guarantee external consistency, but the next example from the lecture shows why this behaviour could violate the property.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Suppose we have two bank transfers, T1 and T2, and a transaction T3 that reads both.

    T1:  Wx  Wy  C
    T2:                 Wx  Wy  C
    T3:             Rx             Ry

If T3 reads the value of x from T1 and y from T2, the results won&#39;t match any serial order.
    Not T1, T2, T3 or T1, T3, T2.

We want T3 to see both of T2&#39;s writes or none.
We want T3&#39;s reads to *all* occur at the *same* point relative to T1/T2.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Read-only transactions in Spanner use snapshot isolation to prevent the violation in the example.&lt;/p&gt;
&lt;h3 id=&quot;snapshot-isolation&quot;&gt;Snapshot Isolation &lt;a class=&quot;direct-link&quot; href=&quot;#snapshot-isolation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#snapshot-isolation-and-repeatable-read&quot;&gt;snapshot isolation&lt;/a&gt;, a database keeps multiple versions of an object—each version labelled with a timestamp representing what transaction produced it. Spanner assigns a timestamp to each transaction using these rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For a read-only transaction, Spanner chooses the time at the start of the transaction as the transaction timestamp (TS). Spanner has an API layer on the machines in the cluster which uses the system clock to get the current time.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The leader of the coordinator group in charge of a read-write transaction chooses its time when commit begins as the transaction timestamp.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Snapshot isolation enforces that a read-only transaction will only see the versions of a record that have a timestamp less than its assigned transaction timestamp i.e. a snapshot of what the record was before the transaction started.&lt;/p&gt;
&lt;p&gt;This prevents the problem in the previous example as it guarantees that T3 will see either both of T2&#39;s writes or none. Here&#39;s another version of the example with snapshot isolation implemented:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                      x@10=9         x@20=8
                      y@10=11        y@20=12
    T1 @ 10:  Wx  Wy  C
    T2 @ 20:                 Wx  Wy  C
    T3 @ 15:             Rx             Ry

  &amp;quot;@ 10&amp;quot; indicates the time-stamp.

  - Now,T3&#39;s reads will both be served from the @10 versions.
    T3 won&#39;t see T2&#39;s write even though T3&#39;s read of y occurs after T2.

  - The results are now serializable: T1 T3 T2.
    The serial order is the same as the time-stamp order.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, it is okay for T3 to read an older value of y even though there is a newer value because T2 and T3 transactions are concurrent, and external consistency allows either order for concurrent transactions.&lt;/p&gt;
&lt;p&gt;But there is another problem that may arise in the example. T3 can read x from a local replica that hasn&#39;t seen T1&#39;s write because the replica wasn&#39;t in the Paxos majority. To prevent this violation, each replica maintains a &lt;em&gt;safe time&lt;/em&gt; property, which is the maximum timestamp at which it is up to date. Paxos leaders send writes to followers in timestamp order and the safe time represents the most recent timestamp a replica has seen.&lt;/p&gt;
&lt;p&gt;Before serving a read at time 20, a replica must have seen Paxos writes for time &amp;gt; 20, or it will have to wait until it is up to date.&lt;/p&gt;
&lt;h4 id=&quot;what-if-the-clocks-on-the-replicas-are-not-synchronized%3F&quot;&gt;What if the clocks on the replicas are not synchronized? &lt;a class=&quot;direct-link&quot; href=&quot;#what-if-the-clocks-on-the-replicas-are-not-synchronized%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Imagine a scenario where the clocks on each replica are wildly different from each other. Remember that these clocks are used to assign transaction timestamps. How do you think that will affect the correctness of transactions? Does your answer depend on whether we are considering read-only or read-write transactions? I&#39;ll answer these questions in this section, but I suggest taking a minute to think about them before moving on.&lt;/p&gt;
&lt;p&gt;Unsynchronized clocks will not affect the correctness of read-write transactions because they acquire locks for their operations and don&#39;t use snapshot isolation. As stated earlier, the locks guarantee external consistency and ensure that the transactions operate on recent data.&lt;/p&gt;
&lt;p&gt;But for a read-only transaction spanning multiple Paxos groups, the effect of unsynchronized clocks can play out in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If Spanner assigns a timestamp that is too large, that transaction timestamp will be higher than the safe time of the replicas in the other Paxos groups involved, and the read will block until they are up to date. Here, the result will be correct, but the execution will be slow.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the transaction timestamp is too small, the transaction will miss writes that have committed on other replicas before it started. Remember that the snapshot isolation rule mandates that a transaction can only read values below its timestamp. Let&#39;s look at the example below where T2 is assigned a timestamp less than T1, even though T1 committed first.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  r/w T0 @  0: Wx1 C
  r/w T1 @ 10:         Wx2 C
  r/o T2 @  5:                   Rx?

(C represents a commit)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would cause T2 to read the version of x at time 0—which was 1—even though T2 started after T1 committed (in real time). External consistency requires that T2 sees x=2.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The next section will discuss how Google uses its TrueTime service to synchronize computer clocks to high accuracy.&lt;/p&gt;
&lt;h2 id=&quot;truetime&quot;&gt;TrueTime &lt;a class=&quot;direct-link&quot; href=&quot;#truetime&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#unreliable-clocks&quot;&gt;Clocks are unreliable in distributed systems.&lt;/a&gt; A computer&#39;s time is defined by clocks at a collection of government labs and distributed to the computer via various protocols — &lt;a href=&quot;https://en.wikipedia.org/wiki/Network_Time_Protocol&quot;&gt;NTP&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Global_Positioning_System#Timekeeping&quot;&gt;GPS&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/WWV_(radio_station)#Service&quot;&gt;WWV&lt;/a&gt;. This distribution to different computers is subject to variable network delays, and so there&#39;s always some uncertainty in what the actual time is.&lt;/p&gt;
&lt;p&gt;TrueTime is a globally distributed clock that Google provides to applications on its servers. It uses GPS and &lt;a href=&quot;https://en.wikipedia.org/wiki/Atomic_clock&quot;&gt;atomic clocks&lt;/a&gt; as its underlying time reference to ensure better clock synchronization between the participating servers than other protocols offer. For context, Google mentions an upper bound of 7ms for the clock offsets between nodes in a cluster, while using NTP for clock synchronization gives somewhere between 100ms and 250ms.&lt;/p&gt;
&lt;h3 id=&quot;truetime-models-the-uncertainty-in-clocks&quot;&gt;TrueTime models the uncertainty in clocks &lt;a class=&quot;direct-link&quot; href=&quot;#truetime-models-the-uncertainty-in-clocks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The beauty of TrueTime is that it also models the uncertainty in clocks by representing time as an interval. When you ask for the current time, it gives you an interval that represents what the earliest and latest possible values for the current time are. TrueTime&#39;s protocol guarantees that this interval is accurate and that the correct time is somewhere in the interval.&lt;/p&gt;
&lt;p&gt;Table 1 below shows the TrueTime API.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/truetime.png&quot; alt=&quot;Table 1: TrueTime API. The argument t is of type TTstamp.&quot;&gt; &lt;/p&gt;
&lt;p&gt;From the paper, the &lt;em&gt;TT.now()&lt;/em&gt; method returns a &lt;em&gt;TTinterval&lt;/em&gt; that is guaranteed to contain the absolute time during which &lt;em&gt;TT.now()&lt;/em&gt; was invoked. Note also that the endpoints of a &lt;em&gt;TTinterval&lt;/em&gt; are of type &lt;em&gt;TTstamp&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Spanner uses TrueTime to guarantee external consistency through the &lt;em&gt;start&lt;/em&gt; and &lt;em&gt;commit wait&lt;/em&gt; rules, which we&#39;ll look at next.&lt;/p&gt;
&lt;h3 id=&quot;start-rule&quot;&gt;Start rule &lt;a class=&quot;direct-link&quot; href=&quot;#start-rule&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This states that a transaction&#39;s timestamp, TS, must be no less than the value of &lt;em&gt;TT.now().latest&lt;/em&gt;. Recall from an earlier section that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For a RO transaction, Spanner assigns TS at the start of the transaction.&lt;/li&gt;
&lt;li&gt;For a RW transaction, Spanner assigns TS when commit begins.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The start rule enforces that the value chosen for TS must be greater than or equal to &lt;em&gt;TT.now().latest&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;commit-wait&quot;&gt;Commit Wait &lt;a class=&quot;direct-link&quot; href=&quot;#commit-wait&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This applies to only read-write transactions and ensures that a transaction will not commit until the chosen transaction timestamp has passed, i.e. when &lt;em&gt;TT.after(TS)&lt;/em&gt; is true. Before a node can report that a transaction has committed, it must wait for about 7ms.&lt;/p&gt;
&lt;p&gt;This rule guarantees that any transaction that starts after a read-write transaction has committed will be given a later transaction timestamp.&lt;/p&gt;
&lt;h3 id=&quot;using-the-rules&quot;&gt;Using the rules &lt;a class=&quot;direct-link&quot; href=&quot;#using-the-rules&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s look at the example below which updates the earlier examples with intervals and the commit wait rule. The scenario here is T1 commits, then T2 starts, and so T2 must see T1&#39;s writes i.e. we need TS1 to be less than TS2.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                   |1-----------10| |11--------------20|
  r/w T1 @ 10:         Wx2 P           C
                                 |10--------12|
  r/o T2 @ 12:                           Rx?

(where P stands for the &#39;prepare&#39; phase in the two-phase protocol)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we assume the database assigns TS1 as 10, which guarantees that C will occur after time 10 due to commit wait. We also assume that Rx in T2 starts after T1 commits, and thus after time 10. T2 chooses &lt;em&gt;TT.now().latest()&lt;/em&gt; as its transaction time (start rule), which is after the current time, which is after 10. Therefore TS2 &amp;gt; TS1.&lt;/p&gt;
&lt;p&gt;Going back to the definition of external consistency which is that a transaction that starts after an earlier one commits should see the effects of the earlier one, this protocol provides that safety because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Commit wait guarantees that the timestamp for a read-write transaction is in the past.&lt;/li&gt;
&lt;li&gt;The timestamp used for snapshot isolation in a read-only transaction (&lt;em&gt;TT.now().latest&lt;/em&gt;) is guaranteed to be greater than or equal to the correct time, and thus greater than or equal to the timestamp of any previously committed transaction (because of commit wait).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Spanner has inspired the creation of other systems like &lt;a href=&quot;https://www.cockroachlabs.com/&quot;&gt;CockroachDB&lt;/a&gt; and &lt;a href=&quot;https://www.yugabyte.com/&quot;&gt;YugaByteDB&lt;/a&gt; in its use of tightly synchronized clocks to provide external consistency, but its design hasn&#39;t come without criticism. &lt;em&gt;&amp;quot;NewSQL database systems are failing to guarantee consistency,&amp;quot;&lt;/em&gt; Daniel Abadi wrote in a &lt;a href=&quot;https://dbmsmusings.blogspot.com/2018/09/newsql-database-systems-are-failing-to.html&quot;&gt;2018 post&lt;/a&gt;, &lt;em&gt;&amp;quot;and I blame Spanner.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/spanner.pdf&quot;&gt;Spanner: Google&#39;s Globally-Distributed Database&lt;/a&gt; - Original Spanner paper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-spanner.txt&quot;&gt;Lecture 13: Spanner&lt;/a&gt; - MIT 6.824 Lecture Notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/spanner/docs/whitepapers/life-of-reads-and-writes#top_of_page&quot;&gt;Life of Cloud Spanner Reads &amp;amp; Writes&lt;/a&gt; - Official Spanner documentation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fauna.com/blog/serializability-vs-strict-serializability-the-dirty-secret-of-database-isolation-levels&quot;&gt;Serializability vs “Strict” Serializability: The Dirty Secret of Database Isolation Levels&lt;/a&gt; by Daniel Abadi and Matt Freels.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.bailis.org/blog/linearizability-versus-serializability&quot;&gt;Linearizability vs Serializability&lt;/a&gt; by Peter Bailis.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techcommunity.microsoft.com/t5/sql-server/serializable-vs-snapshot-isolation-level/ba-p/383281&quot;&gt;Serializable vs Snapshot Isolation&lt;/a&gt; by Craig Freedman.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dbmsmusings.blogspot.com/2018/09/newsql-database-systems-are-failing-to.html&quot;&gt;NewSQL database systems are failing to guarantee consistency, and I blame Spanner&lt;/a&gt; by Daniel Abadi&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://muratbuffalo.blogspot.com/2013/07/spanner-googles-globally-distributed_4.html&quot;&gt;Spanner: Google&#39;s Globally-Distributed Database&lt;/a&gt; - Murat Demirbas&#39; summary of the Spanner paper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cockroachlabs.com/blog/living-without-atomic-clocks/&quot;&gt;Living without Atomic Clocks&lt;/a&gt; by Spencer Kimball and Irfan Sharif on the CockroachDB blog. CockroachDB is a database based on Spanner&#39;s design.&lt;/li&gt;
&lt;li&gt;I&#39;ve written more about snapshot isolation in another &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#snapshot-isolation-and-repeatable-read&quot;&gt;post&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 12 - Distributed Transactions</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-12-distributed-transactions/"/>
		<updated>2020-08-13T23:50:35-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-12-distributed-transactions/</id>
		<content type="html">&lt;p&gt;Distributed databases typically divide their tables into &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6/&quot;&gt;partitions&lt;/a&gt; spread across different servers which get accessed by many clients. In these databases, client transactions often span the different servers as the transactions may need to read from various partitions. A distributed transaction is a database transaction which spans multiple servers.&lt;/p&gt;
&lt;p&gt;A transaction with the correct behaviour must exhibit the following, also known as the &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#the-meaning-of-acid&quot;&gt;ACID properties&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Atomicity&lt;/strong&gt;: Either all writes in the transaction succeed or none, even in the presence of failures.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: The transaction must obey application-specific invariants.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Isolation&lt;/strong&gt;: There must be no interference from concurrently executing transactions. The ideal isolation level is &lt;em&gt;serializable isolation.&lt;/em&gt; This guarantees that the result of the execution of concurrent transactions will be the same as if the database executed the transactions one after the order. I&#39;ve written about serializability more extensively &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#serializability&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://timilearning.com/posts/consistency-models/&quot;&gt;here&lt;/a&gt;, and I&#39;ll recommend reading them if you&#39;re interested in learning more about it.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Durability&lt;/strong&gt;: Committed writes must be permanent.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These properties are more difficult to guarantee when a transaction involves multiple servers. For example, the transaction may succeed on some servers and fail on others. There needs to be a protocol to ensure that the database maintains atomicity even in that scenario. Also, if several clients are executing transactions concurrently, we must take extra care to control access to the shared data for those transactions.&lt;/p&gt;
&lt;p&gt;This post will focus on how distributed databases provide &lt;em&gt;atomicity&lt;/em&gt; through an atomic commit protocol known as Two-phase commit, and how concurrency control methods like Two-phase locking help to guarantee &lt;em&gt;serializability&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I&#39;ve written about some of these topics in other posts on this site, so I&#39;ll be posting links to them if you want more detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#concurrency-control&quot;&gt;Concurrency Control&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#pessimistic-concurrency-control&quot;&gt;Pessimistic Concurrency Control&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#simple-locking&quot;&gt;Simple locking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#two-phase-locking&quot;&gt;Two-phase locking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#atomic-commit&quot;&gt;Atomic Commit&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#two-phase-commit&quot;&gt;Two-phase commit&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-coordinator-is-a-bottleneck&quot;&gt;The coordinator is a bottleneck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#two-phase-commit-and-raft&quot;&gt;Two-phase commit and Raft&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;concurrency-control&quot;&gt;Concurrency Control &lt;a class=&quot;direct-link&quot; href=&quot;#concurrency-control&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Concurrency control ensures that concurrent transactions execute &lt;em&gt;correctly,&lt;/em&gt; i.e.&lt;em&gt;,&lt;/em&gt; that they are serializable. There are two classes of concurrency control for transactions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pessimistic:&lt;/strong&gt; Here, a transaction must place locks on the shared data objects that it wants to access before doing any actual reading or writing. When another transaction wants to access any of those records, it must wait for the original transaction to release those locks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimistic:&lt;/strong&gt; In this class, transactions read or modify records without placing any locks on them. However, when it&#39;s time to commit the transaction, the system checks if the reads/writes were serializable, i.e. if the transaction&#39;s results are consistent with a serial order of execution. If not, the database aborts the transaction and retries it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pessimistic concurrency control is faster if there are frequent conflicts between concurrent transactions, while optimistic concurrency control is faster when the conflicts are rare. We&#39;ll cover optimistic concurrency control in a later post.&lt;/p&gt;
&lt;h3 id=&quot;pessimistic-concurrency-control&quot;&gt;Pessimistic Concurrency Control &lt;a class=&quot;direct-link&quot; href=&quot;#pessimistic-concurrency-control&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are two pessimistic concurrency control mechanisms highlighted in the lecture material for ensuring serializable transactions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple locking&lt;/li&gt;
&lt;li&gt;Two-phase locking&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;simple-locking&quot;&gt;Simple locking &lt;a class=&quot;direct-link&quot; href=&quot;#simple-locking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In simple locking, each transaction must first acquire a lock for &lt;em&gt;every&lt;/em&gt; shared data object that it intends to read or write before it does any actual reading or writing. It then releases its locks only after the transaction has committed or aborted.&lt;/p&gt;
&lt;p&gt;One downside of this method is that applications that discover which objects need to be read by reading other shared data will have to lock every object that they &lt;em&gt;might&lt;/em&gt; need to read. Thus, a transaction may end up locking more data objects than needed.&lt;/p&gt;
&lt;h4 id=&quot;two-phase-locking&quot;&gt;Two-phase locking &lt;a class=&quot;direct-link&quot; href=&quot;#two-phase-locking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Two-phase locking (or 2PL) differs from simple locking in that a transaction only acquires locks as needed. It works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each transaction acquires locks as it proceeds its operation i.e. it monotonically increases the locks it holds until it requires no more locks. This is the &lt;em&gt;first phase&lt;/em&gt; of the process.&lt;/li&gt;
&lt;li&gt;After committing or aborting the transaction, it releases the locks. This is the &lt;em&gt;second phase.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two-phase locking is prone to deadlocks. A scenario involving two transactions T1 and T2, as shown below, is a real possibility in this protocol.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                                  T1      T2
                                  get(x)  get(y)
                                  get(y)  get(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The system must be able to detect cycles or specify a lock timeout, after which it must abort a blocked transaction. This is an issue even for single-node databases, as long as multiple clients can access the database at the same time. The database must be able to detect deadlocks and abort a transaction when that happens. &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/&quot;&gt;This&lt;/a&gt; post I wrote earlier goes into more detail about 2PL and transaction isolation levels.&lt;/p&gt;
&lt;h2 id=&quot;atomic-commit&quot;&gt;Atomic Commit &lt;a class=&quot;direct-link&quot; href=&quot;#atomic-commit&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So far, we have discussed how concurrency control methods ensure that transactions are serializable. This next challenge, however, is more peculiar to distributed transactions. As stated earlier, the outcome on the individual servers involved in a distributed transaction may vary if one or more servers fail. To guarantee the atomicity property of transactions, we must take extra care to ensure that all the servers involved come to the same decision on the transaction outcome.&lt;/p&gt;
&lt;h3 id=&quot;two-phase-commit&quot;&gt;Two-phase commit &lt;a class=&quot;direct-link&quot; href=&quot;#two-phase-commit&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Two-phase commit(or 2PC) is a protocol used to guarantee atomicity in distributed transactions. Note that the only similarity it shares with Two-phase locking is in the naming, they do different things.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/two-phase-commit.png&quot; alt=&quot;Figure 1: A successful execution of two-phase commit (2PC)&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 1: A successful execution of two-phase commit (2PC)&lt;sup&gt;\[1\]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Two-phase commit works as follows for a distributed transaction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The database adds another entity, known as the &lt;em&gt;transaction coordinator&lt;/em&gt;, to be in charge of the transaction.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All the other servers involved in the transaction are called &lt;em&gt;participants&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The transaction coordinator first delegates the writes in the transaction to the participants. Each participant creates a &lt;em&gt;nested&lt;/em&gt; transaction from the original one, executes the operations which may require holding locks, and sends an acknowledgement to the coordinator.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the coordinator receives the acknowledgement messages, it begins the first phase of the protocol. In this phase, the coordinator sends PREPARE messages to the participants. Each participant then responds to the coordinator by telling it whether it is PREPARED to commit or abort the transaction, based on the outcome of the nested transaction.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If &lt;em&gt;any&lt;/em&gt; of the participants responds with an abort message, the coordinator decides to abort the whole transaction. The coordinator commits a transaction only if all the participants are ready to commit. The second phase starts when the coordinator creates a COMMITTED or ABORTED record for the overall transaction based on these conditions, and stores that outcome in its durable log. It then broadcasts that decision to the participant nodes as the outcome of the overall transaction.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that once a participant promises that it can commit the transaction, it must fulfil that promise regardless of failures. This is done by storing its outcome in a durable log before responding to the coordinator, so it can read from that log on recovery.&lt;/p&gt;
&lt;h4 id=&quot;the-coordinator-is-a-bottleneck&quot;&gt;The coordinator is a bottleneck &lt;a class=&quot;direct-link&quot; href=&quot;#the-coordinator-is-a-bottleneck&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The major downside of the two-phase commit protocol is if the coordinator fails before it can broadcast the outcome to the participants, the participants may get stuck in a waiting state. A participant that has indicated that it&#39;s prepared to commit cannot decide the outcome of the transaction on its own, as another participant may be prepared to abort. Also, a stuck participant cannot decide on its own to abort the transaction, because the coordinator might have sent a COMMIT message to another participant before it crashed.&lt;/p&gt;
&lt;p&gt;This is not ideal because the participants may hold locks on shared objects while they are stuck in the waiting state, and thus may prevent other transactions from progressing.&lt;/p&gt;
&lt;p&gt;We can improve the fault tolerance of 2PC by integrating it with a consensus algorithm, which will get discussed next.&lt;/p&gt;
&lt;h4 id=&quot;two-phase-commit-and-raft&quot;&gt;Two-phase commit and Raft &lt;a class=&quot;direct-link&quot; href=&quot;#two-phase-commit-and-raft&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Consensus algorithms like &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-6-7-fault-tolerance-raft/&quot;&gt;Raft&lt;/a&gt; solve a different problem from atomic commit protocols. We use Raft to get high availability by replicating the data on multiple servers, where all servers do the same thing. This differs from two-phase commit in that 2PC does not help with availability, and all the participant servers here perform different operations. 2PC also requires that all the servers must do their part, unlike Raft, which only needs a majority.&lt;/p&gt;
&lt;p&gt;However, we can combine the two-phase commit protocol with a consensus algorithm as shown below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/two-phase-and-raft.png&quot; alt=&quot;Figure 2: Using 2PC with a distributed consensus algorithm&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 2: Using 2PC with a distributed consensus algorithm&lt;/p&gt;
&lt;p&gt;In Figure 2, the transaction coordinator(Tc) and the participants(A and B) each form a Raft group with three replicas. We can then perform 2PC among the leaders of each Raft group. This way, we can tolerate failures and still make progress with the system, as Raft will automatically elect a new leader. The next lecture will be on &lt;a href=&quot;https://cloud.google.com/spanner&quot;&gt;Google Spanner&lt;/a&gt;, which combines 2PC with the Paxos algorithm.&lt;/p&gt;
&lt;p&gt;[1] By Martin Kleppmann in &lt;a href=&quot;https://dataintensive.net/&quot;&gt;Designing Data-Intensive Applications.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Chapter 9 of &lt;a href=&quot;https://ocw.mit.edu/resources/res-6-004-principles-of-computer-system-design-an-introduction-spring-2009/online-textbook/&quot;&gt;Principles of Computer System Design: An Introduction, Part I.&lt;/a&gt; by Jerome H. Saltzer and M. Frans Kaashoek&lt;/li&gt;
&lt;li&gt;Chapters 7 and 9 of &lt;a href=&quot;http://dataintensive.net/&quot;&gt;Designing Data-Intensive Applications&lt;/a&gt; by Martin Kleppmann.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-2pc.txt&quot;&gt;Lecture 12: Distributed Transactions&lt;/a&gt; - MIT 6.824 Lecture Notes.&lt;/li&gt;
&lt;li&gt;I&#39;ve gone into more detail about 2PC in &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-2/#atomic-commit-and-two-phase-commit-(2pc)&quot;&gt;another post.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 11 - Cache Consistency, Frangipani</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-11-cache-consistency-frangipani/"/>
		<updated>2020-07-29T23:56:45-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-11-cache-consistency-frangipani/</id>
		<content type="html">&lt;p&gt;The ideal distributed file system would guarantee that all its users have coherent access to a shared set of files and be easily scalable. It would also be fault-tolerant and require minimal human administration.&lt;/p&gt;
&lt;p&gt;Frangipani is a distributed file system that approximates this ideal by providing a consistent view of shared files while maintaining a cache for each user, offering the ability to scale up by adding new Frangipani servers, being able to recover automatically from server failures, and providing easy administration.&lt;/p&gt;
&lt;p&gt;The post will focus on how Frangipani maintains cache coherence through the interaction between its two-layer structure and a distributed lock service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#frangipani&quot;&gt;Frangipani&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#overview&quot;&gt;Overview&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#frangipani-maintains-a-write-back-cache&quot;&gt;Frangipani maintains a write-back cache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#synchronization-and-cache-coherence&quot;&gt;Synchronization and Cache Coherence&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#frangipani-uses-multiple-reader%2Fsingle-writer-locks&quot;&gt;Frangipani uses multiple-reader/single-writer locks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dealing-with-conflicts&quot;&gt;Dealing with conflicts&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#a-read-lock-holder-must-invalidate-its-cache-entries&quot;&gt;A read lock holder must invalidate its cache entries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#a-write-lock-holder-must-flush-its-cache-entries&quot;&gt;A write lock holder must flush its cache entries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-locking-protocol-ensures-cache-coherence&quot;&gt;The locking protocol ensures cache coherence&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#logging&quot;&gt;Logging&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#frangipani-does-not-log-user-data&quot;&gt;Frangipani does not log user data&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#recovery&quot;&gt;Recovery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#frangipani-vs-gfs&quot;&gt;Frangipani vs GFS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;frangipani&quot;&gt;Frangipani &lt;a class=&quot;direct-link&quot; href=&quot;#frangipani&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;direct-link&quot; href=&quot;#overview&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Frangipani is built on top of a distributed storage service named Petal, which provides virtual disks to its clients. Petal’s virtual disks are similar to physical disks in the way that data is written and read in blocks. It also provides the option to replicate data for high availability. Much of Frangipani’s abilities to be fault-tolerant, scalable and provide easy administration are inherited from Petal.&lt;/p&gt;
&lt;p&gt;A typical setup consists of multiple Frangipani servers running on top of a shared Petal virtual disk as shown in Figure 1 below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/frangipani.png&quot; alt=&quot;Figure 1: Frangipani layering. Several interchangeable Frangipani servers provide access to one set of files on one Petal virtual disk&quot;&gt; &lt;/p&gt;
&lt;h4 id=&quot;frangipani-maintains-a-write-back-cache&quot;&gt;Frangipani maintains a write-back cache &lt;a class=&quot;direct-link&quot; href=&quot;#frangipani-maintains-a-write-back-cache&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Frangipani was built for a use case where all the servers are under a common administration e.g. a research lab with collaborating users. In such an environment, each user’s workstation will have a Frangipani file server module sitting below the user programs.&lt;/p&gt;
&lt;p&gt;In this scenario, most of the operations will involve a user accessing their files. Frangipani makes these operations &lt;em&gt;fast&lt;/em&gt; by maintaining a &lt;a href=&quot;https://www.d.umn.edu/~gshute/arch/cache-coherence.xhtml&quot;&gt;write-back cache&lt;/a&gt; on each workstation. However, a user may occasionally want to access files written by another user, or even access their files on another workstation. The goal in these cases is that the operations are &lt;em&gt;correct.&lt;/em&gt; That is, we want every read for a file from one workstation to see the latest write to that file, despite the file being in another workstation’s cache. Herein lies the challenge of cache coherence: &lt;em&gt;how can we keep the data across multiple caches consistent?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Next up, we’ll discuss Frangipani’s approach to solving this problem.&lt;/p&gt;
&lt;h3 id=&quot;synchronization-and-cache-coherence&quot;&gt;Synchronization and Cache Coherence &lt;a class=&quot;direct-link&quot; href=&quot;#synchronization-and-cache-coherence&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;frangipani-uses-multiple-reader%2Fsingle-writer-locks&quot;&gt;Frangipani uses multiple-reader/single-writer locks &lt;a class=&quot;direct-link&quot; href=&quot;#frangipani-uses-multiple-reader%2Fsingle-writer-locks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a Frangipani server wants to read a file or directory, it requests for a &lt;em&gt;read lock&lt;/em&gt; on the object which enables the server to load relevant data from disk into its cache.&lt;/p&gt;
&lt;p&gt;Similarly, a server updates a file or directory by requesting for a &lt;em&gt;write lock&lt;/em&gt;, after which it can read or write associated data from the disk and cache it.&lt;/p&gt;
&lt;p&gt;Multiple servers can hold read locks for an object, but those locks must be released before a write lock request can be granted. A Frangipani server gets a &lt;em&gt;lease&lt;/em&gt; from the lock service when a lock request is granted, and it must continually renew this lease before a specified expiration time. Otherwise, the lock server will mark it as failed and reallocate the locks.&lt;/p&gt;
&lt;h4 id=&quot;dealing-with-conflicts&quot;&gt;Dealing with conflicts &lt;a class=&quot;direct-link&quot; href=&quot;#dealing-with-conflicts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;h5 id=&quot;a-read-lock-holder-must-invalidate-its-cache-entries&quot;&gt;A read lock holder must invalidate its cache entries &lt;a class=&quot;direct-link&quot; href=&quot;#a-read-lock-holder-must-invalidate-its-cache-entries&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;When a conflicting lock request comes in for a file/directory, the lock service asks the current lock holder to release its lock. For a server holding a read lock, it will be asked to release its lock if a write lock request comes in. When this happens, the server must invalidate the object&#39;s entry in its cache before complying. This ensures that the server must fetch fresh data from the disk for any subsequent reads to that file or directory.&lt;/p&gt;
&lt;h5 id=&quot;a-write-lock-holder-must-flush-its-cache-entries&quot;&gt;A write lock holder must flush its cache entries &lt;a class=&quot;direct-link&quot; href=&quot;#a-write-lock-holder-must-flush-its-cache-entries&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;A server holding a write lock may be asked to release its lock or downgrade it to a read lock. When that happens, it must first flush the dirty data to disk before complying. Note that if it is only downgrading its lock, it can still keep the cached data since no other server will update it. The upshot of this is that the cached copy of a server’s disk block can differ from the on-disk version &lt;em&gt;only&lt;/em&gt; if it holds the write lock for that block.&lt;/p&gt;
&lt;h4 id=&quot;the-locking-protocol-ensures-cache-coherence&quot;&gt;The locking protocol ensures cache coherence &lt;a class=&quot;direct-link&quot; href=&quot;#the-locking-protocol-ensures-cache-coherence&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Summarizing this section with a quote from the paper:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Frangipani’s locking protocol ensures that updates requested to the same data by different servers are serialized. A write lock that covers dirty data can change owners only after the dirty data has been written to Petal, either by the original lock holder or by a recovery demon running on its behalf.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This protocol ensures that reads in Frangipani always see the latest writes, guaranteeing cache coherence.&lt;/p&gt;
&lt;h3 id=&quot;logging&quot;&gt;Logging &lt;a class=&quot;direct-link&quot; href=&quot;#logging&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Frangipani keeps track of &lt;em&gt;metadata&lt;/em&gt; updates in a &lt;a href=&quot;https://timilearning.com/posts/data-storage-on-disk/part-one/#write-ahead-logs&quot;&gt;write-ahead log&lt;/a&gt; to simplify failure recovery and improve the performance of the system. The paper defines metadata as any on-disk data structure other than the contents of an ordinary file. This could refer to information about a directory, or pointers to the location of the files it contains.&lt;/p&gt;
&lt;p&gt;Each Frangipani server has its own private log in Petal and before making a metadata update, it creates a log record describing the changes and appends the record to its in-memory log. This log is then written to Petal before the actual metadata is modified in its permanent location.&lt;/p&gt;
&lt;p&gt;Frangipani assigns a &lt;em&gt;version number&lt;/em&gt; for a metadata block each time it gets updated. A metadata update could span multiple blocks, and for each block that a log record updates, the record contains a description of the changes and the new version number.&lt;/p&gt;
&lt;h4 id=&quot;frangipani-does-not-log-user-data&quot;&gt;Frangipani does not log user data &lt;a class=&quot;direct-link&quot; href=&quot;#frangipani-does-not-log-user-data&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Note that Frangipani does not log the user data, only metadata is logged. This means that if a user on a workstation writes data to a file in its cache and the workstation crashes immediately, the recently written data may be lost. This is the same property on ordinary Unix file systems today. Applications that need stronger recovery guarantees can call &lt;em&gt;fsync&lt;/em&gt; to flush the cache to disk immediately a file is written.&lt;/p&gt;
&lt;p&gt;For example, if a user adds a new file with contents to a directory, Frangipani will log that a new file has been added to the directory, but it will not know the file contents until the cached data is flushed to disk. Note that if another workstation had been granted a read lock for the file before the crash happened, Frangipani’s locking protocol guarantees that the user changes from the original server must have been written to disk beforehand.&lt;/p&gt;
&lt;h3 id=&quot;recovery&quot;&gt;Recovery &lt;a class=&quot;direct-link&quot; href=&quot;#recovery&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There is a recovery daemon which helps to manage the recovery of failed servers. A failure can be detected in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When the lock service asks for a lock back and does not get a reply.&lt;/li&gt;
&lt;li&gt;A client of a Frangipani server not receiving a response.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When a Frangipani server crashes while holding locks, the locks that it owns cannot be released without performing the necessary recovery actions. Specifically, the crashed server’s logs must be processed and any pending updates must be written to Petal.&lt;/p&gt;
&lt;p&gt;The lock service performs recovery by asking another Frangipani server to process the crashed server’s logs and apply pending updates. The recovery server is itself granted a lock to ensure exclusive access to the crashed server’s log.&lt;/p&gt;
&lt;p&gt;Frangipani uses the version number attached to each metadata block to ensure that recovery never replays a log record that describes an update which has already been completed.&lt;/p&gt;
&lt;h3 id=&quot;frangipani-vs-gfs&quot;&gt;Frangipani vs GFS &lt;a class=&quot;direct-link&quot; href=&quot;#frangipani-vs-gfs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;GFS is another system which has been covered earlier in a &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-3-gfs/&quot;&gt;post&lt;/a&gt; for this course. Although they bear a similarity in that they are both distributed file systems, a major architectural difference is that GFS does not have caches, since its goal is good performance for sequential reads and writes of large files that are too big to fit in a cache. As a result, it needs no cache coherence protocol and its clients are relatively simple, unlike Frangipani workstations.&lt;/p&gt;
&lt;p&gt;Another difference is that unlike Frangipani which presents itself as an actual file system, applications have to be explicitly written to use GFS via library calls. In other words, Frangipani runs at the kernel level while GFS runs at the application level.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although some organizations today still store user and project files on distributed file systems, their importance has waned with the rise of laptops (which must be self-contained) and commercial cloud services. Also, the rise of web sites, big data, and cloud computing has shifted the focus of storage system development from file servers to database-like servers which provide a key/value interface.&lt;/p&gt;
&lt;p&gt;However, Frangipani still presents some interesting ideas around cache coherence, distributed crash recovery, distributed transactions, and how these all interact with each other. Note that there are some limitations in its design, such as how locks are held on entire files/directories and the possibility of redundant logging since Petal also maintains its own log.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/thekkath-frangipani.pdf&quot;&gt;Frangipani: A Scalable Distributed File System&lt;/a&gt; - Original paper by Chandramohan A. Thekkath, Timothy Mann, and Edward K. Lee published in 1997.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-frangipani.txt&quot;&gt;Lecture 11: Frangipani&lt;/a&gt; - MIT 6.824 Lecture Notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.d.umn.edu/~gshute/arch/cache-coherence.xhtml&quot;&gt;Cache coherence&lt;/a&gt; by Gary Shute.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 10 -  Cloud Replicated DB, Aurora</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-10-aurora/"/>
		<updated>2020-07-15T09:40:36-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-10-aurora/</id>
		<content type="html">&lt;p&gt;Amazon Aurora is a distributed database service provided by AWS. Its original &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/aurora.pdf&quot;&gt;paper&lt;/a&gt; describes the considerations in building a database for the cloud and details how Aurora&#39;s architecture differs from many traditional databases today. This post will explain how traditional databases work and then highlight how Aurora provides great performance through quorum writes and by building a database around the log.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#how-databases-work-%28simplified%29&quot;&gt;How databases work (simplified)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#aurora&quot;&gt;Aurora&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#aurora-decouples-the-storage-layer&quot;&gt;Aurora decouples the storage layer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-log-is-the-database&quot;&gt;The log is the database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#aurora-uses-quorum-writes&quot;&gt;Aurora uses quorum writes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-database-instance-is-replicated-too&quot;&gt;The database instance is replicated too&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#aurora-does-not-need-quorum-reads&quot;&gt;Aurora does not need quorum reads&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-databases-work-(simplified)&quot;&gt;How databases work (simplified) &lt;a class=&quot;direct-link&quot; href=&quot;#how-databases-work-(simplified)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Traditional relational databases comprise many units that work together: the &lt;em&gt;transport module&lt;/em&gt; which communicates with clients and receives queries, the &lt;em&gt;query processor&lt;/em&gt; which parses a query and creates a query plan to be carried out, the &lt;em&gt;execution engine&lt;/em&gt; which collects the results of the execution of the operations in the plan, and the &lt;em&gt;storage engine.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The storage engine interacts with the execution engine and is responsible for the actual execution of the query. Databases typically make use of a two-level memory hierarchy: the faster main memory (RAM) and the slower persistent storage (disk). The storage engine helps to manage both the data in memory and on disk. The main memory is used to prevent frequent access to the disk.&lt;/p&gt;
&lt;p&gt;The database organizes its records into &lt;em&gt;pages.&lt;/em&gt; When a page in the database is about to be updated, the page is first retrieved from disk and stored in a &lt;em&gt;page cache&lt;/em&gt; in memory. The changes are then made against that cached page until it is eventually synchronized with the persistent storage. A cached page is said to be &lt;em&gt;dirty&lt;/em&gt; when it has been updated in memory and it needs to be &lt;em&gt;flushed&lt;/em&gt; back on disk.&lt;/p&gt;
&lt;p&gt;Let&#39;s consider the example of a database transaction which involves updating two records: A and B. It will involve the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The data pages on which the records are stored are located on disk and loaded into the page cache.&lt;/li&gt;
&lt;li&gt;The database then generates redo log records from the changes that will be made to the pages. A redo log record consists of the difference between the before-image of a page and its after-image as a result of any changes made. These changes are then applied to the cached pages.&lt;/li&gt;
&lt;li&gt;When the transaction commits, these log records are durably persisted to a &lt;a href=&quot;https://timilearning.com/posts/data-storage-on-disk/part-one/#write-ahead-logs&quot;&gt;write-ahead log&lt;/a&gt; stored on disk. The modified data pages may still be kept in memory and written back to disk later. In the event of a crash that leads to the loss of the in-memory data, the write-ahead log helps with recovery by ensuring that the database can still apply the logged changes to the &lt;a href=&quot;https://timilearning.com/posts/data-storage-on-disk/part-two/&quot;&gt;on-disk structure&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;After a period, the dirty pages are written back to disk.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The storage engine has a &lt;em&gt;buffer manager&lt;/em&gt; for managing the page cache. Besides the buffer manager, it is made up of other components such as the &lt;em&gt;transaction manager&lt;/em&gt; which schedules and coordinates transactions, the &lt;em&gt;lock manager&lt;/em&gt; which prevents concurrent access to shared resources that would violate data integrity, and the &lt;em&gt;log manager&lt;/em&gt; which keeps track of the write-ahead log.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I&#39;ve written another &lt;a href=&quot;https://timilearning.com/posts/data-storage-on-disk/part-one/&quot;&gt;post&lt;/a&gt; that goes into more detail about how databases work.&lt;/p&gt;
&lt;h2 id=&quot;aurora&quot;&gt;Aurora &lt;a class=&quot;direct-link&quot; href=&quot;#aurora&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;aurora-decouples-the-storage-layer&quot;&gt;Aurora decouples the storage layer &lt;a class=&quot;direct-link&quot; href=&quot;#aurora-decouples-the-storage-layer&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In a monolithic database setup, the database runs on the same physical machine as the storage volume. However, in many modern distributed databases, the storage layer is decoupled from the database layer and replicated across multiple nodes to achieve scalability and resilience. While this is helpful for better fault tolerance, the downside is that communication between the database layer and the storage layer now happens via the network and the bottleneck lies there. Each write to the database could involve multiple write operations at the database layer, in what&#39;s known as &lt;a href=&quot;https://timilearning.com/posts/data-storage-on-disk/part-two/#write-amplification-in-b-trees&quot;&gt;write amplification&lt;/a&gt;, and all the communication between the database layer and the storage layer will happen over the network.&lt;/p&gt;
&lt;p&gt;For example, the figure below illustrates write amplification. The setup shown is a synchronous mirrored MySQL configuration which achieves high availability across data centres. Each &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html&quot;&gt;availability zone(AZ)&lt;/a&gt; has a MySQL instance with networked storage on &lt;a href=&quot;https://aws.amazon.com/ebs/&quot;&gt;Amazon Elastic Block Store (EBS)&lt;/a&gt;, with the primary instance being in AZ1 and the standby instance in AZ2. There is also a primary EBS volume which is synchronized with the standby EBS using &lt;a href=&quot;https://en.wikipedia.org/wiki/Disk_mirroring&quot;&gt;software mirroring&lt;/a&gt;.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/aurora-mirrored-setup.png&quot; alt=&quot;Network IO in mirrored MySQL&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 1 - Network IO in mirrored MySQL&lt;/p&gt;
&lt;p&gt;From the paper:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Figure 1 shows the various types of data that the engine needs to write: the redo log, the binary (statement) log that is archived to Amazon Simple Storage Service (S3) in order to support point-in-time restores, the modified data pages, a second temporary write of the data page (double-write) to prevent torn pages, and finally the metadata files.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This model shown in the setup is undesirable for two major reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There is a high network load involved in moving the data pages around from the database instance to the storage volumes.&lt;/li&gt;
&lt;li&gt;All four EBS volumes must respond for a write to be complete. This means that the process can be slowed down by at least one faulty node, which makes it less fault tolerant.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Aurora tackles these problems by offloading the processing of the redo log to the storage engine and through quorum writes. The next few sections will go into more detail about these optimizations.&lt;/p&gt;
&lt;h3 id=&quot;the-log-is-the-database&quot;&gt;The log is the database &lt;a class=&quot;direct-link&quot; href=&quot;#the-log-is-the-database&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As described above, a traditional database generates a redo log record when it modifies a data page in a transaction. The log applicator then applies this log record to the in-memory before-image of the page to produce the after-image. This log record must be persisted on disk for the transaction to be committed, but the dirty data page can be written back to disk at a later time.&lt;/p&gt;
&lt;p&gt;Aurora reworks this process by having the log applicator at the storage layer in addition to the database layer. This way, no pages are ever written from the database layer to the storage layer. Redo log records are the only writes that ever cross the network. These records are much smaller than data pages and hence reduce the network load. The log applicator generates any relevant data pages at the storage tier.&lt;/p&gt;
&lt;p&gt;Note that the log applicator is still present at the database layer. This way, we can still modify cached data pages based on the redo log records and read up-to-date values from them. The difference now is that those dirty pages are not written back to the storage layer; instead, only the log records are written back. There is a caveat on what redo records can be applied by the log applicator in the database layer, and that will be discussed in the next section.&lt;/p&gt;
&lt;h3 id=&quot;aurora-uses-quorum-writes&quot;&gt;Aurora uses quorum writes &lt;a class=&quot;direct-link&quot; href=&quot;#aurora-uses-quorum-writes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Aurora &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6/&quot;&gt;partitions&lt;/a&gt; the storage volume into fixed-size &lt;em&gt;segments&lt;/em&gt; of size 10 GB. Each partition is replicated six ways into Protection Groups (PGs)&lt;em&gt;.&lt;/em&gt; A Protection Group is made up of six 10GB segments organized across three availability zones, with two segments in each availability zone. Each write must achieve a &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-5/#quorums-for-reading-and-writing&quot;&gt;quorum&lt;/a&gt; of votes from 4 out of 6 segments before it is committed. By doing this, Aurora can survive the failure of an availability zone or any other two nodes without losing write availability.&lt;/p&gt;
&lt;p&gt;The database layer generates the fully ordered redo log records and delivers them to all six replicas of the destination segment. However, it only needs to wait for acknowledgement from 4 out of the 6 replicas before the log records are considered durable. Each replica can apply its redo records using its log applicator.&lt;/p&gt;
&lt;p&gt;The paper&#39;s authors ran an experiment to measure the network I/O based on these optimizations and compared it with the setup in Figure 1. The results are shown in the table below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/aurora-network-io.png&quot; alt=&quot;Network IOs for Aurora vs MySQL&quot;&gt; &lt;/p&gt;
&lt;p&gt;Aurora performed significantly better than the mirrored MySQL setup. From the paper:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Over the 30-minute period, Aurora was able to sustain 35 times more transactions than mirrored MySQL. The number of I/Os per transaction on the database node in Aurora was 7.7 times fewer than in mirrored MySQL despite amplifying writes six times with&lt;br&gt;
Aurora and not counting the chained replication within EBS nor the cross-AZ writes in MySQL. Each storage node sees unamplified writes, since it is only one of the six copies, resulting in 46 times fewer I/Os requiring processing at this tier. The savings we obtain by writing less data to the network allow us to aggressively replicate data for durability and availability and issue requests in parallel to minimize the impact of jitter.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;the-database-instance-is-replicated-too&quot;&gt;The database instance is replicated too &lt;a class=&quot;direct-link&quot; href=&quot;#the-database-instance-is-replicated-too&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In Aurora, the database tier can have up to 15 read replicas and one write replica. In addition to the storage nodes, the log stream generated by the writer is also sent to the read replicas. The writer does not wait for an acknowledgement from the read replicas before committing a write, it only needs a quorum from the storage nodes.&lt;/p&gt;
&lt;p&gt;Each read replica consumes the log stream and uses its log applicator to modify the pages in its cache based on the log records. By doing this, the replica can serve pages from its buffer cache and will only make a storage IO request if the requested page is not in its cache.&lt;/p&gt;
&lt;h3 id=&quot;aurora-does-not-need-quorum-reads&quot;&gt;Aurora does not need quorum reads &lt;a class=&quot;direct-link&quot; href=&quot;#aurora-does-not-need-quorum-reads&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In Aurora, the database layer directly feeds log records to the storage nodes and keeps track of the progress of each segment in its runtime state. Therefore, under normal circumstances, the database layer can issue a read request directly to the segment which has the most up-to-date data without needing to establish a read quorum.&lt;/p&gt;
&lt;p&gt;However, after a crash, the database layer needs to reestablish its runtime state through a read quorum of the segments for each protection group.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;According to this &lt;a href=&quot;https://www.allthingsdistributed.com/2019/03/Amazon-Aurora-design-cloud-native-relational-database.html&quot;&gt;post&lt;/a&gt;, Aurora was the fastest-growing service in AWS history as of March 2019. Its architecture has enabled it to provide performance and availability comparable to other commercial-grade databases at a cheaper cost. The paper goes into more detail about how the read and write operations work across the segments and the recovery procesws. As an aside, this has been my most difficult paper to read so far, and it&#39;s one where the &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/video/10.html&quot;&gt;lecture video&lt;/a&gt; (and rereading the paper multiple times!) really helped to clarify stuff.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/aurora.pdf&quot;&gt;Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases&lt;/a&gt; - The original paper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-aurora.txt&quot;&gt;Lecture 10: Database logging, quorums, Amazon Aurora&lt;/a&gt; - MIT 6.824 lecture notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.databass.dev/&quot;&gt;Database Internals&lt;/a&gt; by Alex Petrov.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.allthingsdistributed.com/2019/03/Amazon-Aurora-design-cloud-native-relational-database.html&quot;&gt;Amazon Aurora ascendant: How we designed a cloud-native relational database&lt;/a&gt; by Werner Vogels.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.acolyer.org/2019/03/25/amazon-aurora-design-considerations-for-high-throughput-cloud-native-relational-databases/&quot;&gt;Amazon Aurora: Design considerations for high throughput cloud-native relational databases&lt;/a&gt; from The Morning Paper.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 9 - CRAQ</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-9-craq/"/>
		<updated>2020-07-04T14:52:45-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-9-craq/</id>
		<content type="html">&lt;p&gt;Many distributed systems today &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-1/#the-cost-of-linearizability&quot;&gt;sacrifice stronger consistency guarantees&lt;/a&gt; for the sake of greater availability and higher throughput. &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/craq.pdf&quot;&gt;CRAQ&lt;/a&gt;, which stands for Chain Replication with Apportioned Queries, is a system designed to challenge this tradeoff. CRAQ&#39;s approach differs from existing replication techniques we have seen so far, like in &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-6-7-fault-tolerance-raft&quot;&gt;Raft&lt;/a&gt;. It improves on the original form of Chain Replication.&lt;/p&gt;
&lt;p&gt;CRAQ is a distributed &lt;a href=&quot;https://www.ibm.com/cloud/learn/object-storage&quot;&gt;object-storage&lt;/a&gt; system that maintains strong consistency while still providing a high read throughput. Object-storage systems are better suited for applications that need flat namespaces, such as a key-value store. These are unlike file-based systems, which store data in a hierarchical directory structure.&lt;/p&gt;
&lt;p&gt;This post will start by describing Chain Replication, before presenting how CRAQ improves on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#chain-replication&quot;&gt;Chain Replication&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#chain-replication-achieves-strong-consistency&quot;&gt;Chain replication achieves strong consistency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#write-operations-are-cheaper&quot;&gt;Write operations are cheaper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-tail-is-a-bottleneck&quot;&gt;The tail is a bottleneck&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#craq&quot;&gt;CRAQ&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#consistency-models-on-craq&quot;&gt;Consistency models on CRAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#craq-needs-a-configuration-manager&quot;&gt;CRAQ needs a configuration manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#one-slow-node-can-weaken-the-chain&quot;&gt;One slow node can weaken the chain&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;chain-replication&quot;&gt;Chain Replication &lt;a class=&quot;direct-link&quot; href=&quot;#chain-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Chain Replication is an approach to replicating data across multiple nodes that involves arranging the nodes in a &lt;em&gt;chain&lt;/em&gt; of a defined length &lt;em&gt;C&lt;/em&gt;. All the write operations from clients go to the &lt;em&gt;head&lt;/em&gt; of the chain, which passes them down to the next node in the chain. When a node receives a write operation, it applies the write and passes it down to the next node in the chain until the write reaches the &lt;em&gt;tail&lt;/em&gt; node.&lt;/p&gt;
&lt;p&gt;The tail node handles all &lt;em&gt;read&lt;/em&gt; operations. This is because a write will not reach the tail until all the other replicas in the chain have applied it. The write is marked as &lt;em&gt;committed&lt;/em&gt; when it reaches the tail. Therefore, a read will only return committed values.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/chain-replication.png&quot; alt=&quot;Figure 1: All reads in Chain Replication must be handled by the tail node, while all writes propagate down the chain from the head.&quot;&gt; &lt;/p&gt;
&lt;p&gt;Figure 1 illustrates a sample chain of length four. As shown by the dashed lines in the figure, the tail sends an acknowledgment back to the head when it commits a write.&lt;/p&gt;
&lt;h4 id=&quot;chain-replication-achieves-strong-consistency&quot;&gt;Chain replication achieves strong consistency &lt;a class=&quot;direct-link&quot; href=&quot;#chain-replication-achieves-strong-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Since all reads go to the tail and the tail stores all the committed write operations, the tail can apply a &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-1/#the-causal-order-is-not-a-total-order&quot;&gt;total ordering&lt;/a&gt; over all the operations. There is no possibility of a client seeing stale data. Concurrent reads to the tail will always return the most up-to-date value.&lt;/p&gt;
&lt;h4 id=&quot;write-operations-are-cheaper&quot;&gt;Write operations are cheaper &lt;a class=&quot;direct-link&quot; href=&quot;#write-operations-are-cheaper&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Another advantage of chain replication is that the cost of writes is spread equally over all nodes. Unlike in primary/backup replication where the primary node transmits data to all its backups, each node in chain replication transmits only to its successor in the chain. The simulation results by the paper&#39;s authors showed that chain replication achieved competitive or superior write throughput when compared with primary/backup replication.&lt;/p&gt;
&lt;h4 id=&quot;the-tail-is-a-bottleneck&quot;&gt;The tail is a bottleneck &lt;a class=&quot;direct-link&quot; href=&quot;#the-tail-is-a-bottleneck&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The major downside of chain replication is that since all the reads must go to the tail, read throughput cannot scale linearly with chain size. This is a trade-off that this approach makes to guarantee strong consistency. If clients can read from intermediate nodes, there is a possibility of concurrent reads for the same value to different nodes seeing different writes as they are being passed down the chain.&lt;/p&gt;
&lt;p&gt;CRAQ, which we will discuss next, helps to address this downside.&lt;/p&gt;
&lt;h3 id=&quot;craq&quot;&gt;CRAQ &lt;a class=&quot;direct-link&quot; href=&quot;#craq&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CRAQ (Chain Replication with Apportioned Queries) is a modification of chain replication that increases the read throughput by allowing any node in the chain to handle read requests, while still guaranteeing strong consistency. CRAQ works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Each node in the chain can store multiple versions of an object: one &lt;em&gt;clean&lt;/em&gt; version and a &lt;em&gt;dirty&lt;/em&gt; version per recent write.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For write operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Clients send writes to the head.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As the write passes through a replica, the replica creates a new dirty version for that object.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The tail creates a clean version for the object when it receives the write and sends an &lt;em&gt;acknowledgment&lt;/em&gt; back along the chain.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When a node receives an acknowledgment for an object version, it marks the latest object version as clean and deletes all previous versions for the object.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For reads from non-tail nodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the latest version that a node has for an object is clean, it replies with that version.&lt;/li&gt;
&lt;li&gt;Otherwise, it will ask the tail for the last committed version number for that object and returns that version of the object (also known as a &lt;em&gt;version query)&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If a node returns the most recent clean version of an object without asking the tail first, it may violate strong consistency, as the tail may have exposed a newer clean version to another reader.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/craq-reads.png&quot; alt=&quot;Figure 2: Reads to clean objects in CRAQ can be completely handled by any node in the system.&quot;&gt; &lt;/p&gt;
&lt;p&gt;In Figure 2, we see a CRAQ chain in the starting clean state. All the nodes will return the same value for any read request since they store an identical copy of the object. The nodes will remain in a clean state until they receive a write operation.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/craq-dirty-reads.png&quot; alt=&quot;Figure 3: Reads to dirty objects in CRAQ can be received by any node, but require small version requests (dotted blue line) to the chain tail to properly serialize operations.&quot;&gt; &lt;/p&gt;
&lt;p&gt;Figure 3 illustrates a dirty read situation where the successor node to the head makes a version query to the tail for its latest version number. The write request received at the head is still in propagation when the dirty read for key &lt;em&gt;K&lt;/em&gt; comes in, which is why the node has multiple versions of the object.&lt;br&gt;
The node then makes a version query to the tail node which returns V1 since that it&#39;s latest committed value. As a result, the dirty node returns the object value associated with the version number it gets from the tail. If a clean replica had received the read request, it would have returned its value immediately with no version query.&lt;/p&gt;
&lt;p&gt;CRAQ offers throughput improvements over the standard chain replication in two different scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Read-Mostly Workloads&lt;/strong&gt;: Here, most of the reads to the &lt;em&gt;C&lt;/em&gt;-1 non-tail nodes will be clean reads, and so the throughput can scale linearly with the chain size &lt;em&gt;C.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write-Heavy Workloads:&lt;/strong&gt; These have most read requests to non-tail nodes as dirty, and require version queries to the tail. However, these version queries are more light-weight than reading full objects from the tail, which allows the tail to process them at a higher rate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The performance evaluation of CRAQ described in the paper showed that its read throughput is higher than in chain replication under these workloads.&lt;/p&gt;
&lt;h4 id=&quot;consistency-models-on-craq&quot;&gt;Consistency models on CRAQ &lt;a class=&quot;direct-link&quot; href=&quot;#consistency-models-on-craq&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CRAQ supports the varying needs of applications by providing three consistency models:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Strong Consistency:&lt;/strong&gt; This works as described above. The guarantee is that all object reads will return the last committed write.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eventual Consistency&lt;/strong&gt;: For applications that may not need always need the latest version of an object, this allows an intermediate node to return the newest object version it knows about without contacting the tail. This means that a subsequent read to a different node for the same object may return an object version that is older than the previous one returned.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eventual Consistency with Maximum-Bounded Inconsistency:&lt;/strong&gt; This allows read requests to a node to return the newest object version it is aware of, but only to a certain point. We can set a limit based on either the time (relative to the local clock of a node) or an absolute version number. The advantage over the standard eventual consistency is that it guarantees that the value of a read operation has a maximum inconsistency period.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;craq-needs-a-configuration-manager&quot;&gt;CRAQ needs a configuration manager &lt;a class=&quot;direct-link&quot; href=&quot;#craq-needs-a-configuration-manager&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Unlike Raft, the CRAQ protocol cannot prevent a split-brain by itself. It is only concerned with data replication and does not handle things like leader (or head) election in the event of partitions. To address this, CRAQ is usually coupled with a configuration manager like &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-8-zookeeper/&quot;&gt;ZooKeeper&lt;/a&gt; to deal with managing the nodes that make up a chain and handling leader election for the chain.&lt;/p&gt;
&lt;p&gt;To recover from failure, each node in a chain keeps track of its predecessor and successor, as well as the chain head and tail. When a head fails, its immediate successor becomes the new head. Similarly, the tail&#39;s immediate predecessor takes over when the tail fails. Intermediate nodes can also be replaced by adding a new node between two nodes like in a doubly-linked list.&lt;/p&gt;
&lt;p&gt;The configuration manager manages the nodes that make up a chain and choosing the chain&#39;s head and tail.&lt;/p&gt;
&lt;h4 id=&quot;one-slow-node-can-weaken-the-chain&quot;&gt;One slow node can weaken the chain &lt;a class=&quot;direct-link&quot; href=&quot;#one-slow-node-can-weaken-the-chain&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The major downside to CRAQ (and the standard chain replication) is that it requires all the nodes in the chain to take part before any write can commit. This is unlike quorum systems like &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-6-7-fault-tolerance-raft/#how-do-we-choose-the-right-election-timeout&quot;&gt;Raft&lt;/a&gt; and &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-8-zookeeper/&quot;&gt;ZooKeeper&lt;/a&gt; that only need a majority of the nodes to participate.&lt;/p&gt;
&lt;p&gt;The upshot of this is that CRAQ by itself is less fault-tolerant than Raft and ZooKeeper as the system&#39;s throughput can be severely affected by at least one slow node.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CRAQ is a straightforward approach to replication with minimal chit-chat compared to a system like Raft, with its downside being that it&#39;s not very fault-tolerant as it needs all the nodes in a chain to respond to writes. It will be interesting to explore a quorum-based approach to chain replication.&lt;/p&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/craq.pdf&quot;&gt;Object Storage on CRAQ&lt;/a&gt; - Original paper by Jeff Terrace and Michael J. Freedman.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-craq.txt&quot;&gt;Lecture 9: Chain Replication, CRAQ&lt;/a&gt; - MIT 6.824 lecture notes.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 8 - ZooKeeper</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-8-zookeeper/"/>
		<updated>2020-06-24T20:29:01-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-8-zookeeper/</id>
		<content type="html">&lt;p&gt;This week&#39;s lecture was on &lt;a href=&quot;https://zookeeper.apache.org/&quot;&gt;ZooKeeper&lt;/a&gt;, with the original &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/zookeeper.pdf&quot;&gt;paper&lt;/a&gt; being used as a case study. The paper sheds light on the following questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Can the coordination of distributed systems be handled by a stand-alone general-purpose service? If so, what should the API of that service look like?&lt;/li&gt;
&lt;li&gt;Can we improve the performance of a system by N times if we add N times replica servers? That is, can the performance scale linearly by adding more servers?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This post will start by addressing the second question and discussing how ZooKeeper&#39;s performance can scale linearly through the guarantees it provides. In a later section, we&#39;ll discuss how ZooKeeper&#39;s API allows it to act as a stand-alone coordination service for distributed systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#zookeeper&quot;&gt;ZooKeeper&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#overview&quot;&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#zookeeper-can-scale-linearly&quot;&gt;ZooKeeper can scale linearly&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#writes-to-zookeeper-are-linearizable&quot;&gt;Writes to ZooKeeper are linearizable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#client-requests-are-fifo&quot;&gt;Client Requests are FIFO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#zookeeper-as-a-service&quot;&gt;ZooKeeper as a service&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#data-model&quot;&gt;Data Model&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#znodes-map-to-client-abstractions&quot;&gt;Znodes map to client abstractions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#znodes-can-also-store-specific-metadata&quot;&gt;Znodes can also store specific metadata&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#client-api&quot;&gt;Client API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementing-primitives-using-zookeeper&quot;&gt;Implementing primitives using ZooKeeper&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#using-zookeeper-for-configuration-management&quot;&gt;Using ZooKeeper for Configuration Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#using-zookeeper-for-rendezvous&quot;&gt;Using ZooKeeper for Rendezvous&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#using-zookeeper-for-group-membership&quot;&gt;Using ZooKeeper for Group Membership&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;zookeeper&quot;&gt;ZooKeeper &lt;a class=&quot;direct-link&quot; href=&quot;#zookeeper&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;direct-link&quot; href=&quot;#overview&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ZooKeeper is a service for coordinating the processes of distributed applications. Coordination can be in the form of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Group membership&lt;/strong&gt;: Ensuring that members of a group know about all the other members of that group. The set of replicas involved in a &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-6-7-fault-tolerance-raft/#how-does-log-replication-work&quot;&gt;Raft&lt;/a&gt; log entry replication form a group, for example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configuration&lt;/strong&gt;: Keeping track of the operational parameters needed by the nodes in a cluster. Configuration items could include database server URLs for different environments, security settings, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leader Election:&lt;/strong&gt; Like Raft, ZooKeeper can also be used to coordinate the election of a leader among a set of nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;zookeeper-can-scale-linearly&quot;&gt;ZooKeeper can scale linearly &lt;a class=&quot;direct-link&quot; href=&quot;#zookeeper-can-scale-linearly&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s consider the distributed key-value store shown below which makes use of a Raft module in each replica. As discussed in the previous lecture, all the writes in Raft must go through a leader. To guarantee a &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-1/#linearizability&quot;&gt;linearizable&lt;/a&gt; history, all reads must go through the leader as well. One reason why reads cannot be sent to followers is that a replica may not be in the majority needed by Raft, and so may return stale value which violates linearizability.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/resized-raft-application.png&quot; alt=&quot;Figure 1: Raft Application&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 1 - Sample Key-Value store using Raft for replication.&lt;/p&gt;
&lt;p&gt;Going back to the second question asked at the beginning of this post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If we added 2x more replicas to this setup, is there a chance that we could get 2x better performance of reads and writes?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The simple answer is: &lt;em&gt;it depends.&lt;/em&gt; In a Raft based system like in Figure 1, adding more servers will likely degrade the performance of the system. This is because all the reads must still go through the leader, and the leader now has to store more information about the new servers.&lt;/p&gt;
&lt;p&gt;ZooKeeper, on the other hand, allows us to scale the performance of our system linearly by adding more servers. It does this by relaxing the definition of correctness and providing weaker guarantees for clients. Reads can be served from any replica but writes are still sent to a leader. While this has the downside that reads may return stale data, it greatly improves the performance of reads in the system. ZooKeeper is a system designed for read-heavy workloads, and so the trade-off that leads to better read performance is worth it.&lt;/p&gt;
&lt;p&gt;Next, we&#39;ll discuss the two basic ordering guarantees provided by ZooKeeper that make it suitable as a distributed coordination system.&lt;/p&gt;
&lt;h3 id=&quot;writes-to-zookeeper-are-linearizable&quot;&gt;Writes to ZooKeeper are linearizable &lt;a class=&quot;direct-link&quot; href=&quot;#writes-to-zookeeper-are-linearizable&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ZooKeeper guarantees linearizable writes, stated in the paper as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All requests that update the state of ZooKeeper are serializable and respect precedence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means that if Client A writes a value for key X, any subsequent updates to that key by another client will first see the write by Client A. The leader chooses an order for the writes, and that order is maintained on all the followers.&lt;/p&gt;
&lt;p&gt;Note that the distinction between this and linearizable reads is that the lack of linearizable reads here means that clients can read stale values for a key. For example, if a value for key X is updated by client A on one server, a read on another server for that key by client B may return the old value. This is because the freshness guarantee for ZooKeeper only applies to writes.&lt;/p&gt;
&lt;h3 id=&quot;client-requests-are-fifo&quot;&gt;Client Requests are FIFO &lt;a class=&quot;direct-link&quot; href=&quot;#client-requests-are-fifo&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A client is any user of the ZooKeeper service. The guarantee for clients is stated as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All requests from a given client are executed in the order that they were sent by the client.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In other words, each client can specify an order for its operations (reads and writes), and that order will be maintained by ZooKeeper when executing.&lt;/p&gt;
&lt;p&gt;These guarantees combined can be used to implement many useful distributed system primitives despite the weaker consistency guarantee. &lt;em&gt;Note&lt;/em&gt; that ZooKeeper does provide optional support for linearizable reads which comes with a performance cost.&lt;/p&gt;
&lt;h2 id=&quot;zookeeper-as-a-service&quot;&gt;ZooKeeper as a service &lt;a class=&quot;direct-link&quot; href=&quot;#zookeeper-as-a-service&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ZooKeeper is a good example of how the coordination of distributed systems can be handled by a stand-alone service. It does this by exposing an API that application developers can use to implement specific primitives. Some examples of this are shown in a &lt;a href=&quot;#implementing-primitives-using-zookeeper&quot;&gt;later section&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The ZooKeeper service is made up of a cluster of nodes that use replication for better fault tolerance and performance. Clients communicate with ZooKeeper through a client API contained in the client library. The client library also handles the network connections between ZooKeeper servers and the client. Some systems covered in the &lt;a href=&quot;https://timilearning.com/tags/mit-6.824/&quot;&gt;previous lectures&lt;/a&gt; where ZooKeeper could be used are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-4-primary-backup-replication/#open-question-i-have&quot;&gt;VMware FT&#39;s Test-and-Set server&lt;/a&gt;: The system uses a test-and-set server to prevent split-brain by ensuring that the operation can succeed for only one of the replicas in the event of a network partition. This test-and-set server needs to be fault-tolerant. ZooKeeper is a fault tolerant service that allows to implement primitives like test-and-set operations.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-3-gfs&quot;&gt;GFS&lt;/a&gt;: GFS (pre-Colossus) made use of a single master to keep track of the metadata related to the chunks in the system. ZooKeeper could have played this role in the system and maybe even improved performance since all the replicas of the master would have been able to serve reads.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-1-mapreduce/&quot;&gt;MapReduce&lt;/a&gt;, ZooKeeper can be used to keep track of information like who the current master is, the list of workers, what jobs are assigned to what workers, the status of tasks, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ZooKeeper uses a leader-based atomic broadcast protocol called &lt;a href=&quot;https://cwiki.apache.org/confluence/display/ZOOKEEPER/Zab+vs.+Paxos&quot;&gt;Zab&lt;/a&gt; to guarantee that writes are linearizable in the system. You can think of Zab as a consensus protocol similar to Raft or Paxos.&lt;/p&gt;
&lt;h3 id=&quot;data-model&quot;&gt;Data Model &lt;a class=&quot;direct-link&quot; href=&quot;#data-model&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ZooKeeper provides a client API to manipulate a set of &lt;a href=&quot;https://www.baeldung.com/lock-free-programming#3-wait-free&quot;&gt;wait-free&lt;/a&gt; data objects known as &lt;em&gt;znodes&lt;/em&gt;. Znodes are organized in a hierarchical form similar to file systems, and we can refer to a given znode using the standard UNIX notation for file systems. For example, we can use &lt;em&gt;/A/B/C&lt;/em&gt; to refer to znode C which has znode B as its parent, where B has znode A as its parent.&lt;/p&gt;
&lt;p&gt;A client can create two types of znodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Regular:&lt;/strong&gt; Regular znodes are created and deleted explicitly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ephemeral&lt;/strong&gt;: Ephemeral znodes can either be deleted explicitly or are automatically removed by the system when the session that created them is terminated.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, a client can set a &lt;em&gt;sequential&lt;/em&gt; flag when creating a new znode. When this flag is set, the znode&#39;s name is appended with the value of a monotonically increasing counter. For example, If &lt;em&gt;z&lt;/em&gt; is the new znode and &lt;em&gt;p&lt;/em&gt; is the parent znode&#39;s name, then the sequence value of &lt;em&gt;z&lt;/em&gt; will be greater than that of any other sequential child znode of &lt;em&gt;p&lt;/em&gt; created before &lt;em&gt;z.&lt;/em&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/zookeeper-hierarchy.png&quot; alt=&quot;Figure 2: Illustration of ZooKeeper hierarchical name space&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 2: Illustration of ZooKeeper&#39;s hierarchical name space.&lt;/p&gt;
&lt;h4 id=&quot;znodes-map-to-client-abstractions&quot;&gt;Znodes map to client abstractions &lt;a class=&quot;direct-link&quot; href=&quot;#znodes-map-to-client-abstractions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Znodes of either type can store data but only regular znodes can have children. Znodes are not designed for general data storage but are instead to use represent abstractions in a client&#39;s application. For example, the figure above has two subtrees for Application 1 and Application 2. Application 1 also has a subtree that implements a group membership protocol. The client processes &lt;em&gt;p&lt;sub&gt;1&lt;/sub&gt;-p&lt;sub&gt;n&lt;/sub&gt;&lt;/em&gt; each create a znode &lt;em&gt;p&lt;sub&gt;i&lt;/sub&gt;&lt;/em&gt; under &lt;em&gt;/app1.&lt;/em&gt; In this example, the znodes are used to represent the processes for an application, and a process can be aware of its group members by reading from the &lt;em&gt;/app1&lt;/em&gt; subtree.&lt;/p&gt;
&lt;h4 id=&quot;znodes-can-also-store-specific-metadata&quot;&gt;Znodes can also store specific metadata &lt;a class=&quot;direct-link&quot; href=&quot;#znodes-can-also-store-specific-metadata&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Note that although znodes are not designed for general data storage, they allow clients to store specific metadata or configuration. For example, it is useful for a new server in a leader-based system to learn about which other server is the current leader. To achieve this, the current leader can be configured to write this information in a known znode space. Any new servers can then read from that znode space.&lt;/p&gt;
&lt;p&gt;There is also some metadata associated with znodes by default like timestamps and version counters, with which clients can execute conditional updates based on the version of the znode. This will be explained further in the next section.&lt;/p&gt;
&lt;h3 id=&quot;client-api&quot;&gt;Client API &lt;a class=&quot;direct-link&quot; href=&quot;#client-api&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The ZooKeeper client API exposes a number of methods. Here are a few of them:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;create(path, data, flags)&lt;/strong&gt;: Creates a znode with pathname &lt;em&gt;path&lt;/em&gt;, stores &lt;em&gt;data[]&lt;/em&gt; in it, and&lt;br&gt;
returns the name of the new znode. &lt;em&gt;flags&lt;/em&gt; enables a client to select the type of znode: regular, ephemeral, and set the sequential flag;&lt;br&gt;
&lt;strong&gt;delete(path, version)&lt;/strong&gt;: Deletes the znode path if that znode is at the expected version;&lt;br&gt;
&lt;strong&gt;exists(path, watch)&lt;/strong&gt;: Returns true if the znode with path name path exists, and returns false otherwise. The &lt;em&gt;watch&lt;/em&gt; flag enables a client to set a watch on the znode;&lt;br&gt;
&lt;strong&gt;getData(path, watch)&lt;/strong&gt;: Returns the data and meta-data, such as version information, associated with the znode. The &lt;em&gt;watch&lt;/em&gt; flag works in the same way as it does for exists(), except that ZooKeeper does not set the watch if the znode does not exist;&lt;br&gt;
&lt;strong&gt;setData(path, data, version)&lt;/strong&gt;: Writes &lt;em&gt;data[]&lt;/em&gt; to znode path if the version number is&lt;br&gt;
the current version of the znode;&lt;br&gt;
&lt;strong&gt;getChildren(path, watch):&lt;/strong&gt; Returns the set of names of the children of a znode;&lt;br&gt;
&lt;strong&gt;sync(path)&lt;/strong&gt;: Waits for all updates pending at the start of the operation to propagate to the server that the client is connected to.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note the following about the client API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When clients connect to ZooKeeper, they establish a &lt;em&gt;session.&lt;/em&gt; It is through this session that ZooKeeper can identify clients in fulfilling the FIFO order guarantee.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;sync&lt;/em&gt; method can be used to ensure that the read for a znode is linearizable, though it comes at a performance cost. It forces a server to apply all its pending write requests before processing a read.&lt;/li&gt;
&lt;li&gt;All the methods have both a synchronous and asynchronous version available through the API.&lt;/li&gt;
&lt;li&gt;The update methods(&lt;em&gt;delete&lt;/em&gt; and &lt;em&gt;setData&lt;/em&gt;) take an expected version number. If this differs from the actual version number of the znode, the operation will fail.&lt;/li&gt;
&lt;li&gt;When the &lt;em&gt;watch&lt;/em&gt; parameter in the read methods (&lt;em&gt;getData&lt;/em&gt; and &lt;em&gt;getChildren&lt;/em&gt;) is set, the operation will complete as normal except that the server promises that it will notify the client when the returned information changes. This is another optimization made in ZooKeeper to prevent a client from continuously having to poll for the latest information.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;implementing-primitives-using-zookeeper&quot;&gt;Implementing primitives using ZooKeeper &lt;a class=&quot;direct-link&quot; href=&quot;#implementing-primitives-using-zookeeper&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;using-zookeeper-for-configuration-management&quot;&gt;Using ZooKeeper for Configuration Management &lt;a class=&quot;direct-link&quot; href=&quot;#using-zookeeper-for-configuration-management&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To implement dynamic configuration management with ZooKeeper, we can store configuration in a znode, &lt;em&gt;z&lt;sub&gt;c&lt;/sub&gt;&lt;/em&gt;. When a process is started, it starts up with the full pathname of &lt;em&gt;z&lt;sub&gt;c&lt;/sub&gt;&lt;/em&gt;. The process can get its required configuration by reading &lt;em&gt;z&lt;sub&gt;c&lt;/sub&gt;&lt;/em&gt; and setting the watch flag to true. It will get notified whenever the configuration changes, and can then read the new configuration with the watch flag set to true again.&lt;/p&gt;
&lt;h3 id=&quot;using-zookeeper-for-rendezvous&quot;&gt;Using ZooKeeper for Rendezvous &lt;a class=&quot;direct-link&quot; href=&quot;#using-zookeeper-for-rendezvous&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s consider a scenario where a client wants to start a master process and many worker processes, with the job of starting the processes being handled by a scheduler. The worker processes will need information about the address and port of the master to connect to it. However, because a scheduler starts the processes, the client will not know the master&#39;s port and address ahead of time for it to give to the workers.&lt;/p&gt;
&lt;p&gt;This can be handled by the client creating a &lt;a href=&quot;https://cs.stackexchange.com/a/105332&quot;&gt;rendezvous&lt;/a&gt; znode, &lt;em&gt;z&lt;sub&gt;r&lt;/sub&gt;&lt;/em&gt;, and passing the full pathname of &lt;em&gt;z&lt;sub&gt;r&lt;/sub&gt;&lt;/em&gt; as a startup parameter to both the master and worker processes. When the master starts up, it can fill in &lt;em&gt;z&lt;sub&gt;r&lt;/sub&gt;&lt;/em&gt; with information about its address and port. The workers can read from the znode when they start up and set the watch flag set to true. This way, workers will get notified when &lt;em&gt;z&lt;sub&gt;r&lt;/sub&gt;&lt;/em&gt; is updated and can use the information there.&lt;/p&gt;
&lt;h3 id=&quot;using-zookeeper-for-group-membership&quot;&gt;Using ZooKeeper for Group Membership &lt;a class=&quot;direct-link&quot; href=&quot;#using-zookeeper-for-group-membership&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As described in an &lt;a href=&quot;#data-model&quot;&gt;earlier section&lt;/a&gt;, we can use ZooKeeper to implement group membership by creating a znode &lt;em&gt;z&lt;sub&gt;g&lt;/sub&gt;&lt;/em&gt; to represent the group. Any process member of the group can create an ephemeral child znode with a unique name under &lt;em&gt;z&lt;sub&gt;g&lt;/sub&gt;&lt;/em&gt;. The znode representing a process will be automatically removed when the process fails or ends.&lt;/p&gt;
&lt;p&gt;A process can obtain other information about the other members of its group by listing the children under &lt;em&gt;z&lt;sub&gt;g&lt;/sub&gt;&lt;/em&gt;. It can then monitor changes in group membership by setting the watch flag to true.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The design of ZooKeeper is another great example of tailoring a system for a specific use case; in this example, strong consistency was relaxed to improve the performance of reads in read-mostly workloads. The results in the paper show that the throughput of ZooKeeper can scale linearly. ZooKeeper is also used in many distributed systems today, and you can find a list of some of those &lt;a href=&quot;https://zookeeper.apache.org/doc/r3.6.1/recipes.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are a few more details about the implementation of ZooKeeper that were skipped in this post which you can find in the paper about things like how replication works, the atomic broadcast protocol, request handling from clients, etc. The section below contains links that can help if you&#39;re interested in learning more about these details.&lt;/p&gt;
&lt;h1 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/zookeeper.pdf&quot;&gt;ZooKeeper: Wait-free coordination for Internet-scale systems&lt;/a&gt; - Original paper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zookeeper.apache.org/doc/r3.6.1/recipes.html&quot;&gt;ZooKeeper recipes&lt;/a&gt; - Further examples of how ZooKeeper can be used.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sysgears.com/articles/managing-configuration-of-distributed-system-with-apache-zookeeper/&quot;&gt;Managing configuration of a distributed system with Apache ZooKeeper&lt;/a&gt; by Oleg Yermolaiev.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-500%3A+Replace+ZooKeeper+with+a+Self-Managed+Metadata+Quorum&quot;&gt;KIP-500: Replace ZooKeeper with a Self-Managed Metadata Quorum&lt;/a&gt; - Interesting discussion on the plan to replace ZooKeeper with Raft in Apache Kafka.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jepsen.io/consistency/models/linearizable&quot;&gt;Linearizability&lt;/a&gt; - Jepsen post on Linearizability.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lectures 6 &amp; 7 - Fault Tolerance(Raft)</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-6-7-fault-tolerance-raft/"/>
		<updated>2020-05-30T15:54:54-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-6-7-fault-tolerance-raft/</id>
		<content type="html">&lt;p&gt;One common pattern in the previous systems we have discussed like &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-1-mapreduce/&quot;&gt;MapReduce&lt;/a&gt;, &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-3-gfs/&quot;&gt;GFS&lt;/a&gt;, and &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-4-primary-backup-replication/&quot;&gt;VMware FT&lt;/a&gt; is that they all rely on a single entity to make the key decisions. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MapReduce has a single master node responsible for organizing the computation among the workers.&lt;/li&gt;
&lt;li&gt;GFS has a master responsible for picking the primary replica for a chunkserver.&lt;/li&gt;
&lt;li&gt;VMware FT uses an atomic test-and-set operation on a single shared disk to choose a new leader and prevent split-brain.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While this makes it easier for the system to decide, the downside of this approach is that the entity is now a single point of failure. If the entity is down, the system may not be able to make any progress without manual intervention.&lt;/p&gt;
&lt;p&gt;Ideally, we would want a more fault-tolerant system that can withstand the loss of at least one node, even if that node is the one making critical decisions at the time. Such a system would need a mechanism for the other nodes in the cluster to automatically &lt;em&gt;agree&lt;/em&gt; on which one of them should take over as the next leader.&lt;/p&gt;
&lt;p&gt;It turns out that getting all the nodes in a cluster to agree on a decision is a hard problem in distributed systems. The main difficulty lies in the fact that it is impossible to distinguish between when a node has crashed, and when there is a network fault. These two problems have the same symptom: no response will be received from the node. If a node is wrongly declared dead, it may still be able to receive and execute requests from clients, which may lead to inconsistencies between the nodes. The other nodes may need to decide on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What node should take over as the leader/master in the cluster when there&#39;s a failure; if two or more nodes think that they are the master, we could end up with a split-brain situation.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;What client requests to execute; if two nodes decide differently, a client could see inconsistent results.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This problem of getting multiple nodes to agree has led to the development of &lt;em&gt;consensus&lt;/em&gt; algorithms. Distributed consensus is the ability for components in a distributed system to reach agreement even in the presence of failures and an unreliable network. &lt;a href=&quot;http://paxos.systems/index.html&quot;&gt;Paxos&lt;/a&gt; and &lt;a href=&quot;https://raft.github.io/&quot;&gt;Raft&lt;/a&gt; are two of the most popular consensus algorithms today. This post will focus on Raft and you can find its original paper &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/raft-extended.pdf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#raft-paper-summary&quot;&gt;Raft Paper Summary&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#replicated-state-machines&quot;&gt;Replicated State Machines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#raft-overview&quot;&gt;Raft Overview&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#why-use-logs%3F&quot;&gt;Why Use Logs?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#server-states&quot;&gt;Server States&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#raft-uses-terms&quot;&gt;Raft Uses Terms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how-is-a-leader-elected%3F&quot;&gt;How is a leader elected?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#election-timeouts-are-randomized&quot;&gt;Election Timeouts Are Randomized&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#any-two-majorities-must-overlap-in-at-least-one-server&quot;&gt;Any Two Majorities Must Overlap in At Least One Server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how-does-log-replication-work%3F&quot;&gt;How does log replication work?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#what-happens-if-the-leader-crashes%3F&quot;&gt;What happens if the leader crashes?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#why-is-it-safe-for-a-new-leader-to-overwrite-its-follower%27s-logs%3F&quot;&gt;Why is it safe for a new leader to overwrite its follower&#39;s logs?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#what-happens-when-a-follower-or-candidate-crashes%3F&quot;&gt;What happens when a follower or candidate crashes?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how-do-we-choose-the-right-election-timeout%3F&quot;&gt;How do we choose the right election timeout?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how-does-raft-tame-its-logs%3F&quot;&gt;How does Raft tame its logs?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;raft-paper-summary&quot;&gt;Raft Paper Summary &lt;a class=&quot;direct-link&quot; href=&quot;#raft-paper-summary&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before Raft, Paxos had almost solely dominated the landscape of consensus algorithms. The problem with Paxos, however, is that it is difficult to understand. This difficulty motivated the creation of Raft. The authors wanted to develop a consensus algorithm that was not only practical, but also understandable. Their primary goal in the creation of the algorithm was &lt;em&gt;understandability.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One of the approaches the authors took to create an understandable algorithm was to decompose the consensus problem into separate parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;leader election&lt;/li&gt;
&lt;li&gt;log replication, and&lt;/li&gt;
&lt;li&gt;safety.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These parts will be discussed separately below.&lt;/p&gt;
&lt;h3 id=&quot;replicated-state-machines&quot;&gt;Replicated State Machines &lt;a class=&quot;direct-link&quot; href=&quot;#replicated-state-machines&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The replicated state machine approach to replication has been discussed in an &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-4-primary-backup-replication/#replication-approaches&quot;&gt;earlier post&lt;/a&gt;, but to recap, the idea is that if two state machines are fed the same input operations in the same order, their outputs will be the same.&lt;/p&gt;
&lt;p&gt;A replicated log is typically used to implement replicated state machines. Each server maintains its log, which contains a series of commands that its state machine must execute in order. These logs must be kept consistent across all the servers. Consistency here means that all the logs must have the same commands in the same order.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/replicated-state-machine.png&quot; alt=&quot;Figure 1: Replicated state machine architecture. &quot;&gt; &lt;/p&gt;
&lt;p&gt;The job of a consensus algorithm is to keep this replicated log consistent. Each server has a consensus module for managing its log. The consensus module on a server is responsible for adding client commands to the log and communicating with the consensus modules on the other servers to ensure that their logs &lt;em&gt;eventually&lt;/em&gt; contain the same commands in the same order.&lt;/p&gt;
&lt;p&gt;Practical consensus algorithms must not violate the following properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Safety:&lt;/em&gt; That is, they must return a correct result under all &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#byzantine-faults&quot;&gt;non-Byzantine&lt;/a&gt; conditions. These conditions include packet losses, network delays, partitions, etc. Any value decided on by a server must have been proposed by another server, it is not enough for the server to just always return &#39;null&#39;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Availability:&lt;/em&gt; Consensus algorithms must be fully functional, provided that the majority of the servers are operational and can communicate with clients and each other. A cluster of seven servers can tolerate the failure of any three servers. A minority of slow or failed servers should not impact the overall performance of the system.&lt;/li&gt;
&lt;li&gt;They do not depend on &lt;em&gt;timing&lt;/em&gt; to ensure that logs are consistent. &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#unreliable-clocks&quot;&gt;Clocks are unreliable&lt;/a&gt;, and consensus algorithms must not rely on them to determine the right order of events.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;raft-overview&quot;&gt;Raft Overview &lt;a class=&quot;direct-link&quot; href=&quot;#raft-overview&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Raft works by electing a single leader among the servers, which manages log replication. The leader accepts client requests and decides where log entries should be placed, without having to consult other servers. When the current leader fails, Raft includes a protocol for electing a new leader to take over.&lt;/p&gt;
&lt;p&gt;As stated earlier, Raft breaks down consensus into three independent subproblems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Leader election&lt;/strong&gt;: A new leader must be chosen when the old one fails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log Replication:&lt;/strong&gt; The leader accepts log entries from clients and is responsible for replicating them to the other servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Safety:&lt;/strong&gt; If a log entry has been applied at an index on one server, it must be applied at the same index on all the other servers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/resized-raft-application.png&quot; alt=&quot;Figure 2: Raft Application&quot;&gt; &lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Figure 2: An example of an application where Raft could be used is a key-value database, as shown above.  Client requests are converted to log entries on a leader, which are then replicated on the other servers by the Raft module. The state machine converts those log entries into records in the key-value store. &lt;/p&gt;
&lt;h4 id=&quot;why-use-logs%3F&quot;&gt;Why Use Logs? &lt;a class=&quot;direct-link&quot; href=&quot;#why-use-logs%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A log is an append-only sequence of records used as a storage abstraction in many distributed systems today. Some of the benefits of using a log include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Ordering&lt;/em&gt;: The log assigns an order to all its records. This helps the replicas agree on a single execution order of commands.&lt;/li&gt;
&lt;li&gt;A log stores tentative commands until they are committed.&lt;/li&gt;
&lt;li&gt;A log also keeps a persistent state of all the commands that have been executed by the state machine. By doing this, the current state of the application can be recreated at any time by replaying the log commands.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;server-states&quot;&gt;Server States &lt;a class=&quot;direct-link&quot; href=&quot;#server-states&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A server is always in one of three states: &lt;em&gt;leader, follower,&lt;/em&gt; or &lt;em&gt;candidate.&lt;/em&gt; A leader receives requests from clients and communicates them with the other servers. A follower is passive; it only receives log entries from the leader and votes in elections. Any requests from a client to a follower will be redirected to the leader. The candidate state is used for leader elections.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/raft-states.png&quot; alt=&quot;Figure 3: Raft server states&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 3 - &quot;Server states. Followers only respond to requests from other servers. If a follower receives no communication, it becomes a candidate and initiates an election. A candidate that receives votes from a majority of the full cluster becomes the new leader. Leaders typically operate until they fail.&quot;&lt;/p&gt;
&lt;h4 id=&quot;raft-uses-terms&quot;&gt;Raft Uses Terms &lt;a class=&quot;direct-link&quot; href=&quot;#raft-uses-terms&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Time in Raft is divided into &lt;em&gt;terms&lt;/em&gt; which can be of arbitrary length. Each term begins with an election and has at most one leader. Terms act as a form of a logical clock in a system. Some other key things to note about terms are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Terms are labelled with consecutive integers.&lt;/li&gt;
&lt;li&gt;Each server stores its current term number and whenever servers communicate, they exchange term numbers.&lt;/li&gt;
&lt;li&gt;If a server&#39;s current term is smaller than the other servers&#39; own, it updates its term to the larger value.&lt;/li&gt;
&lt;li&gt;If a &lt;em&gt;candidate&lt;/em&gt; or &lt;em&gt;leader&lt;/em&gt; detects that its term is smaller than another server&#39;s own, it reverts to &lt;em&gt;follower&lt;/em&gt; state.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The servers in Raft communicate through &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-2-rpc-and-threads/#remote-procedure-call-rpc&quot;&gt;RPCs&lt;/a&gt;. There are two main RPC methods involved here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;AppendEntries RPC&lt;/strong&gt;: This method is invoked by a leader to replicate its log entries to the other servers. It includes the leader&#39;s term, the new log entries, and identifiers for where to place the entries, among other things.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RequestVote RPC&lt;/strong&gt;: This is invoked by candidates to gain votes from the other servers. It includes the candidate&#39;s term, identifier, and some other items which will be discussed later.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;how-is-a-leader-elected%3F&quot;&gt;How is a leader elected? &lt;a class=&quot;direct-link&quot; href=&quot;#how-is-a-leader-elected%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A leader sends periodic &lt;em&gt;heartbeat&lt;/em&gt; messages to its followers to maintain its authority. These heartbeat messages are in the form of AppendEntries RPCs which contain no log entry. If a follower has not received any communication from the leader within a specified &lt;em&gt;election timeout,&lt;/em&gt; it will transition to candidate state. The follower does this because it assumes there is no leader in the cluster at present, and so it begins an election to choose a new one.&lt;/p&gt;
&lt;p&gt;The first step that a follower takes after becoming a candidate is to increase its term number. After doing that, it votes for itself and sends an RPC (including its term number) to all the other servers in parallel to request votes from them. A candidate will remain in its state until any of the following conditions is met:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It wins the election.&lt;/li&gt;
&lt;li&gt;Another server establishes itself as the leader.&lt;/li&gt;
&lt;li&gt;There&#39;s a period with no winner of the election.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For a candidate to win an election, it must have received votes from the &lt;em&gt;majority&lt;/em&gt; of the servers in the cluster. Note that the majority is out of all the servers in the cluster, not just the live ones. A server can vote for at most one candidate in a given term, and the decision is made on a first-come-first-served basis. The winning candidate in a term then becomes a leader and immediately sends out &lt;em&gt;heartbeat&lt;/em&gt; messages to the other servers to establish its authority. Note that there is another restriction on how servers can vote which will be discussed later.&lt;/p&gt;
&lt;p&gt;It is also possible that while waiting for votes, a candidate receives an AppendEntries RPC from another server that claims to be the leader. If the supposed leader&#39;s term included in the RPC is greater than or equal to the candidate&#39;s term, the candidate will accept that the leader is legitimate and then transition back to follower state. Otherwise, if the leader has a smaller term than the candidate&#39;s term, the candidate will reject the RPC and continue in its state.&lt;/p&gt;
&lt;p&gt;Lastly, it&#39;s possible that a candidate neither wins nor loses an election. This can happen if there are many candidates at the same time, and the votes get split equally among them. If this happens, the candidates will time out, increase their terms, and then begin another round of the election process. Raft takes an extra measure to prevent &lt;em&gt;split votes&lt;/em&gt; from happening indefinitely, which will be discussed next.&lt;/p&gt;
&lt;h4 id=&quot;election-timeouts-are-randomized&quot;&gt;Election Timeouts Are Randomized &lt;a class=&quot;direct-link&quot; href=&quot;#election-timeouts-are-randomized&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Raft takes measures to prevent split votes in the first place by ensuring that the election timeout of each server is randomly chosen from a fixed interval. This way, the probability that two or more servers time out at the same time and become candidates is reduced. A single server can then win an election and send heartbeat messages to the other servers before their election timeout expires.&lt;/p&gt;
&lt;p&gt;Randomized timeouts are also used to handle split votes. Each candidate&#39;s election timeout is restarted at the start of an election, and the timeout must elapse before it can start another election. This reduces the probability of another split vote in the new election.&lt;/p&gt;
&lt;h4 id=&quot;any-two-majorities-must-overlap-in-at-least-one-server&quot;&gt;Any Two Majorities Must Overlap in At Least One Server &lt;a class=&quot;direct-link&quot; href=&quot;#any-two-majorities-must-overlap-in-at-least-one-server&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;All of Raft&#39;s key decisions rely on getting some confirmation from a majority of the servers in the cluster. The insight here is that if there is a partition in the cluster i.e., one or more nodes cannot communicate with the other set of nodes, at most one of the partitions can have the majority. In addition, any subsequent majorities &lt;em&gt;must&lt;/em&gt; overlap with the previous ones in at least one server. This is how Raft is able to ensure that a term has at most one leader and prevent a split-brain situation. If we have candidates from separate partitions that cannot communicate, only one of them will be able to gain a majority of votes. There is no chance of two candidates in separate partitions gaining a majority for the same term.&lt;/p&gt;
&lt;p&gt;A Raft cluster is typically made up of an odd number of servers. This helps to improve fault tolerance in the cluster. For example, a cluster of 4 nodes needs 3 nodes to make a decision, and a cluster of 5 nodes also needs the same amount of nodes. This means that we can tolerate more failures and still make progress in the cluster. More generally, a cluster of &lt;em&gt;2f + 1&lt;/em&gt; servers can tolerate &lt;em&gt;f&lt;/em&gt; failed servers.&lt;/p&gt;
&lt;p&gt;Systems that rely on the overlap of majority set of servers for operation are referred to as &lt;em&gt;quorum&lt;/em&gt; systems.&lt;/p&gt;
&lt;h3 id=&quot;how-does-log-replication-work%3F&quot;&gt;How does log replication work? &lt;a class=&quot;direct-link&quot; href=&quot;#how-does-log-replication-work%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A leader is responsible for receiving client requests once it has been elected. These client requests contain commands that must be replicated to the servers in the cluster.&lt;/p&gt;
&lt;p&gt;When a request comes in, the leader creates a new log entry containing that command and appends the entry to its log. It then sends AppendEntries RPCs in parallel to its followers. An entry will only be considered as &lt;em&gt;committed&lt;/em&gt; when it has been safely replicated on the majority of its followers. Once an entry is marked as committed, it must be durably persisted on all the followers. If a follower crashes or there is a network fault that drops packets to the follower, the leader will keep retrying the request indefinitely until the follower receives the log entry. All followers &lt;em&gt;must&lt;/em&gt; eventually store committed log entries. When an entry is committed, it can then be executed by the state machine.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/raft-log.png&quot; alt=&quot;Figure 4: Raft Log&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 4 - &quot;Logs are composed of entries, which are numbered sequentially. Each entry contains the term in which it was created (the number in each box) and a command for the state machine. An entry is considered committed if it is safe for that entry to be applied to state machines&quot;&lt;/p&gt;
&lt;p&gt;The protocol for log replication in Raft is described as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each log entry contains a command as well as the term number when the entry was received by the leader. An entry is also identified by an index, which is its position in the log.&lt;/li&gt;
&lt;li&gt;A leader keeps track of the highest entry that it has committed and includes that index in its future AppendEntries RPCs. Note that when the leader commits the entry at an index, that also commits all the preceding entries in its log. Followers apply entries to their state machines once they are notified that the entries have been committed by the leader.&lt;/li&gt;
&lt;li&gt;To maintain consistency in the logs, Raft ensures that the following properties are met:
&lt;ul&gt;
&lt;li&gt;If two entries in different logs have the same index and term, then they store the same command.&lt;/li&gt;
&lt;li&gt;If two entries in different logs have the same index and term, then the logs are identical in all preceding entries&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;These properties constitute what is known as the &lt;em&gt;Log Matching&lt;/em&gt; property. The first property is guaranteed by the fact that a leader will only create one entry at a particular log index with a given term, and the position of a log entry will never change.&lt;/li&gt;
&lt;li&gt;Raft performs a consistency check with the AppendEntries RPC to guarantee the second property above. The leader always includes the index and term of the log entry that immediately precedes the new entries when it sends AppendEntries RPCs to its followers. When a follower receives the RPC, if it does not contain an entry with the same index and term from the leader, it will refuse the entry. If an entry is not refused, it means that the follower&#39;s log is identical to the leader&#39;s log up through the new entries.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;what-happens-if-the-leader-crashes%3F&quot;&gt;What happens if the leader crashes? &lt;a class=&quot;direct-link&quot; href=&quot;#what-happens-if-the-leader-crashes%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;During normal operations, the protocol described above helps the leader and the followers&#39; logs to remain consistent. However, leader crashes can result in inconsistencies. These inconsistencies can then be compounded by subsequent leader and follower crashes.&lt;/p&gt;
&lt;p&gt;The figure below describes how inconsistencies between the logs can play out.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/raft-inconsistencies.png&quot; alt=&quot;Figure 5: Possible inconsistency scenarios&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 5 - Possible inconsistencies in the logs of Raft servers.&lt;/p&gt;
&lt;p&gt;It is possible that any of the scenarios (a-f) in Figure 5 could happen in follower logs when the leader at the top is elected. Each box represents a log entry and the number in a box is the term of the entry. In this figure, we see that a follower may be missing entries (as in a and b), may have extra uncommitted entries (c-d), or both scenarios (e-f). Quoting the paper:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Scenario (f) could occur if that server was the leader for term 2, added several entries to its log, then crashed before committing any of them; it restarted quickly, became leader for term 3, and added a few more entries to its log; before any of the entries in either term 2 or term 3 were committed, the server crashed again and remained down for several terms.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Raft handles inconsistencies by forcing the followers&#39; log to duplicate the leader&#39;s, meaning that conflicting entries in a follower&#39;s log can be overwritten by the entries in the leader&#39;s log. To make the logs between a leader and follower consistent, the leader first finds the latest index at which their logs are identical, deletes all the entries in the follower&#39;s log after that index, and then sends the follower the entries in its own log that come after that point.&lt;/p&gt;
&lt;h3 id=&quot;why-is-it-safe-for-a-new-leader-to-overwrite-its-follower&#39;s-logs%3F&quot;&gt;Why is it safe for a new leader to overwrite its follower&#39;s logs? &lt;a class=&quot;direct-link&quot; href=&quot;#why-is-it-safe-for-a-new-leader-to-overwrite-its-follower&#39;s-logs%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Raft places a restriction on what servers can be elected as a leader with the &lt;em&gt;Leader Completeness Property&lt;/em&gt;, which states that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The leader for any given term must contain all the entries committed in previous terms.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is able to enforce this because the RequestVote RPC contains information about the candidate&#39;s log, and a voter will deny a candidate&#39;s vote request if its log is more up-to-date than the candidate&#39;s. Since a candidate must get votes from the majority, at least one node in the majority must have the latest entries. A log is more up-to-date than another if it has a higher term number. If the two logs have the same term number, the log with the higher index entry is the more up-to-date one.&lt;/p&gt;
&lt;p&gt;By this restriction, it is safe for a leader to overwrite a follower&#39;s log since the leader will &lt;em&gt;always&lt;/em&gt; have the latest committed entries. Any uncommitted entries can be safely discarded because there is no expectation on the client&#39;s side that their request has been executed. Only committed entries guarantee that. Therefore, the server can return an error message to the client, telling it to retry the requests for the uncommitted entries.&lt;/p&gt;
&lt;h3 id=&quot;what-happens-when-a-follower-or-candidate-crashes%3F&quot;&gt;What happens when a follower or candidate crashes? &lt;a class=&quot;direct-link&quot; href=&quot;#what-happens-when-a-follower-or-candidate-crashes%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a follower crashes, any RPCs sent to it will fail. These requests are retried indefinitely until the server restarts and the RPC completes. It is safe for the AppendEntries and RequestVote RPCs to be retried because they are &lt;a href=&quot;https://stackoverflow.com/a/1077421/5430313&quot;&gt;idempotent&lt;/a&gt;. For example, a follower can ignore an AppendEntries RPC if the request contains entries that are already present in its logs. Candidate crashes are handled in the same way.&lt;/p&gt;
&lt;h3 id=&quot;how-do-we-choose-the-right-election-timeout%3F&quot;&gt;How do we choose the right election timeout? &lt;a class=&quot;direct-link&quot; href=&quot;#how-do-we-choose-the-right-election-timeout%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although Raft does not depend on timing to ensure the correctness of the logs, timing is important in ensuring that client requests are responded to swiftly. If the election timeout is too long, the servers might be without a leader for some time, which will delay the time it takes to respond to a client request.&lt;/p&gt;
&lt;p&gt;The timing requirement that a Raft system should maintain is stated in the paper as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;broadcast Time &amp;lt;&amp;lt; election Timeout &amp;lt;&amp;lt; MTBF&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;where &lt;em&gt;broadcast time&lt;/em&gt; is the average time taken by a server to send RPCs to all the servers in parallel and receive responses from them, and &lt;em&gt;MTBF&lt;/em&gt; stands for the Mean Time Between Failures for a single server.&lt;/p&gt;
&lt;p&gt;By selecting an election timeout that is significantly greater than the broadcast time, it means that leaders can reliably send the required heartbeat messages to prevent unnecessary elections.&lt;/p&gt;
&lt;p&gt;In addition, the election timeout should be orders of magnitude less than the average time it takes for a server to fail. If the MTBF is less than the chosen election timeout, there could be a delay between when a leader fails and when its followers begin a new election. However, by selecting a timeout that is based on the MTBF, we can reduce that delay on average.&lt;/p&gt;
&lt;h3 id=&quot;how-does-raft-tame-its-logs%3F&quot;&gt;How does Raft tame its logs? &lt;a class=&quot;direct-link&quot; href=&quot;#how-does-raft-tame-its-logs%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To prevent the log from growing indefinitely, which can increase the time it takes for the state machine to replay a log when it restarts, Raft uses &lt;em&gt;snapshotting&lt;/em&gt; for log compaction. A snapshot of the current application&#39;s state is written to durable storage, and all the log entries up to the point of the snapshot are deleted from the log. Each server takes its snapshots independently, and snapshots are taken when the log reaches a fixed size in bytes.&lt;/p&gt;
&lt;p&gt;A snapshot also contains metadata such as the &lt;em&gt;last included index,&lt;/em&gt; which is the index of the last entry in the log being replaced by the snapshot, and the &lt;em&gt;last included term.&lt;/em&gt; These metadata are kept because of the AppendEntries consistency check for the first log entry after the snapshot, which needs a previous log entry and term.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a few details missed in this post about how Raft manages the changes to the server in a cluster, and how client interactions are handled, among others. I do hope, however, that this post has helped you build some intuition for a consensus algorithm like Raft if you were not previously familiar. The further reading section contains links to resources with more detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The study group will be going on a break for the two weeks until 15 June, and so there will be a longer delay before the next lecture summary.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/raft-extended.pdf&quot;&gt;In Search of an Understandable Consensus Algorithm (Extended Version)&lt;/a&gt; by Diego Ongaro and John Ousterhout. The original Raft paper.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://raft.github.io/&quot;&gt;Raft Consensus Algorithm&lt;/a&gt; - Site with more Raft resources.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://thesecretlivesofdata.com/raft/&quot;&gt;The Secret Life of Data&lt;/a&gt; - Really cool Raft visualization.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-raft.txt&quot;&gt;Lecture 6: Raft (1)&lt;/a&gt; and &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-raft2.txt&quot;&gt;Lecture 7: Raft (2)&lt;/a&gt;- MIT 6.824 Lecture Notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying&quot;&gt;The Log: What every software engineer should know about real-time data&#39;s unifying abstraction&lt;/a&gt; by Jay Kreps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Last updated on 15-02-2025 to reduce clutter.&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 5 -  Go, Threads, and Raft</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-5-go-threads-and-raft/"/>
		<updated>2020-05-19T18:50:01-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-5-go-threads-and-raft/</id>
		<content type="html">&lt;p&gt;Although &#39;Raft&#39; is mentioned in the title, a better title for this post is &#39;Concurrency in Go&#39;, as Raft will not be discussed until the next post. Also, unlike other posts on this blog, this one will also feature code samples! Examples of good and bad Go code will be shown for building concurrent applications.&lt;/p&gt;
&lt;p&gt;Note that although the examples below are in Go, these concepts apply more generally to other programming languages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#mutexes&quot;&gt;Mutexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#condition-variables&quot;&gt;Condition Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#channels&quot;&gt;Channels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;mutexes&quot;&gt;Mutexes &lt;a class=&quot;direct-link&quot; href=&quot;#mutexes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Goroutines are lightweight threads in Go. Every Go program has at least one goroutine running, which is the main one from which other goroutines are started. Goroutines execute their functions asynchronously.&lt;/p&gt;
&lt;p&gt;In the block below, goroutines are started on line 4 using the &lt;em&gt;go&lt;/em&gt; keyword. The for loop on line 3 starts 1000 goroutines, each of which increments the counter variable declared on line 2.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://timilearning.com/uploads/basic-no-mutex.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Can you spot the bug?&lt;/p&gt;
&lt;p&gt;Now, if you wrote a function like above, chances are that you would want and expect the final value of the counter to be equal to 1000. However, when this program is executed, it is highly unlikely that the final value of the counter is 1000.&lt;/p&gt;
&lt;p&gt;This is because the counter field is shared by all the goroutines, and we are not protecting access to that variable. For example, if the current value of the counter is 5 and two goroutines try to increment the value at the same time, the value of the counter after the operations will be 6, rather than the 7 we expect after two increments. This is a &lt;em&gt;race condition,&lt;/em&gt; and a program like this must take care to prevent it.&lt;/p&gt;
&lt;p&gt;Go has &lt;em&gt;mutexes&lt;/em&gt;, which help prevent race conditions when used properly. Mutexes can be used to protect &lt;em&gt;critical sections&lt;/em&gt; in your code. Critical sections are those blocks of code which should only be accessed by one thread at a time. Those blocks will typically involve accessing shared variables, and protecting them is essential in ensuring that your program runs predictably.&lt;/p&gt;
&lt;p&gt;In the block below, the variable &lt;em&gt;mu&lt;/em&gt; declared on line 3 represents the mutex, and we wrap a lock around the shared counter variable on lines 6-8.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://timilearning.com/uploads/basic-mutex.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;When this program is executed, the final value of the counter will be 1000, as expected.&lt;/p&gt;
&lt;p&gt;More generally, &lt;em&gt;use locks when you want to protect invariants in a concurrent application&lt;/em&gt;, i.e. properties of your application which should always be true. To explain this, let&#39;s look at this example below in which we perform operations for two clients of a bank: Seyi and Yinka.&lt;/p&gt;
&lt;p&gt;Seyi and Yinka both start the operation with 10,000 in each of their accounts, leading to a total of 20,000 in the bank. In the first goroutine declared on line 8, Seyi transfers money from her account to Yinka&#39;s. In the second goroutine on line 19, Yinka transfers money from his account to Seyi&#39;s.&lt;/p&gt;
&lt;p&gt;An author of a program like this will likely expect the sum of the amount in each account to always be equal to 20,000—since any amount removed from one account is expected to be transferred to the other immediately.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://timilearning.com/uploads/bank-per-item.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Can you spot the bug?&lt;/p&gt;
&lt;p&gt;When this program is run, the code on line 33 will likely be executed multiple times, highlighting that there were multiple periods where that invariant—that the sum of each accounts&#39; value should equal the initial total value—is observed to be violated.&lt;/p&gt;
&lt;p&gt;The problem in this example is that although we have placed locks on each shared variable, locks by themselves are not enough to enforce the invariants in our program. If a section of code must be executed atomically (i.e. all the operations in that section must either execute together or not execute at all), then a lock should wrap around that section.&lt;/p&gt;
&lt;p&gt;A modified version of the bank example is shown below. In this example, we see on lines 10-13 and lines 18-21 that locks are wrapped around the critical sections, which will help protect the invariants we are interested in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://timilearning.com/uploads/bank-proper.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;condition-variables&quot;&gt;Condition Variables &lt;a class=&quot;direct-link&quot; href=&quot;#condition-variables&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To introduce condition variables, let&#39;s consider this example below.&lt;/p&gt;
&lt;p&gt;In this program, we have a for loop on line 6 which starts goroutines from the main goroutine and tries to gain votes from them on line 8. If it wins a vote, the shared &lt;em&gt;count&lt;/em&gt; variable is incremented. After each vote is cast, the &lt;em&gt;finished&lt;/em&gt; variable is also incremented.&lt;/p&gt;
&lt;p&gt;On line 18, we have a for loop which will prevent the main goroutine from progressing until it has either won at least 5 votes or all 10 goroutines have cast their vote.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://timilearning.com/uploads/no-cond-var.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Note that we have placed locks to protect our invariants, and this program will indeed execute as we expect. However, on closer observation, the for loop starting on line 18 is not the best use of the CPU&#39;s resources. In each iteration, it will obtain a lock, check that the condition is met and then release the lock. The problem is that there is nothing guiding the number of iterations that the loop will undergo before the &lt;em&gt;break&lt;/em&gt; condition on line 20 is met. This means that there could many unnecessary loop iterations which will each obtain access to the shared variable.&lt;/p&gt;
&lt;p&gt;What if there was a way to run an iteration of the loop &lt;em&gt;only&lt;/em&gt; when it is possible for the break condition to have been met? In the example above, the condition can only be met when the requestVote() call returns. Thanks to &lt;em&gt;Condition Variables,&lt;/em&gt; we can delay the execution of the loop until it is possible for the break condition to have been met.&lt;/p&gt;
&lt;p&gt;This next example illustrates that.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://timilearning.com/uploads/cond-var.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this example, we have initialized a condition variable on line 5 and passed it the address of the mutex. When the for loop on line 21 is reached and the condition isn&#39;t met, we use the &lt;em&gt;cond.Wait()&lt;/em&gt; method to pause the execution of the main goroutine. A key thing to note is that &lt;em&gt;cond.Wait() releases the lock being held on mu by the current Goroutine&lt;/em&gt;. This execution pause and lock release will happen until the &lt;em&gt;cond.Broadcast()&lt;/em&gt; method is executed on line 16.&lt;/p&gt;
&lt;p&gt;Any newly cast vote may cause the break condition to be true, and so we use the cond.Broadcast() method to inform all the threads that are waiting on that condition to resume their execution, and try to obtain the lock on the mutex again.&lt;/p&gt;
&lt;p&gt;This is a more efficient use of CPU resources than looping continuously without any knowledge of whether the break condition could have been met or not.&lt;/p&gt;
&lt;p&gt;The general format when using condition variables is as shown in the pseudocode below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://timilearning.com/uploads/cond-var-structure.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;channels&quot;&gt;Channels &lt;a class=&quot;direct-link&quot; href=&quot;#channels&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A &lt;a href=&quot;https://tour.golang.org/concurrency/2&quot;&gt;Channel&lt;/a&gt; is a primitive for communicating between goroutines. The general idea is that one goroutine sends a &lt;em&gt;message&lt;/em&gt; on the channel, and that message is received from that channel on a separate goroutine.&lt;/p&gt;
&lt;p&gt;An example is shown below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://timilearning.com/uploads/channel.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The channel operator is used on lines 5 and 8 to communicate between the goroutines.&lt;/p&gt;
&lt;p&gt;Some key things to note about channels in their default usage are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Channels have no internal capacity, not even of one. The implication of this is that for every send operation to a channel, there must be a corresponding receive operation, and vice versa.&lt;/li&gt;
&lt;li&gt;Every send operation will block its thread until a receive happens, and vice versa.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go has buffered channels which may have some internal capacity that corresponds to a user-defined value. With buffered channels, sends are non-blocking until the buffer is full, and receive operations are non-blocking until the buffer is empty.&lt;/p&gt;
&lt;p&gt;A major use of channels is for implementing producer/consumer queues.&lt;/p&gt;
&lt;p&gt;If you&#39;re a Java developer, the &lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html&quot;&gt;BlockingQueue&lt;/a&gt; interface is similar to Go Channels, with the &lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ArrayBlockingQueue.html&quot;&gt;ArrayBlockingQueue&lt;/a&gt; being an implementation of a buffered channel, and a &lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/SynchronousQueue.html&quot;&gt;SynchronousQueue&lt;/a&gt; being an unbuffered channel.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We have gone over some Go constructs that will help when building concurrent applications. I&#39;ve had to make use of some of these in working out the &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/labs/lab-mr.html&quot;&gt;labs&lt;/a&gt;. If you&#39;re interested in learning more about concurrency in Go, I would highly recommend checking out &lt;a href=&quot;https://golang.org/ref/mem&quot;&gt;this&lt;/a&gt; page on the official Go site.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The code samples are lifted from the course material and can be downloaded from &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/go-concurrency.tar.gz&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://golang.org/ref/mem&quot;&gt;The Go Memory Model &lt;/a&gt; - Official Documentation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-go-concurrency.txt&quot;&gt;Go Concurrency&lt;/a&gt; - MIT 6.824 Lecture Notes&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 4 - Primary/Backup Replication</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-4-primary-backup-replication/"/>
		<updated>2020-05-09T19:11:43-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-4-primary-backup-replication/</id>
		<content type="html">&lt;p&gt;This lecture&#39;s material was on the subject of replication as a means of achieving fault tolerance in a distributed system. The &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/vm-ft.pdf&quot;&gt;VMware FT paper&lt;/a&gt; was used as a case study on how replication can be implemented.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#vmware-ft-paper-summary&quot;&gt;VMware FT Paper Summary&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#glossary&quot;&gt;Glossary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#overview&quot;&gt;Overview&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#deterministic-replay&quot;&gt;Deterministic Replay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ft-protocol&quot;&gt;FT Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#detecting-and-handling-failures&quot;&gt;Detecting and Handling Failures&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#practical-implementation-of-ft&quot;&gt;Practical Implementation of FT&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#starting-and-restarting-vms&quot;&gt;Starting and Restarting VMs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#managing-the-logging-channel&quot;&gt;Managing the Logging Channel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementation-issues-for-disk-io&quot;&gt;Implementation Issues for Disk IO&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#issue-%231&quot;&gt;Issue #1&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#solution&quot;&gt;Solution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#issue-%232&quot;&gt;Issue #2&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#solution-1&quot;&gt;Solution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#issue-%233&quot;&gt;Issue #3&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#solution-2&quot;&gt;Solution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#design-alternatives&quot;&gt;Design Alternatives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#open-question-i-have&quot;&gt;Open Question I Have&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#update&quot;&gt;Update&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;replication-approaches&quot;&gt;Replication Approaches &lt;a class=&quot;direct-link&quot; href=&quot;#replication-approaches&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are two main ways in which replication can be implemented in a cluster:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;State Transfer&lt;/strong&gt;: In this mode, the primary replica executes all the relevant operations and regularly ships its state to the backup(s). This state could include CPU changes, memory and I/O device changes, etc, and this approach to replication often requires a lot of bandwidth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replicated State Machine&lt;/strong&gt;: The idea here is that &lt;em&gt;if two state machines are fed the same input operations in the same order, their outputs will be the same&lt;/em&gt;. The caveat is that these input operations should be deterministic. VMware FT uses this approach for replication and does work to ensure that non-deterministic operations are applied in a deterministic way. This will be explained further in a later section.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;replication-challenges&quot;&gt;Replication Challenges &lt;a class=&quot;direct-link&quot; href=&quot;#replication-challenges&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Implementing replication is challenging, and some of those challenges deal with answering the questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What state do we replicate?&lt;/li&gt;
&lt;li&gt;Should the primary machine always wait for the backup to apply the replicated state?&lt;/li&gt;
&lt;li&gt;When should the backup should take over and become the primary?&lt;/li&gt;
&lt;li&gt;How do we ensure that a replacement backup gets in sync with the primary?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As for what state to replicate, the options include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Application-Level State&lt;/strong&gt;: An example of this is replicating a database&#39;s tables. &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-3-gfs/&quot;&gt;GFS&lt;/a&gt; replicates application-level state. In this case, the primary will only send high level operations like database queries to its backups.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Machine-Level State:&lt;/strong&gt; This involves replicating things like the contents of the RAM and registers, machine events like interrupts and DMA, etc. The primary also forwards machine events (interrupts, DMA) to the backup. VMware FT replicates machine-level state. It&#39;s able to do this because it has full control of the execution of both the primary and backup, which makes it easier to deal with non-deterministic events. I&#39;ll go into more detail about this later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For all the benefits of replication, it&#39;s important to note that replication is not able to protect against all kinds of failures. There are different failure modes in distributed systems and the failures dealt with in the paper&#39;s implementation are &lt;em&gt;fail-stop failures.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Fail-stop failures are noticeable failures which cause a machine to stop executing. Here, it is assumed that the machine will not compute any bad results in the process of failing. Replication can help to provide fault tolerance for these kinds of failures. On the other hand, replication may less helpful when dealing with hardware defects, software bugs or human configuration errors. These may sometimes be detected using checksums, for example.&lt;/p&gt;
&lt;h2 id=&quot;vmware-ft-paper-summary&quot;&gt;VMware FT Paper Summary &lt;a class=&quot;direct-link&quot; href=&quot;#vmware-ft-paper-summary&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h4 id=&quot;glossary&quot;&gt;Glossary &lt;a class=&quot;direct-link&quot; href=&quot;#glossary&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Guest OS&lt;/strong&gt;: A Guest OS is the operating system installed on a virtual machine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hypervisor:&lt;/strong&gt; A Hypervisor (also known as a Virtual Machine Monitor) is the software which creates, runs and manages virtual machines.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Host OS:&lt;/strong&gt; The Host OS is the operating system installed on the physical machine on which the hypervisor runs. (Note: There are some hypervisors which interact directly with the hardware in place of a Host OS, but those are not the focus of this topic.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Virtual Machine:&lt;/strong&gt; A virtual machine is an emulation of a physical computer system. Like physical machines, virtual machines are able to run an operating system and execute its applications as normal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VMware vSphere FT:&lt;/strong&gt; This is an implementation of fault tolerance on VMware&#39;s cloud computing virtualization platform.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;direct-link&quot; href=&quot;#overview&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/basic-ft.png&quot; alt=&quot;Figure 1: Basic FT Configuration. Illustrates the interaction between the Primary VM and Backup VM via a Logging channel&quot;&gt; &lt;/p&gt;
&lt;p&gt;Figure 1 illustrates the FT configuration which involves a primary VM and its backup VM. The backup VM is located on a different physical server from the primary and is kept in sync with the execution of the primary. The VMs are said to be in &lt;em&gt;virtual lock-step.&lt;/em&gt; They also have their virtual disks located on shared storage, which means that the disks are accessible by either VM.&lt;/p&gt;
&lt;p&gt;All the inputs (e.g. network, mouse, keyboard, etc.) go to the primary VM. These inputs are then forwarded to the backup VM via a network connection known as the &lt;em&gt;logging channel.&lt;/em&gt; For non-deterministic input operations, additional information is also sent to ensure that the backup VM executes them in a deterministic way. Both VMs execute these operations but only the outputs produced by the primary VM are visible to the client. The outputs of the backup VM get dropped by the hypervisor.&lt;/p&gt;
&lt;h4 id=&quot;deterministic-replay&quot;&gt;Deterministic Replay &lt;a class=&quot;direct-link&quot; href=&quot;#deterministic-replay&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As mentioned earlier, for state machine replication to work, there needs to be a way to deal with the presence of non-deterministic instructions. A virtual machine can have non-deterministic events like virtual interrupts, and non-deterministic operations like reading the current clock cycle counter of the processor. These instructions may yield different results even if the primary and its backup have the same state. Therefore, it is important for them to be handled carefully to ensure consistency between the two machines.&lt;/p&gt;
&lt;p&gt;The occurrence of this non-determinism presents three challenges when replicating the execution of a VM:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All the input and non-determinism on the primary VM must be correctly captured to ensure that it can be executed deterministically on the backup VM.&lt;/li&gt;
&lt;li&gt;The inputs and non-determinism must be correctly applied to the backup VM.&lt;/li&gt;
&lt;li&gt;They must be applied in a manner that does not degrade the performance of the system.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To deal with these challenges, VMware has a &lt;a href=&quot;http://www-mount.ece.umn.edu/~jjyi/MoBS/2007/program/01C-Xu.pdf&quot;&gt;&lt;em&gt;deterministic replay functionality&lt;/em&gt;&lt;/a&gt; that is able to capture all the inputs to a primary VM as well as possible non-determinism, and replay these events in their exact order of execution on a backup VM.&lt;/p&gt;
&lt;h4 id=&quot;ft-protocol&quot;&gt;FT Protocol &lt;a class=&quot;direct-link&quot; href=&quot;#ft-protocol&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We have seen so far that VMware FT uses deterministic replay to produce the relevant log entries and ensure that the backup VM executes identically to the primary VM. However, to ensure that the backup VM consistent in a way that makes it indistinguishable from the primary to a client, VMware FT has the following requirement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Output Requirement:&lt;/strong&gt; If the backup VM ever takes over after a failure of the primary, the backup VM will continue executing in a way that is entirely consistent with all outputs that the primary VM has sent to the external world.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The rule set in place to achieve the output requirement is the Output Rule stated as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Output Rule:&lt;/strong&gt; The primary VM may not send an output to the external world, until the backup VM has received and acknowledged the log entry associated with the operation producing the output.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The idea is that as long as the backup VM has received all the log entries (including for any output-producing operations), then it will be able to replay up to the state last seen by the client if the primary VM crashes.&lt;/p&gt;
&lt;p&gt;This rule is illustrated below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/ft-protocol-small.png&quot; alt=&quot;Figure 2: Illustration of the Output Rule&quot;&gt; &lt;/p&gt;
&lt;p&gt;As shown in Figure 2, the output to the external world is delayed until the primary VM has received an acknowledgment from the backup VM that it has received the associated log entry for the output operations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; VMware FT does not guarantee that all outputs are produced exactly once during a failover situation. In a scenario where the primary crashes &lt;em&gt;after&lt;/em&gt; the backup has received the log entry for the latest output operation, the backup cannot tell if the primary crashed immediately before or after sending its latest output. Therefore, the backup may re-execute an output operation. The good news is that the VMware setup can rely on its network infrastructure to detect duplicate packets and prevent them from being retransmitted to the client.&lt;/p&gt;
&lt;h4 id=&quot;detecting-and-handling-failures&quot;&gt;Detecting and Handling Failures &lt;a class=&quot;direct-link&quot; href=&quot;#detecting-and-handling-failures&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Heartbeat operations are used in combination with monitoring the traffic on the logging channel to detect failures of either VM. The system can rely on the traffic on the logging channel because it logs timer interrupts (which occur regularly on a guest OS), and so when the traffic slows down, it can safely assume that the guest OS is not functioning.&lt;/p&gt;
&lt;p&gt;If the backup fails, the primary stops sending entries on the logging channel and just keeps executing as normal. If you&#39;re wondering how the backup will be able to catch up later, VMware has a tool called &lt;a href=&quot;https://www.vmware.com/pdf/vmotion_datasheet.pdf&quot;&gt;VMotion&lt;/a&gt; that is able to clone a VM with minimal interruption to the execution of the VM. I&#39;ll touch more on that later.&lt;/p&gt;
&lt;p&gt;If the primary fails, the backup VM must first replay its execution until it has consumed the last log entry. After that point, the backup will take over as the primary and can now start producing output to the external world.&lt;/p&gt;
&lt;p&gt;To avoid a split-brain situation by ensuring that only one VM can be the primary at a time, VMware uses an atomic test-and-set operation on the shared storage. This is relevant if the primary and backup lose network communication with each other and they both attempt to go-live. The operation will succeed for only one of the machines at a time.&lt;/p&gt;
&lt;p&gt;VMware FT is able to restore redundancy when either of the VMs fails by automatically starting a new backup VM on another host.&lt;/p&gt;
&lt;h3 id=&quot;practical-implementation-of-ft&quot;&gt;Practical Implementation of FT &lt;a class=&quot;direct-link&quot; href=&quot;#practical-implementation-of-ft&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;starting-and-restarting-vms&quot;&gt;Starting and Restarting VMs &lt;a class=&quot;direct-link&quot; href=&quot;#starting-and-restarting-vms&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A challenge in building a system like this is in figuring out how to start up a backup VM in the same state as the primary VM while the primary is running. To address this, VMware modified an existing tool called VMware VMotion. In its original form, VMware VMotion allows for a running VM to be migrated from one server to another with minimal disruption. However, for fault tolerance purposes, the tool was reworked as FT VMotion to allow for the &lt;em&gt;cloning&lt;/em&gt; of a VM to a remote host. This cloning operation interrupts the execution of the primary by less than a second.&lt;/p&gt;
&lt;p&gt;A logging channel is set up automatically by FT VMotion between the source VM and the destination, with the source entering logging mode as the primary, and the destination entering replay mode as a backup.&lt;/p&gt;
&lt;h4 id=&quot;managing-the-logging-channel&quot;&gt;Managing the Logging Channel &lt;a class=&quot;direct-link&quot; href=&quot;#managing-the-logging-channel&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/logging-channel-small.png&quot; alt=&quot;Figure 3: FT Logging Buffers and Channel&quot;&gt; &lt;/p&gt;
&lt;p&gt;Figure 3 above illustrates how the flow of log entries from when they are produced by the primary VM to their consumption at the backup VM.&lt;/p&gt;
&lt;p&gt;If the log buffer of the backup VM is empty, the VM will stop its execution until the next log entry arrives. This pause can be tolerated since the backup VM is not interacting with clients.&lt;/p&gt;
&lt;p&gt;On the other hand, the primary VM will stop executing when its log buffer is full, and the execution is paused until the log entries are flushed out. However, this pause can affect clients and so work must be done to minimize the possibility of the log buffer getting filled up.&lt;/p&gt;
&lt;p&gt;The log buffer of the primary may fill up when the backup VM is executing too slowly and thus consuming its log entries too slowly. This can happen when the host machine of the backup VM is overloaded with other VMs, and so the backup is unable to get enough CPU and memory resources to execute normally.&lt;/p&gt;
&lt;p&gt;Apart from avoiding the pauses due to a full log buffer in the primary VM, another reason why we do not want the backup to execute slowly is to reduce the time that it takes to &amp;quot;go-live&amp;quot;; i.e. during the failover process, if the backup is lagging far behind the latest log entry, it will take longer for the backup to take over as the primary. This delay will be noticeable by a client.&lt;/p&gt;
&lt;p&gt;Interestingly, a mechanism has been implemented in VMware FT to slow down the execution of the primary VM when the backup VM is starting to get far behind (more than 1 second behind according to the paper). The execution lag between the primary and backup VM is typically less than 100 milliseconds, and so it makes sense that a lag of 1 second will raise an alarm. The primary VM is slowed down by giving it a smaller amount of CPU resources to use.&lt;/p&gt;
&lt;h4 id=&quot;implementation-issues-for-disk-io&quot;&gt;Implementation Issues for Disk IO &lt;a class=&quot;direct-link&quot; href=&quot;#implementation-issues-for-disk-io&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This section details some challenges related to disk IO in building a system like this, and how VMware FT addresses these challenges.&lt;/p&gt;
&lt;h5 id=&quot;issue-%231&quot;&gt;Issue #1 &lt;a class=&quot;direct-link&quot; href=&quot;#issue-%231&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Simultaneous disk operations to the same location can lead to non-determinism, since these operations can execute in parallel and are non-blocking.&lt;/p&gt;
&lt;h6 id=&quot;solution&quot;&gt;Solution &lt;a class=&quot;direct-link&quot; href=&quot;#solution&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Their implementation is able to detect such IO races and force racing disk operations to execute sequentially.&lt;/p&gt;
&lt;h5 id=&quot;issue-%232&quot;&gt;Issue #2 &lt;a class=&quot;direct-link&quot; href=&quot;#issue-%232&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;What happens with disk IO operations that are not completed on the primary due to a failure, and the backup takes over? The challenge here is that the backup cannot know whether or not the operation completed successfully.&lt;/p&gt;
&lt;h6 id=&quot;solution-2&quot;&gt;Solution &lt;a class=&quot;direct-link&quot; href=&quot;#solution-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Those operations are simply reissued.&lt;/p&gt;
&lt;h5 id=&quot;issue-%233&quot;&gt;Issue #3 &lt;a class=&quot;direct-link&quot; href=&quot;#issue-%233&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;If a network packet or requested disk block arrives and needs to be copied into the main memory of the primary VM while an application running in the VM is also reading from the same memory, that application may or may not see the new data coming in. This non-determinism is a race condition.&lt;/p&gt;
&lt;h6 id=&quot;solution-3&quot;&gt;Solution &lt;a class=&quot;direct-link&quot; href=&quot;#solution-3&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;They avoid this race by using &lt;em&gt;bounce buffers&lt;/em&gt; to temporarily hold any incoming packet or disk block in the primary VM. When the data is copied to the buffer, the primary is interrupted and the bounce buffer is copied into the primary&#39;s memory, after which it can resume its execution. This interrupt is logged as an entry in the logging channel, ensuring that both the primary and backup will execute the operations at the same time.&lt;/p&gt;
&lt;h3 id=&quot;design-alternatives&quot;&gt;Design Alternatives &lt;a class=&quot;direct-link&quot; href=&quot;#design-alternatives&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This section compares some alternative design choices that were explored before the implementation of VMware FT and the tradeoffs they settled for.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shared vs Non-shared Disk:&lt;/strong&gt; The implementation of VMware FT uses a shared storage that is accessible to both the primary and backup VMs. In an alternative design, the VMs could have separate virtual disks which they can write to separately. This design could be used in situations where shared storage is not accessible to both the primary and secondary VM or shared storage is too expensive. A disadvantage of the non-shared design in this system is that extra work needs to be done to keep not just the running states in sync, but also their disk states.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Executing Disk Reads on the Backup VM:&lt;/strong&gt; In the current implementation, the results of disk reads from the primary VM are sent to the backup via the logging channel. In an alternative design, the backup VM could just perform its disk reads directly. This approach can help to reduce the logging channel traffic when a lot of disk reads are involved in a workload. However, two main challenges with this approach are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It may slow down the execution of the backup VM. Remember that we need the backup VM to execute quickly to speed up the failover process.&lt;/li&gt;
&lt;li&gt;What if the read succeeds on the primary but fails on the backup(&lt;em&gt;and vice versa&lt;/em&gt;)?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Performance evaluation by VMware showed that executing disk reads on the backup slowed down throughput by 1-4%, but also reduced the logging bandwidth.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This was an interesting lecture for me because my knowledge of replication was limited to data replication (application-level state), and so getting exposed to a different approach has been a good experience.&lt;/p&gt;
&lt;p&gt;One limitation of this system is that it only supports uniprocessor executions, as the non-determinism involved in multi-core parallelism will make it even more challenging to implement. VMware has since extended the system described in the paper with an &lt;a href=&quot;http://www.wooditwork.com/2014/08/26/whats-new-vsphere-6-0-fault-tolerance/&quot;&gt;implementation&lt;/a&gt; that does support mutli-core parallelism. I do not know the details of that system yet and can&#39;t say much about what has changed.&lt;/p&gt;
&lt;h3 id=&quot;open-question-i-have&quot;&gt;Open Question I Have &lt;a class=&quot;direct-link&quot; href=&quot;#open-question-i-have&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the &lt;a href=&quot;#detecting-and-handling-failures&quot;&gt;section&lt;/a&gt; on detecting and handling failures, it was mentioned that an atomic test-and-set operation is used to prevent split-brain scenarios. What is unclear to me is how those operations work.&lt;/p&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/vm-ft-faq.txt&quot;&gt;FAQ&lt;/a&gt; on the course website for this lecture, the pseudocode of such an operation looks like:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt; test&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    acquire_lock&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; flag &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; true&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      release_lock&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; false&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      flag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; true&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      release_lock&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is unclear to me is when the flag is set to false; i.e. if the operation succeeds for a VM, does that VM set the flag to false when it is about to fail? I&#39;m assuming it does, but would like some confirmation on this.&lt;/p&gt;
&lt;h4 id=&quot;update&quot;&gt;Update &lt;a class=&quot;direct-link&quot; href=&quot;#update&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I sent an email about the above question to the course staff and got this response from Prof. Morris on how the ATS operation works:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The flag is ordinarily false, when both primary and backup are alive.&lt;/p&gt;
&lt;p&gt;If the primary and backup stop being able to talk to each other, the one&lt;br&gt;
that is alive will use test-and-set to set the flag and acquire the&lt;br&gt;
right to &amp;quot;go live&amp;quot;. If both are alive but can&#39;t talk to each other, both&lt;br&gt;
may call test-and-set() but only one will get a &amp;quot;true&amp;quot; return value, so&lt;br&gt;
only one will go live -- thus avoiding split brain.&lt;/p&gt;
&lt;p&gt;The paper doesn&#39;t say when the flag is cleared to false. Perhaps a&lt;br&gt;
human administrator does it after restarting the failed server and&lt;br&gt;
ensuring that it&#39;s up to date. Perhaps the server that &amp;quot;went live&amp;quot;&lt;br&gt;
clears the flag after it sees that the other server is alive again&lt;br&gt;
and is up to date.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-vm-ft.txt&quot;&gt;Lecture 4: Primary/Backup Replication&lt;/a&gt; - MIT 6.824 Lecture Notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://alvaro-videla.com/2013/12/failure-modes-in-distributed-systems.html&quot;&gt;Failure Modes In Distributed Systems&lt;/a&gt; by Alvaro Videla&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Last updated on 12-05-20.&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 3 - GFS</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-3-gfs/"/>
		<updated>2020-05-02T00:03:19-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-3-gfs/</id>
		<content type="html">&lt;p&gt;The &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/gfs.pdf&quot;&gt;Google File System paper&lt;/a&gt; is relevant to this course because GFS is an example of &lt;em&gt;distributed storage&lt;/em&gt;, which is a key abstraction in building distributed systems. Many distributed systems are either distributed storage systems or systems built on top of distributed storage.&lt;/p&gt;
&lt;p&gt;Building distributed storage is a hard problem for a couple of reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;These systems are built to get a &lt;em&gt;high performance&lt;/em&gt; when the volume of data is too large for a single machine, which leads us to &lt;em&gt;sharding (splitting)&lt;/em&gt; the data over multiple servers.&lt;/li&gt;
&lt;li&gt;Because multiple servers are involved, there will likely be more &lt;em&gt;faults&lt;/em&gt; in the system.&lt;/li&gt;
&lt;li&gt;To improve fault tolerance, the data is usually &lt;em&gt;replicated&lt;/em&gt; across multiple machines.&lt;/li&gt;
&lt;li&gt;Replication of data leads to potential &lt;em&gt;inconsistencies&lt;/em&gt; in the data. A client could read from a stale replica, for example.&lt;/li&gt;
&lt;li&gt;The protocols for better consistency often lead to a &lt;em&gt;lower performance,&lt;/em&gt; which is the opposite of what we want.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This cycle, which leads back to performance, highlights the challenges in building distributed systems. The GFS paper touches on these topics and discusses the trade-offs that were made to yield good performance in a production-ready distributed storage system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#paper-summary&quot;&gt;Paper Summary&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#design-overview&quot;&gt;Design Overview&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#single-master&quot;&gt;Single Master&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#chunk-size&quot;&gt;Chunk Size&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#metadata&quot;&gt;Metadata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#consistency-model&quot;&gt;Consistency Model&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#record-appends&quot;&gt;Record Appends&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#system-interactions&quot;&gt;System Interactions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#writes&quot;&gt;Writes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#atomic-record-appends&quot;&gt;Atomic Record Appends&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#fault-tolerance&quot;&gt;Fault Tolerance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#data-integrity&quot;&gt;Data Integrity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;paper-summary&quot;&gt;Paper Summary &lt;a class=&quot;direct-link&quot; href=&quot;#paper-summary&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The system was built at a time when Google needed a system to meet its data-processing demands with the goals of achieving good performance while being:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Global:&lt;/em&gt; Not tailored for just one application, but available to many Google applications.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Fault Tolerant&lt;/em&gt; : Designed to account for component failures by default.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Scalable:&lt;/em&gt; It could expand to meet increasing storage needs by adding extra servers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also note that it was tailored for a workload that largely consisted of sequential access to huge files (read or append). It was not built to optimize for low-latency requests; rather, it was meant for batch workloads which often read a file sequentially, as in &lt;a href=&quot;https://timilearning.com/posts/mit-6.824/lecture-1-mapreduce/&quot;&gt;MapReduce&lt;/a&gt; jobs.&lt;/p&gt;
&lt;h3 id=&quot;design-overview&quot;&gt;Design Overview &lt;a class=&quot;direct-link&quot; href=&quot;#design-overview&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A GFS Cluster is made up of a &lt;em&gt;single master&lt;/em&gt; and multiple &lt;em&gt;chunkservers&lt;/em&gt;, and is accessed by multiple clients, as shown in the figure below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/gfs-architecture.PNG&quot; alt=&quot;GFS Architecture&quot;&gt; &lt;/p&gt;
&lt;p&gt;Breakdown of the architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A client and a chunkserver may reside on the same machine, provided it has enough resources.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A file stored in GFS is broken into &lt;em&gt;chunks.&lt;/em&gt; A chunk is identified by an immutable and globally unique &lt;em&gt;chunk handle.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A chunk can be replicated across multiple chunkservers. It is configured to be replicated across three chunkservers by default.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The master keeps track of all the filesystem metadata. It knows how a file is split into chunks, and keeps track of what chunkservers hold each chunk.&lt;/p&gt;
&lt;p&gt;It is also responsible for garbage collection of orphaned chunks (when a file is deleted), and the migration of chunks between chunk servers for rebalancing load.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The master communicates with each chunkserver via &lt;em&gt;Heartbeat&lt;/em&gt; operations to pass instructions to it and collects its state.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There is a &lt;em&gt;GFS Client library&lt;/em&gt; that is linked into each application using GFS. The library handles communication with the master and chunkservers to read and write data on behalf of the application.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The chunkservers do not perform any form of caching but instead rely on the Linux buffer cache which keeps frequently accessed data in memory.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An interesting design choice made in this system is the decoupling of the data flow from the control flow. The GFS client only communicates with the master for metadata operations, but all data-bearing communications (reads and writes) go directly to the chunkservers. I&#39;ll explain how that works next.&lt;/p&gt;
&lt;h4 id=&quot;single-master&quot;&gt;Single Master &lt;a class=&quot;direct-link&quot; href=&quot;#single-master&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As noted earlier, there is a single master in a GFS cluster which clients only interact with to retrieve metadata. This section highlights the role of the master in decoupling the data flow from the control flow.&lt;/p&gt;
&lt;p&gt;To read the data for a file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The client first communicates with the master, sending it a request containing the file name and the &lt;em&gt;chunk index.&lt;/em&gt; The client derives the chunk index from a combination of the file name and the byte offset the application wants to read from.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;The master replies with the corresponding chunk handle and the location of its replicas.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;The client caches this information using the file name and chunk index as the key.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;The client then sends a request to one of the replicas specified by the client, usually the one closest to it, specifying the chunk handle and the byte range for the requested data.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;By caching the information from the master, further reads to the same chunk do not require any more client-master interactions until the cached information expires or the file is reopened.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;chunk-size&quot;&gt;Chunk Size &lt;a class=&quot;direct-link&quot; href=&quot;#chunk-size&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In typical Linux filesystems, a file is split into blocks and those blocks usually range from 0.5-65 kilobytes in size, with the default on most file systems being 4 kilobytes.&lt;/p&gt;
&lt;p&gt;A block size is the unit of work for the file system, which means reading or writing any files is done in multiples of that block size.&lt;/p&gt;
&lt;p&gt;In GFS, chunks are analogous to blocks, except that chunks are of a much larger size (64 MB). Having a large chunk size offers several advantages in this system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The client will not need to interact with the master as much, since reads and writes on the same chunk will require only one initial request to the master to get the chunk location information, and more data will fit on a single chunk.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;With large chunk sizes, a client is more likely to perform many operations on a given chunk, and so we can reduce network overhead by keeping a persistent TCP connection to the chunkserver over an extended period of time
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Third, it can reduce the size of the metadata stored on the master, since it&#39;s keeping track of fewer chunks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Google uses lazy space allocation to avoid wasting space due to internal fragmentation. Internal fragmentation means having unused portions of the 64 MB chunk. For example, if we allocate a 64 MB chunk and only fill up 10 MB, that&#39;s a lot of unused space.&lt;/p&gt;
&lt;p&gt;According to &lt;a href=&quot;https://stackoverflow.com/a/21738229/5430313&quot;&gt;this&lt;/a&gt; Stack Overflow answer,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lazy space allocation means that the physical allocation of space is delayed as long as possible, until data at the size of the chunk size is accumulated.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From the rest of that answer, I think what this means is that the decision to allocate a new chunk is based &lt;em&gt;solely&lt;/em&gt; on the data available, as opposed to using another &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6/#partitioning-of-key-value-data&quot;&gt;partitioning scheme&lt;/a&gt; to allocate data to chunks.&lt;/p&gt;
&lt;p&gt;This does not mean the chunks will always be filled up. A chunk which contains the file region for the end of a file will typically only be partially filled up.&lt;/p&gt;
&lt;h4 id=&quot;metadata&quot;&gt;Metadata &lt;a class=&quot;direct-link&quot; href=&quot;#metadata&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The master stores three types of metadata in memory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;File and chunk namespaces (i.e. directory hierarchy.)
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;The mapping from files to chunks.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;The location of each chunk&#39;s replica.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first two types listed are also persisted on the master&#39;s local disk. The third is not persisted; instead, the master asks each chunkserver about its chunks at master startup and when a chunkserver joins the cluster.&lt;/p&gt;
&lt;p&gt;By having the chunkserver as the ultimate source of truth of each chunk&#39;s location, GFS eliminates some of the challenges of keeping the master and chunkservers in sync regularly.&lt;/p&gt;
&lt;p&gt;The master keeps an operation log, where it stores the namespace and file-to-chunk mappings on local disk. It replicates this operation log on several machines, and GFS does not make changes to the metadata visible to clients until they have been persisted on all replicas.&lt;/p&gt;
&lt;p&gt;After startup, the master can restore its file system state by replaying the operation log. It keeps this log small to minimize the startup time by periodically checkpointing it.&lt;/p&gt;
&lt;h4 id=&quot;consistency-model&quot;&gt;Consistency Model &lt;a class=&quot;direct-link&quot; href=&quot;#consistency-model&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The consistency guarantee for GFS is relaxed. It does not guarantee that all the replicas of a chunk are &lt;em&gt;byte-wise identical.&lt;/em&gt; What it does guarantee is that every piece of data stored will be written &lt;em&gt;at least once&lt;/em&gt; on each replica. This means that a replica may contain duplicates, and it is up to the application to deal with such anomalies.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/gfs-consistency.PNG&quot; alt=&quot;File Region State After Mutation&quot;&gt; &lt;/p&gt;
&lt;p&gt;From Table 1 above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A file region is &lt;em&gt;consistent&lt;/em&gt; when all the clients will see the same data for it, regardless of which replica they read from.&lt;/li&gt;
&lt;li&gt;After a file data mutation, a region is &lt;em&gt;defined&lt;/em&gt; if it is consistent and all the clients will see the effect of the mutation in its entirety.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The data mutations here may be &lt;em&gt;writes&lt;/em&gt; or &lt;em&gt;record appends.&lt;/em&gt; A write occurs when data is written at a file offset specified by the application.&lt;/p&gt;
&lt;h5 id=&quot;record-appends&quot;&gt;Record Appends &lt;a class=&quot;direct-link&quot; href=&quot;#record-appends&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Record appends cause data to be written &lt;em&gt;atomically at least once&lt;/em&gt; even in the presence of concurrent mutations, but at an offset chosen by GFS.&lt;/p&gt;
&lt;p&gt;If a record append succeeds on some replicas and fails on others, those successful appends are not rolled back. This means that if the client retries the operation, the successful replicas may have duplicates for that record.&lt;/p&gt;
&lt;p&gt;Retrying the record append at a new file offset could mean that the offset chosen for the initial failed append operation is now blank in the file regions of the failed replicas; that is, if the region has not been modified before the retry. This blank region is known as a &lt;em&gt;padding&lt;/em&gt;, and the existence of padding and duplicates in replicas are what make them inconsistent.&lt;/p&gt;
&lt;p&gt;Applications that use GFS are left with the responsiblilty of dealing with these inconsistent file regions. These applications can include a unique ID with each record to filter out duplicates, and use &lt;a href=&quot;https://www.howtogeek.com/363735/what-is-a-checksum-and-why-should-you-care/&quot;&gt;checksums&lt;/a&gt; to detect and discard extra padding.&lt;/p&gt;
&lt;p&gt;There is also the possibility of a client reading from a stale replica. Each chunk replica is given a version number that gets increased for each successful mutation. If the chunkserver hosting a chunk replica is down during a mutation, the chunk replica will become stale and will have an older version number. Stale replicas are not given to clients when they ask the master for the location of a chunk, and they are not involved in mutations either.&lt;/p&gt;
&lt;p&gt;Despite this, because a client caches the location of a chunk, it may read from a stale replica before the information is refreshed. The impact of this is low because most operations to a chunk are append-only. This means that a stale replica usually returns a premature end of chunk, rather than outdated data for a value.&lt;/p&gt;
&lt;h3 id=&quot;system-interactions&quot;&gt;System Interactions &lt;a class=&quot;direct-link&quot; href=&quot;#system-interactions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This section describes in more detail how the client, master and chunkservers interact to implement data mutations and atomic record appends.&lt;/p&gt;
&lt;h4 id=&quot;writes&quot;&gt;Writes &lt;a class=&quot;direct-link&quot; href=&quot;#writes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When the master receives a modification operation for a particular chunk, the following happen:&lt;/p&gt;
&lt;p&gt;a) The master finds the chunkservers which hold that chunk and grants a &lt;em&gt;chunk lease&lt;/em&gt; to one of them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The server with the lease is called the &lt;em&gt;primary,&lt;/em&gt; while the others are &lt;em&gt;secondaries.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;The primary determines the order in which mutations are applied to all the replicas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;b) After the lease expires (typically after 60 seconds), the master is free to grant primary status to a different server for that chunk.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The master may sometimes try to revoke a lease before it expires (e.g. to prevent the mutation of a file when the file is being renamed. )&lt;/li&gt;
&lt;li&gt;The primary may also request an indefinite extension of the lease as long as the chunk is still being modified.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;c) The master may lose communication with a primary while the mutation is still happening. If this happens, it is fine for the master to grant a new lease to another replica as long as the lease timeout has expired.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at Figure 2 which illustrates the control flow of a write operation.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/gfs-write-control.PNG&quot; alt=&quot;Write Control and Data Flow&quot;&gt; &lt;/p&gt;
&lt;p&gt;The numbered steps below correspond to each number in the diagram.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The client asks the master for all chunkservers.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;The master grants a new lease to a replica (if none exist), increases the chunk version number, and tells all replicas to do the same after the mutation has been applied. It then replies to the client. After this, &lt;em&gt;the client no longer has to talk to the master.&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;The client pushes the data to all the chunkservers, not necessarily to the primary first. The servers will initially store this data in an internal LRU buffer cache until the data is used.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Once the client receives the acknowledgement that this data has been pushed successfully, it sends the write request to the primary chunkserver. The primary decides what serial order to apply the mutations in and applies them to the chunk.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;After applying the mutations, the primary forwards the write request and the serial number order to all the secondaries for them to apply in the same order.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;All secondaries reply to the primary once they have completed the operation.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;The primary replies to the client, indicating whether the operation was a success or an error. Note:
&lt;ul&gt;
&lt;li&gt;If the write succeeds at the primary but fails at any of the secondaries, we&#39;ll have an inconsistent state and an error is returned to the client.&lt;/li&gt;
&lt;li&gt;The client can retry steps 3 through 7.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;atomic-record-appends&quot;&gt;Atomic Record Appends &lt;a class=&quot;direct-link&quot; href=&quot;#atomic-record-appends&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The system interactions for record appends are largely the same as discussed for writes, with the following exceptions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In step 4, the primary first checks to see if appending the record to the current chunk would exceed the maximum size of 64MB. If so, the primary pads the chunk, notifies the secondaries to the same, and then tells the client to retry the request on the next chunk.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;If the record append fails on any of the replicas, the client must retry the operation. As discussed in the Consistency section, this means that replicas of the same chunk may contain duplicates.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;A record append is successful only when the data has been written &lt;em&gt;at the same offset&lt;/em&gt; on all the replicas of a chunk.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;fault-tolerance&quot;&gt;Fault Tolerance &lt;a class=&quot;direct-link&quot; href=&quot;#fault-tolerance&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Fault Tolerance is achieved in GFS by implementing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Fast Recovery -&lt;/em&gt; The master and the chunkservers are designed to restore their state and start in a matter of seconds.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Chunk Replication:&lt;/em&gt; Each chunk is replicated on multiple chunkservers on different racks. This ensures that some replicas are still available even if a rack is destroyed. The master is able to clone existing replicas as needed when chunkservers go offline or a replica is detected as stale or corrupted.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Master Replication:&lt;/em&gt; The master is also replicated for reliability. A state mutation is considered committed only when the operation log has been flushed to disk on all master replicas. When the master fails, it can restart almost immediately.&lt;/p&gt;
&lt;p&gt;In addition, there are &lt;em&gt;shadow masters&lt;/em&gt; which provide read-only access to the filesystem when the file is down. There may be a lag in replicating data from the primary master to its shadows, but these shadow masters help to improve availability.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;data-integrity&quot;&gt;Data Integrity &lt;a class=&quot;direct-link&quot; href=&quot;#data-integrity&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Checksumming is used by each chunkserver to detect the corruption of stored data.&lt;/p&gt;
&lt;p&gt;From the course website &lt;sup&gt;[1]&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A checksum algorithm takes a block of bytes as input and returns a single number that&#39;s a function of all the input bytes. For example, a simple checksum might be the sum of all the bytes in the input (mod some big number). GFS stores the checksum of each chunk as well as the chunk.&lt;/p&gt;
&lt;p&gt;When a chunkserver writes a chunk on its disk, it first computes the checksum of the new chunk, and saves the checksum on disk as well as the chunk. When a chunkserver reads a chunk from disk, it also reads the previously-saved checksum, re-computes a checksum from the chunk read from disk, and checks that the two checksums match.&lt;/p&gt;
&lt;p&gt;If the data was corrupted by the disk, the checksums won&#39;t match, and the chunkserver will know to return an error. Separately, some GFS applications stored their own checksums, over application-defined records, inside GFS files, to distinguish between correct records and padding. CRC32 is an example of a checksum algorithm.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;[1] &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/gfs-faq.txt&quot;&gt; GFS FAQ&lt;/a&gt; - Lecture Notes from MIT 6.824&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This week&#39;s material brought some interesting ideas in the design of a distributed storage system. These include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The decoupling of data flow from control flow.&lt;/li&gt;
&lt;li&gt;Using large chunk sizes to reduce overhead.&lt;/li&gt;
&lt;li&gt;The sequencing of writes through a primary replica.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, having a single master eventually became less than ideal for Google&#39;s use case. As the number of files stored increased by thousands, it became harder to fit all the metadata for those files on the master. In addition, the number of clients also increased, leading to too much CPU load on the master.&lt;/p&gt;
&lt;p&gt;Another challenge with GFS at Google was that the weak consistency model meant applications had to be designed to cope with those limitations. These limitations led to the creation of &lt;a href=&quot;https://www.systutorials.com/colossus-successor-to-google-file-system-gfs/&quot;&gt;Colossus&lt;/a&gt; as a successor to GFS.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-gfs.txt&quot;&gt;Lecture 3: GFS&lt;/a&gt; - MIT 6.824 Lecture Notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cs.stanford.edu/~matei/courses/2015/6.S897/slides/gfs.pdf&quot;&gt;The Google File System&lt;/a&gt; by Firas Abuzaid (Stanford Lecture Notes)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www0.cs.ucl.ac.uk/staff/B.Karp/gz03/f2014/lectures/gz03-lecture9-GFS.pdf&quot;&gt;GFS: The Google File System&lt;/a&gt; by Brad Karp (UCL Lecture Notes)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://queue.acm.org/detail.cfm?id=1594206&quot;&gt;GFS: Evolution on Fast-forward&lt;/a&gt; - 2009 Interview with a Google engineer about the origin and evolution of the Google File System.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 2 - RPC and Threads</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-2-rpc-and-threads/"/>
		<updated>2020-04-25T11:49:39-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-2-rpc-and-threads/</id>
		<content type="html">&lt;p&gt;This course is based on the &lt;a href=&quot;https://golang.org/&quot;&gt;Go programming language&lt;/a&gt;, and this post will introduce some features in Go that make it well suited for building concurrent and distributed applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#threads&quot;&gt;Threads&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#why-use-threads%3F&quot;&gt;Why use threads?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#what-if-we-can%27t-have-multiple-threads%3F&quot;&gt;What if we can&#39;t have multiple threads?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#downsides-of-event-driven-programming&quot;&gt;Downsides of Event-Driven Programming&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#threading-challenges&quot;&gt;Threading Challenges&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#remote-procedure-call-%28rpc%29&quot;&gt;Remote Procedure Call (RPC)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#dealing-with-failures&quot;&gt;Dealing with failures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rpc-semantics&quot;&gt;RPC Semantics&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#go-rpc&quot;&gt;Go RPC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;threads&quot;&gt;Threads &lt;a class=&quot;direct-link&quot; href=&quot;#threads&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Threads are the unit of execution on a processor. When a program is run on your computer, that starts up a &lt;em&gt;process.&lt;/em&gt; That process can then be made up of one or more threads which execute different tasks. Some key things to note about threads are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All the threads in a process share memory. They all have access to global variables.&lt;/li&gt;
&lt;li&gt;Each thread has keeps its own stack, program counter and registers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;why-use-threads%3F&quot;&gt;Why use threads? &lt;a class=&quot;direct-link&quot; href=&quot;#why-use-threads%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Threads enable &lt;em&gt;concurrency&lt;/em&gt;, which is important in distributed systems. Concurrency allows us to schedule multiple tasks on a single processor. These tasks are run in an interleaved manner and essentially share CPU time between themselves. For example: with I/O concurrency, instead of waiting for an I/O operation to complete before continuing execution (thereby rendering the CPU idle), threads allow us to perform other tasks while we wait.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Parallelism&lt;/em&gt;: We can perform multiple tasks in parallel on several cores. Unlike with just concurrency where only one task is making progress at a time (depending on which has its share of CPU time at that instant), parallelism allows multiple tasks to make process at the same time since they are executing on different CPU cores.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Convenience&lt;/em&gt;: Threads provide a convenient way to execute short-lived tasks in the background e.g. a master node continuously polling a worker to check if it&#39;s alive.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go has &lt;a href=&quot;https://tour.golang.org/concurrency/1&quot;&gt;&lt;em&gt;Goroutines&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; which are lightweight threads for managing concurrency.&lt;/p&gt;
&lt;h4 id=&quot;what-if-we-can&#39;t-have-multiple-threads%3F&quot;&gt;What if we can&#39;t have multiple threads? &lt;a class=&quot;direct-link&quot; href=&quot;#what-if-we-can&#39;t-have-multiple-threads%3F&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There&#39;s a concept of &lt;em&gt;Event Driven Programming&lt;/em&gt; where a process only has a single thread which listens for events and executes user specified functions when the event occurs. This concept is used by &lt;a href=&quot;https://nodesource.com/blog/understanding-the-nodejs-event-loop/&quot;&gt;Node.js&lt;/a&gt; and the thread is known as the &lt;em&gt;event loop.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The key thing here is that although the application appears to run on a single thread from the programmer&#39;s perspective, the runtime internally uses multiple threads to handle tasks. The main difference is that the programmer does not have to deal with these internal threads and the challenges of coordination between them. All the programmer has to do is specify &lt;em&gt;callback&lt;/em&gt; functions to be executed on the main thread when those background tasks have completed.&lt;/p&gt;
&lt;p&gt;When the single thread receives an event (like a button click or a task completion), it pauses its current task, executes the callback function for the event, and then returns to the paused job.&lt;/p&gt;
&lt;h5 id=&quot;downsides-of-event-driven-programming&quot;&gt;Downsides of Event-Driven Programming &lt;a class=&quot;direct-link&quot; href=&quot;#downsides-of-event-driven-programming&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;You will need additional coordination between processes to gain the benefits of parallelism on a multi-core system. With Node.js, you can fire up child processes to be run on each CPU, but you need to handle coordination between those processes.&lt;/li&gt;
&lt;li&gt;It&#39;s harder to implement this pattern (&lt;em&gt;Though this is subjective, of course).&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;threading-challenges&quot;&gt;Threading Challenges &lt;a class=&quot;direct-link&quot; href=&quot;#threading-challenges&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Deadlocks&lt;/em&gt;: These happen when two or more threads are waiting on each other in such a way that neither can progress.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Accessing shared data&lt;/em&gt;: What happens if two threads do n = n + 1 at the same time? Or one thread reads a value while another one increments it? This is known as a race condition. Using Go&#39;s &lt;a href=&quot;https://tour.golang.org/concurrency/9&quot;&gt;sync.Mutex &lt;/a&gt; to add locks around the shared data is one way to solve this problem. An alternative to that is to avoid sharing mutable data. Go has a built-in &lt;a href=&quot;https://golang.org/doc/articles/race_detector.html&quot;&gt;data race detector&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Coordination between threads&lt;/em&gt;: If one thread is producing data while another is consuming that data, it raises questions like &amp;quot;How can the consumer wait for data to be produced, and release the CPU while waiting?&amp;quot; and &amp;quot;How can the producer then wake up the consumer?&lt;em&gt;&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Go has &lt;a href=&quot;https://tour.golang.org/concurrency/2&quot;&gt;channels&lt;/a&gt; and &lt;a href=&quot;https://gobyexample.com/waitgroups&quot;&gt;WaitGroups&lt;/a&gt; for coordinating communication between threads.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;remote-procedure-call-(rpc)&quot;&gt;Remote Procedure Call (RPC) &lt;a class=&quot;direct-link&quot; href=&quot;#remote-procedure-call-(rpc)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RPC is a means of client/server communication between processes on the same machine or different machines. Here, the client executes a procedure (function/method) on a remote service as if it were a local procedure call.&lt;/p&gt;
&lt;p&gt;The steps that take place during an RPC are as follows &lt;sup&gt;[1]&lt;/sup&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A client invokes a &lt;em&gt;client&lt;/em&gt; &lt;em&gt;stub&lt;/em&gt; procedure, passing parameters in the usual way. The client stub resides within the client&#39;s own address space.&lt;/li&gt;
&lt;li&gt;The client stub &lt;em&gt;marshalls&lt;/em&gt; the parameters into a message. Marshalling includes converting the representation of the parameters into a standard format, and copying each parameter into the message.&lt;/li&gt;
&lt;li&gt;The client stub passes the message to the transport layer, which sends it to the remote server machine.&lt;/li&gt;
&lt;li&gt;On the server, the transport layer passes the message to a &lt;em&gt;server&lt;/em&gt; &lt;em&gt;stub&lt;/em&gt;, which &lt;em&gt;demarshalls&lt;/em&gt; the parameters and calls the desired server routine using the regular procedure call mechanism.&lt;/li&gt;
&lt;li&gt;When the server procedure completes, it returns to the server stub (e.g., via a normal procedure call return), which marshalls the return values into a message. The server stub then hands the message to the transport layer.&lt;/li&gt;
&lt;li&gt;The transport layer sends the result message back to the client transport layer, which hands the message back to the client stub.&lt;/li&gt;
&lt;li&gt;The client stub demarshalls the return parameters and execution returns to the caller.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main benefit of this is that it simplifies the process of writing distributed applications since RPC hides all the network code into stub functions. Programmers don&#39;t have to worry about details like data conversion and parsing, and opening and closing a connection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The client knows what server to talk to through &lt;em&gt;binding.&lt;/em&gt; Go has an RPC library to ease this communication between processes. In Go&#39;s RPC library, the server name and port are passed as arguments to a method when setting up the connection.&lt;/p&gt;
&lt;h4 id=&quot;dealing-with-failures&quot;&gt;Dealing with failures &lt;a class=&quot;direct-link&quot; href=&quot;#dealing-with-failures&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;From the perspective of a client, failure means sending a request to the server and not getting a response back within a particular time out. This can be caused by a number of things including lost packets, slow server, crashed server, and a broken network.&lt;/p&gt;
&lt;p&gt;Dealing with this is tricky because the client would not know the actual status of its request. Possible scenarios are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The server never saw the request.&lt;/li&gt;
&lt;li&gt;The server executed the request but crashed just before sending a reply.&lt;/li&gt;
&lt;li&gt;The server executed a request and sent the reply, but the network died before delivering the reply.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The simplest way to deal with a failure would be to just retransmit the request; however, if the server had already executed the request, resending it could mean the server executes the same request twice, which could lead to unwanted side effects. This failure handling method works well for &lt;em&gt;idempotent requests&lt;/em&gt; i.e. operations that have the same effect when executed multiple times as if they were executed once. Many operations are not idempotent, and so we need a more general approach to handle failures.&lt;/p&gt;
&lt;h4 id=&quot;rpc-semantics&quot;&gt;RPC Semantics &lt;a class=&quot;direct-link&quot; href=&quot;#rpc-semantics&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An RPC implementation can use any of the following semantics for making requests :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;At-Most-Once:&lt;/strong&gt; At-most-once semantic ensures that a request will not be retried automatically by the client. In this case, resending a request is opt-in for the client. Therefore, without an explicit retry mechanism for a failed request, a request may be lost and never executed. If the request is retried, the server is responsible for detecting duplicate requests and ensuring that only one succeeds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;At-Least-Once:&lt;/strong&gt; Here, a request may be executed one or more times and may not be lost. The client will keep retrying the request until it receives a positive acknowledgement that the request has been executed. This is appropriate for requests with no side effects (like read-only requests) and idempotent operations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exactly-Once:&lt;/strong&gt; In this mode, requests can neither be duplicated nor lost. This is harder to achieve and the least fault tolerant because it requires that a response &lt;em&gt;must&lt;/em&gt; be received from the server, and there can be no duplicates. If we have multiple servers and the one handling the initial request crashes, the other servers may not be able to tell whether the request was executed or not by the initial server, and it becomes a challenge agreeing on a decision for that.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;go-rpc&quot;&gt;Go RPC &lt;a class=&quot;direct-link&quot; href=&quot;#go-rpc&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Go RPC guarantees at-most-once semantic. If it doesn&#39;t get a reply, it will just return an error. The client can opt to retry a failed request, but it is up to the server to handle duplicate requests to maintain the at-most-once guarantee; what if the request actually executed but the reply got lost?&lt;/p&gt;
&lt;p&gt;Some complexities related to at-most-once communication between processes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;How do we guarantee that the ID of a request is unique between multiple clients?&lt;/em&gt; One way to do this is by generating a request ID which combines the unique client ID with a sequence number.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;For detecting duplicates, how long should each request ID be kept for?&lt;/em&gt; We cannot keep all the request IDs indefinitely, so they have to get discarded at a point. A method for handling this could be for each client to include an extra identifier with each request. Let&#39;s call it X. The extra identifier will tell the server that it is safe to delete all request IDs that came before X.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;How do we handle duplicate requests while the original request is still executing?&lt;/em&gt; We could have a &amp;quot;pending&amp;quot; flag next to each executing RPC and wait for it to complete, or simply ignore the new request.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;What if the server crashes and restarts with the duplicate info being kept in memory?&lt;/em&gt; The server could write duplicate info to disk. The server could also replicate information about duplicates across multiple machines.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;[1] &lt;a href=&quot;https://web.cs.wpi.edu/~cs4514/b98/week8-rpc/week8-rpc.html&quot;&gt;Remote Procedure Call (RPC)&lt;/a&gt; - Lecture notes from Worchester Polytechnic Institute&lt;/p&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdos.csail.mit.edu/6.824/notes/l-rpc.txt&quot;&gt;Lecture 2: Infrastructure - RPC and Thread&lt;/a&gt; - MIT 6.824 Lecture Notes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://people.cs.rutgers.edu/~pxk/417/notes/rpc.html&quot;&gt;Remote Procedure Calls&lt;/a&gt; by Paul Krzyzanowski&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>MIT 6.824: Lecture 1 - MapReduce</title>
		<link href="https://timilearning.com/posts/mit-6.824/lecture-1-mapreduce/"/>
		<updated>2020-04-13T15:17:18-00:00</updated>
		<id>https://timilearning.com/posts/mit-6.824/lecture-1-mapreduce/</id>
		<content type="html">&lt;h4 id=&quot;background&quot;&gt;Background &lt;a class=&quot;direct-link&quot; href=&quot;#background&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I started a study group with some of my friends where we&#39;ll be going through &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/schedule.html&quot;&gt;this &lt;/a&gt;course. Over the next couple of weeks, I intend to upload my notes from studying each week&#39;s material.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;mapreduce&quot;&gt;MapReduce &lt;a class=&quot;direct-link&quot; href=&quot;#mapreduce&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This week&#39;s material focused on the MapReduce paradigm for data processing. The material included the &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/papers/mapreduce.pdf&quot;&gt;seminal MapReduce paper&lt;/a&gt; by Jeff Dean and Sanjay Ghemawat, and an accompanying video lecture. Below are my notes from the materials and group discussion.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;MapReduce is a system for parallelizing the computation of a large volume of data across multiple machines in a cluster. It achieves this by exposing a simple API for expressing these computations using two operations: Map and Reduce.&lt;/p&gt;
&lt;p&gt;The Map task takes an input file and outputs a set of intermediate (key, value) pairs. The intermediate values with the same key are then grouped together and processed in the Reduce task for each distinct key.&lt;/p&gt;
&lt;p&gt;Some examples of programs that can be expressed as MapReduce computations are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Word Count in Documents:&lt;/strong&gt; Here, the map function can emit a (key, value) pair for each occurrence of a word like (word, count). The reduce function can then add all the counts for the same word and emit a (word, total count) pair.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distributed Grep:&lt;/strong&gt; Grep is a regular expression search for a given pattern in a text document. To search across a large volume of documents, we could define a map function which emits a line if it matches the supplied pattern like (pattern, line). The reduce function then outputs all the lines from the intermediate values for the given key.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distributed Sort:&lt;/strong&gt; We can have a map function which extracts the key from each record and emits a (key, record) pair. Depending on the partitioning and ordering scheme, we can then have a reduce function that emits all the pairs unchanged. We&#39;ll go into more detail on the ordering scheme later on.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;implementation-details&quot;&gt;Implementation Details &lt;a class=&quot;direct-link&quot; href=&quot;#implementation-details&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The MapReduce interface can be implemented in many ways, so this section just details the implementation specific to Google at the time of writing this paper.&lt;/p&gt;
&lt;p&gt;The Map function invocations are distributed across multiple machines by automatically partitioning the input data into a set of M splits. The Reduce invocations are split into R pieces based on a partitioning function defined on the intermediate key.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/mapreduce.png&quot; alt=&quot;A sample MapReduce Job&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; A sample MapReduce Job &lt;/p&gt;
&lt;p&gt;The flow of execution when the MapReduce function is called by a user is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The MapReduce library splits the input into M files and starts up multiple instances of the program on many machines in the cluster.&lt;/li&gt;
&lt;li&gt;One of the instances is the Master and the others are Workers. The master assigns map tasks to the available workers.&lt;/li&gt;
&lt;li&gt;There are M map tasks and R reduce tasks to be assigned to workers.&lt;/li&gt;
&lt;li&gt;When a worker is assigned a map task, it reads the input from its corresponding split, performs the specified operations, and then emits intermediate (key, value) pairs which are buffered in memory.&lt;/li&gt;
&lt;li&gt;These buffered pairs are periodically written to local disk, partitioned into R regions according to the&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6/#partitioning-of-key-value-data&quot;&gt; partitioning scheme.&lt;/a&gt; The locations of these buffered pairs on local disk are passed to the master, which then forwards these locations to the reduce workers.&lt;/li&gt;
&lt;li&gt;A reduce worker uses &lt;a href=&quot;https://en.wikipedia.org/wiki/Remote_procedure_call&quot;&gt;remote procedure calls&lt;/a&gt; to read the buffered data from local disk based on the location it was forwarded by master. When all intermediate data has been read, it sorts the values according to the key and groups all occurrences for the same key together.&lt;/li&gt;
&lt;li&gt;For each distinct intermediate key, the reduce worker passes its grouped values to the reduce function defined. The output of this reduce function is appended to a final output file for the partition.&lt;/li&gt;
&lt;li&gt;The master wakes up the user program and releases control when all the map and reduce tasks have been completed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The output of a MapReduce job is a set of R files (one per reduce task).&lt;/p&gt;
&lt;h3 id=&quot;dealing-with-faults&quot;&gt;Dealing with Faults &lt;a class=&quot;direct-link&quot; href=&quot;#dealing-with-faults&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Worker Failure:&lt;/strong&gt; The master pings all its workers periodically. If no response is received from a worker after a set period of time, the worker is marked as failed. The tasks assigned to the failed worker are then reassigned to other workers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Master Failure:&lt;/strong&gt; As master failures were rare, their implementation simply aborted the whole execution if master failed, for the execution to be retried from the start. An alternative implementation could have made the master periodically checkpoint its state, so that a retry could pick up from where the execution left off.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;dealing-with-network-resource-scarcity&quot;&gt;Dealing with Network Resource Scarcity &lt;a class=&quot;direct-link&quot; href=&quot;#dealing-with-network-resource-scarcity&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Their implementation at the time of writing the paper used locality as a means of conserving network bandwidth. This means that the input files were kept close to where they will be processed to avoid the network trip of transferring these large files. The Master&#39;s scheduling algorithm took the file location into account when determining what workers should execute what input files.&lt;/p&gt;
&lt;p&gt;Note, the lecture video for the week explained that as Google&#39;s networking infrastructure was expanded and upgraded in later years, they relied less on this locality optimization.&lt;/p&gt;
&lt;h3 id=&quot;dealing-with-stragglers&quot;&gt;Dealing with Stragglers &lt;a class=&quot;direct-link&quot; href=&quot;#dealing-with-stragglers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Stragglers are machines that take longer time than usual to complete one of the last few map or reduce tasks. They addressed this by having the master schedule &lt;em&gt;backup tasks&lt;/em&gt; when the computation is almost completed. A task is then marked as completed when either the primary or backup execution completes.&lt;/p&gt;
&lt;h3 id=&quot;some-other-interesting-features&quot;&gt;Some Other Interesting Features &lt;a class=&quot;direct-link&quot; href=&quot;#some-other-interesting-features&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Combiner Function:&lt;/strong&gt; A user can specify a combiner function, which groups all the values for a key on a machine that performs a map task. The combiner function typically has the same code as the reduce function. The advantage of enabling this is that it can reduce the volume of the data being sent over the network to a reduce worker. &lt;em&gt;I wonder why this isn&#39;t enabled by default?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skipping Bad Records:&lt;/strong&gt; Instead of the entire execution failing because of few bad records, there&#39;s a mechanism in place for bad records to be skipped. This is acceptable in some instances e.g. when doing statistical analysis on a large dataset.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ordering Guarantees:&lt;/strong&gt; The guarantee is that within a given reduce partition, the intermediate key value pairs are processed in increasing order of the keys. I was curious about where/how the sorting happens and it looks like in Hadoop MapReduce, data from the mappers are first sorted according to key in the mapper worker using Quicksort. This sorted data is then &lt;em&gt;shuffled&lt;/em&gt; to the reduce workers. The reduce worker merges the sorted data from different mappers into one sorted set, similar to the merge routine in Mergesort.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Though no longer in use at Google for a number of &lt;a href=&quot;https://www.the-paper-trail.org/post/2014-06-25-the-elephant-was-a-trojan-horse-on-the-death-of-map-reduce-at-google/&quot;&gt;reasons&lt;/a&gt;, MapReduce fundamentally changed the way large-scale data processing architectures are built. It abstracted the complexity of dealing with parallelism, fault-tolerance and load balancing by exposing a simple API that allowed programmers without experience with these systems to distribute the processing of large datasets across a cluster of computers.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Consistency Models</title>
		<link href="https://timilearning.com/posts/consistency-models/"/>
		<updated>2020-04-04T20:59:17-00:00</updated>
		<id>https://timilearning.com/posts/consistency-models/</id>
		<content type="html">&lt;h3 id=&quot;background&quot;&gt;Background &lt;a class=&quot;direct-link&quot; href=&quot;#background&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I was reading the documentation for &lt;a href=&quot;https://cloud.google.com/spanner/docs/true-time-external-consistency#does_cloud_spanner_provide_linearizability&quot;&gt;Google&#39;s Cloud Spanner&lt;/a&gt; recently, and came across the claim that the consistency level guaranteed by the database is &lt;em&gt;External Consistency.&lt;/em&gt; The documentation then went on to state that External Consistency is a stronger guarantee than &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-1/#linearizability&quot;&gt;Linearizability&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I found this confusing because I had come across resources that suggested that these terms referred to the same thing. I brought my confusion about these terms to a colleague, who kindly pointed me to one of the earliest sources of these terms to help clarify it.&lt;/p&gt;
&lt;p&gt;This post contains my notes from studying section 3.1 of &lt;a href=&quot;https://usermanual.wiki/Document/CSL818InformationStorageinaDecentralizedComputerSystem.4233578342&quot;&gt;&#39;Information Storage in a Decentralized Computer System&#39;&lt;/a&gt; by David K Gifford.&lt;/p&gt;
&lt;p&gt;Note that only a small subset of consistency models for concurrent systems are covered here. For a more detailed treatment of this topic, you&#39;ll find &lt;a href=&quot;https://jepsen.io/consistency&quot;&gt;this&lt;/a&gt; article useful.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;serial-consistency&quot;&gt;Serial Consistency &lt;a class=&quot;direct-link&quot; href=&quot;#serial-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Serial Consistency is achieved when a database makes it appear to a transaction that there is no other concurrently executing transaction.&lt;/p&gt;
&lt;p&gt;So if we have two transactions:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/t1-t2.PNG&quot; alt=&quot;Sample Transactions T1 and T2 where T1 has steps a11, a12, a13 and T2 has a21, a22 and a23&quot;&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; Sample Transactions T&lt;sub&gt;1&lt;/sub&gt; and T&lt;sub&gt;2&lt;/sub&gt; &lt;/p&gt;
&lt;p&gt;The order in which the actions of each transaction is processed is called a &lt;strong&gt;&lt;em&gt;schedule&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;serial schedule&lt;/em&gt; results when the transactions are executed one at a time to completion. We have 2 serial schedules for the example above:&lt;/p&gt;
&lt;p&gt;Sa: {a11, a12, a13, a21, a22, a23}&lt;/p&gt;
&lt;p&gt;Sb: {a21, a22, a23, a11, a12, a13}&lt;/p&gt;
&lt;p&gt;A database provides serial consistency if it guarantees that the schedule to process a set of transactions is one of the possible serial schedules.&lt;/p&gt;
&lt;h3 id=&quot;external-consistency-and-linearizability&quot;&gt;External Consistency and Linearizability &lt;a class=&quot;direct-link&quot; href=&quot;#external-consistency-and-linearizability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An &lt;em&gt;external schedule&lt;/em&gt; is the &lt;em&gt;unique&lt;/em&gt; serial schedule that is defined by the actual time order in which transactions complete. Any system which guarantees that the schedule it will use to process a set of transactions is equivalent to its external schedule, is said to provide &lt;strong&gt;&lt;em&gt;external consistency&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Using the example above, if T&lt;sub&gt;1&lt;/sub&gt; completes before T&lt;sub&gt;2&lt;/sub&gt; starts to commit, then external consistency guarantees that the system will appear as if schedule Sa was used.  This means that no clients observing the database will see a state that contains the effects of T&lt;sub&gt;2&lt;/sub&gt; but not T&lt;sub&gt;1&lt;/sub&gt;.&lt;/p&gt;
&lt;p&gt;Remember that these consistency models should be thought of in the context of a distributed database, meaning that these transactions can execute on different nodes, and clients can observe the database from different nodes as well.&lt;/p&gt;
&lt;p&gt;What makes external consistency stronger than serializability is that in serializability, any of the possible serial schedules can be used, regardless of when each transaction actually committed.&lt;/p&gt;
&lt;p&gt;Using the same example of T&lt;sub&gt;1&lt;/sub&gt; completing before T&lt;sub&gt;2&lt;/sub&gt; commits: in a serializable database, it is possible for a client to read from a database node in a state where it contains the effect of T&lt;sub&gt;2&lt;/sub&gt;, but not T&lt;sub&gt;1&lt;/sub&gt;. This can lead to inconsistencies in the data.&lt;/p&gt;
&lt;p&gt;With external consistency we&#39;re saying that when a transaction has committed, all subsequent transactions will see the effect of that committed transaction in the DB, that&#39;s not the same guarantee provided by serializability.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-1/#linearizability&quot;&gt;Linearizability&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; on the other hand, does not say anything about the behaviour of transactions. It is a consistency level that deals more with the behaviour of a single object. It is a recency guarantee for a single object. while it might involve a transaction, its focus is on a recency guarantee for a single object, i.e. when one committed transaction has updated the value of an object, all other reads to that object must see the new value.&lt;/p&gt;
&lt;p&gt;In summary, while linearizability is related to external consistency in some way, they&#39;re talking about different things.&lt;/p&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some other useful sources to explore these topics further include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.bailis.org/blog/linearizability-versus-serializability/&quot;&gt;Linearizability vs Serializability&lt;/a&gt; by Peter Bailis&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irenezhang.net/blog/2015/02/01/consistency.html&quot;&gt;Consistency should be more consistent!&lt;/a&gt; by Irene Zhang&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jepsen.io/consistency&quot;&gt;Consistency Models&lt;/a&gt; - Jepsen blog&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/spanner/docs/true-time-external-consistency#does_cloud_spanner_provide_linearizability&quot;&gt;Cloud Spanner: Truetime and External Consistency&lt;/a&gt; -  Cloud Spanner Documentation&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 9 - Consistency and Consensus (Part Two)</title>
		<link href="https://timilearning.com/posts/ddia/part-two/chapter-9-2/"/>
		<updated>2020-03-23T15:11:57-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-two/chapter-9-2/</id>
		<content type="html">&lt;p&gt;In the &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-1/&quot;&gt;first part&lt;/a&gt; of Chapter 9, we looked at Linearizability and Causality as consistency guarantees and used those topics to discuss the difference between total order and partial order. We also briefly discussed Lamport timestamps and how they are used to enforce a total ordering of operations across multiple nodes.&lt;/p&gt;
&lt;p&gt;We concluded by seeing that it&#39;s not enough to know the total order of operations &lt;em&gt;after&lt;/em&gt; all the operations have been collected. A node might want to make a decision &amp;quot;in the moment&amp;quot;, and so it needs to know what the order is at each point in time.&lt;/p&gt;
&lt;p&gt;This part will focus on &amp;quot;Total Order Broadcast&amp;quot; and &amp;quot;Consensus&amp;quot;, which help to solve the challenge described above.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#total-order-broadcast&quot;&gt;Total Order Broadcast&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#using-total-order-broadcast&quot;&gt;Using total order broadcast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementing-linearizable-storage-using-total-order-broadcast&quot;&gt;Implementing linearizable storage using total order broadcast&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#linearizable-writes-using-total-order-broadcast&quot;&gt;Linearizable writes using total order broadcast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linearizable-reads-using-total-order-broadcast&quot;&gt;Linearizable reads using total order broadcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementing-total-order-broadcast-using-linearizable-storage&quot;&gt;Implementing total order broadcast using linearizable storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#distributed-transactions-and-consensus&quot;&gt;Distributed Transactions and Consensus&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#atomic-commit-and-two-phase-commit-%282pc%29&quot;&gt;Atomic Commit and Two-Phase Commit (2PC)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#from-single-node-to-distributed-atomic-commit&quot;&gt;From single-node to distributed atomic commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#introduction-to-two-phase-commit&quot;&gt;Introduction to Two-phase commit&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#coordinator-failure&quot;&gt;Coordinator Failure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#three-phase-commit&quot;&gt;Three-phase commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#distributed-transactions-in-practice&quot;&gt;Distributed Transactions in Practice&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#exactly-once-message-processing&quot;&gt;Exactly-once message processing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#xa-transactions&quot;&gt;XA Transactions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#holding-locks-while-in-doubt&quot;&gt;Holding locks while in doubt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#limitations-of-distributed-transactions&quot;&gt;Limitations of distributed transactions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#fault-tolerant-consensus&quot;&gt;Fault-Tolerant Consensus&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#consensus-algorithms-and-total-order-broadcast&quot;&gt;Consensus algorithms and total order broadcast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#single-leader-replication-and-consensus&quot;&gt;Single-leader replication and consensus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#epoch-numbering-and-quorums&quot;&gt;Epoch numbering and quorums&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#limitations-of-consensus&quot;&gt;Limitations of consensus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#membership-and-coordination-services&quot;&gt;Membership and Coordination Services&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#allocating-work-to-nodes&quot;&gt;Allocating work to nodes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#service-discovery&quot;&gt;Service discovery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;total-order-broadcast&quot;&gt;Total Order Broadcast &lt;a class=&quot;direct-link&quot; href=&quot;#total-order-broadcast&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We discussed earlier how single-leader replication determines a total order of operations by sequencing all operations on a single CPU core on the leader. However, when the throughput needed is greater than a single leader can handle, there&#39;s a challenge of how to scale the system with multiple nodes, as well as handling failover if the leader fails.&lt;/p&gt;
&lt;p&gt;Note that single-leader replication systems often only maintain ordering per partition. They do not have total ordering across all partitions. An example of such a system is Kafka. Total ordering across all partitions will require additional coordination.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Total Order Broadcast&lt;/em&gt; (or Atomic Broadcast) is a broadcast a protocol for exchanging messages between nodes. It requires that the following safety properties are always satisfied:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Reliable delivery&lt;/em&gt;: No messages are lost. A message delivered to one node must be delivered to all the nodes.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Totally ordered delivery:&lt;/em&gt; Messages are delivered to every node in the same order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From Wikipedia: &lt;em&gt;The broadcast is termed &amp;quot;atomic&amp;quot; because it either eventually completes correctly at all participants, or all participants abort without side effects.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;An algorithm for total order broadcast must ensure that these properties are always satisfied, even in the face of network or node faults. In the face of failures, the algorithm must keep retrying so that messages can get through when the network is repaired.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Q&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;: How can messages be delivered in the same order in multi-leader or leaderless replication systems?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;A:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;In leaderless replication systems, a client typically directly sends its writes to several replicas and then uses a quorum to determine if it&#39;s successful or not. Leaderless replication does not enforce a particular order of writes. Multi-leader systems are typically not linearizable, so it doesn&#39;t apply here.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;using-total-order-broadcast&quot;&gt;Using total order broadcast &lt;a class=&quot;direct-link&quot; href=&quot;#using-total-order-broadcast&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Total order broadcast is exactly what is needed for database replication based on a principle known as &lt;em&gt;state machine replication.&lt;/em&gt; It&#39;s stated as follows:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;If every message represents a write to the database, and every replica processes the same writes in the same order, then the replicas will remain consistent with each other (aside from any temporary replication lag).&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 8950-8951). O&#39;Reilly Media. Kindle Edition.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It can also be used to implement serializable transactions:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;”If every message represents a deterministic transaction to be executed as a stored procedure, and if every node processes those messages in the same order, then the partitions and replicas of the database are kept consistent with each other&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 8957-8958). O&#39;Reilly Media. Kindle Edition.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One thing to note with total order broadcast is that &lt;em&gt;the order is fixed at the time of message delivery&lt;/em&gt;. This means that a node cannot retroactively insert a message into an earlier position if subsequent messages have been delivered. Messages must be delivered in the right order. This makes total order broadcast stronger than timestamp ordering (&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#time-of-day-clocks&quot;&gt;since we know that time can move backward&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Total Order broadcast can also be seen as a way of creating a &lt;em&gt;log&lt;/em&gt; (like a replication log, transaction log, or write-ahead log). Delivering a message is like appending to the log, and if all the nodes read from the log, they will see the same sequence of messages.&lt;/p&gt;
&lt;p&gt;Another use of total order broadcast is for implementing &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#fencing-tokens&quot;&gt;fencing tokens&lt;/a&gt;&lt;em&gt;.&lt;/em&gt; Each request to acquire the lock can be appended as a message to the log, and the messages can be given a sequence number in the order of their appearance in the log. This sequence number can then be used as a fencing token due to the fact that it&#39;s monotonically increasing.&lt;/p&gt;
&lt;h3 id=&quot;implementing-linearizable-storage-using-total-order-broadcast&quot;&gt;Implementing linearizable storage using total order broadcast &lt;a class=&quot;direct-link&quot; href=&quot;#implementing-linearizable-storage-using-total-order-broadcast&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although total order broadcast is a reasonably strong guarantee, it is not quite as strong as linearizability. However, they are closely related.&lt;/p&gt;
&lt;p&gt;Total Order Broadcast guarantees that messages will be delivered reliably in the same order, but it provides no guarantee about &lt;em&gt;when&lt;/em&gt; the message will be delivered (so a read to a node may return stale data). Linearizability, on the other hand, is a &lt;em&gt;recency guarantee&lt;/em&gt;, it guarantees that a read will see the latest value written.&lt;/p&gt;
&lt;p&gt;These two concepts are closely related, and so we can implement linearizable storage using total order broadcast and vice versa.&lt;/p&gt;
&lt;h4 id=&quot;linearizable-writes-using-total-order-broadcast&quot;&gt;Linearizable writes using total order broadcast &lt;a class=&quot;direct-link&quot; href=&quot;#linearizable-writes-using-total-order-broadcast&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Linearizable writes are instantaneous writes, meaning that once a client has written a value, all other reads to that value must see the newly written value or the value of a later write.&lt;/p&gt;
&lt;p&gt;Imagine that we are building a system where each user has a unique username, and we want to deal with a situation where multiple users concurrently try to grab the same username. (&lt;em&gt;Aside:&lt;/em&gt; Now, the scenario I have in my head is one in which these users can write to different replicas (think multiple leaders) concurrently. I imagine that in a single leader system, we would simply use the first successful operation).&lt;/p&gt;
&lt;p&gt;To ensure linearizable writes in this system using total order broadcast:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each node can append a message to the log indicating the username they want to claim.&lt;/li&gt;
&lt;li&gt;The nodes then read the log, waiting for the message they appended to be delivered back to them.&lt;/li&gt;
&lt;li&gt;If the first message that a node receives with the username it wants is its own message, then the node is successful and can commit the username claim. All other nodes that want to claim this username can then abort their operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This works because if there are several concurrent writes, all the nodes will agree on which came first, and these messages are delivered to all the nodes in the same order.&lt;/p&gt;
&lt;p&gt;This algorithm does not guarantee linearizable reads though. A client can still get stale reads if they read from an asynchronously updated store.&lt;/p&gt;
&lt;h4 id=&quot;linearizable-reads-using-total-order-broadcast&quot;&gt;Linearizable reads using total order broadcast &lt;a class=&quot;direct-link&quot; href=&quot;#linearizable-reads-using-total-order-broadcast&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There are a number of options for linearizable reads with total order broadcast:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you want to read a value, you could append a message to the log, read the log and then perform the actual read of the value when the message you appended is delivered back to you. I think this works because since all nodes have to agree on the order of messages, you will always see the latest write that happened before your &#39;read message&#39;. If client B sends a &#39;write message&#39; to the log before client A&#39;s &#39;read message&#39; to the same log, client A will see the effects of that &#39;write message&#39;.&lt;/li&gt;
&lt;li&gt;If you can fetch the position of the latest log message in a linearizable way, you can query that position, wait for all the entries up to that position to be delivered to you, and then actually perform the read. This is the idea used in Zookeeper&#39;s sync() operation.&lt;/li&gt;
&lt;li&gt;You can always make reads from a replica that is synchronously updated on writes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;implementing-total-order-broadcast-using-linearizable-storage&quot;&gt;Implementing total order broadcast using linearizable storage &lt;a class=&quot;direct-link&quot; href=&quot;#implementing-total-order-broadcast-using-linearizable-storage&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What we&#39;re essentially implementing is a mechanism for generating sequence numbers for each message we want to send. The easiest way to do this is to assume that we have a linearizable register which stores an integer and has an atomic increment-and-get operation.&lt;/p&gt;
&lt;p&gt;The algorithm is this:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;For every message you want to send through total order broadcast, you increment-and-get the linearizable integer, and then attach the value you got from the register as a sequence number to the message.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 9023-9024). O&#39;Reilly Media. Kindle Edition.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This way, we&#39;ll avoid race conditions on the integer and each message will have a unique sequence number.&lt;/p&gt;
&lt;p&gt;Something to note is that unlike Lamport timestamps, the numbers gotten from incrementing the linearizable register form a sequence that has no gaps. The sequence won&#39;t jump from 4 to 6. Therefore, if a node has delivered a message with a sequence number of 4 and receives an incoming message with a sequence number of 6, it must wait for message 5 before it can deliver message 6 (because messages must be delivered in the same order to all nodes, and if it delivers 6 before 5 to other nodes, it is likely breaking some order).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It can be proved that a linearizable compare-and-set (or increment-and-get) register and total order broadcast are both equivalents to consensus, meaning that if you can solve one of those problems, we can transform it into a solution for the others.&lt;/p&gt;
&lt;p&gt;We&#39;ll discuss the consensus problem next.&lt;/p&gt;
&lt;h2 id=&quot;distributed-transactions-and-consensus&quot;&gt;Distributed Transactions and Consensus &lt;a class=&quot;direct-link&quot; href=&quot;#distributed-transactions-and-consensus&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Simply put, consensus means getting &lt;em&gt;several nodes to agree on something.&lt;/em&gt; However, it turns out that this is not an easy problem to solve, and it is one of the fundamental problems in distributed computing.&lt;/p&gt;
&lt;p&gt;Some situations where it is important for the nodes to agree include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Leader election:&lt;/em&gt; In a single-leader setup, all the nodes need to agree on which node is the leader. If the nodes don&#39;t agree on who the leader is, it could lead to a split-brain situation in which multiple &#39;leaders&#39; could accept writes, leading to inconsistency and data loss.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Atomic commit:&lt;/em&gt; If a transaction spans several nodes or partitions, there&#39;s a chance that it may fail on some nodes and succeed on others. However, to preserve the atomicity property of ACID transactions, it must either succeed or fail on &lt;em&gt;all&lt;/em&gt; of them. We have to get all the nodes to &lt;em&gt;agree&lt;/em&gt; on the outcome of the transaction. This is known as the &lt;em&gt;atomic commit&lt;/em&gt; problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&#39;ll address the atomic commit problem first before delving into other consensus scenarios.&lt;/p&gt;
&lt;h3 id=&quot;atomic-commit-and-two-phase-commit-(2pc)&quot;&gt;Atomic Commit and Two-Phase Commit (2PC) &lt;a class=&quot;direct-link&quot; href=&quot;#atomic-commit-and-two-phase-commit-(2pc)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Two-phase commit is the most commonly used algorithm for implementing atomic commit. It is a kind of consensus algorithm, but not a very good one and we&#39;ll learn why soon.&lt;/p&gt;
&lt;p&gt;We learned in &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/&quot;&gt;Chapter 7&lt;/a&gt; that the purpose of transaction atomicity is to prevent the database from getting in an &lt;em&gt;inconsistent state&lt;/em&gt; in the event of failure.&lt;/p&gt;
&lt;h4 id=&quot;from-single-node-to-distributed-atomic-commit&quot;&gt;From single-node to distributed atomic commit &lt;a class=&quot;direct-link&quot; href=&quot;#from-single-node-to-distributed-atomic-commit&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Atomicity for single database node transactions is usually implemented by the storage engine. When a request is made to commit a transaction, the writes in the transaction are made durable (typically using a &lt;a href=&quot;https://timilearning.com/posts/data-storage-on-disk/part-one/#write-ahead-logs&quot;&gt;write-ahead log&lt;/a&gt;&lt;em&gt;)&lt;/em&gt; and then a commit record is appended on disk. If the database crashes during this process, upon restarting, it decides whether to commit or rollback the transaction based on whether or not the commit record was written to the disk before the crash.&lt;/p&gt;
&lt;p&gt;However, if multiple nodes are involved, it&#39;s not sufficient to simply send a commit request to all the nodes and then commit the transaction on each one. Some of the scenarios where multiple nodes could be involved are: a multi-object transaction in a partitioned database, or writing to a &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6/#partitioning-secondary-indexes-by-term&quot;&gt;term-partitioned index&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It&#39;s possible that the commit succeeds on some nodes and fails on other nodes, which is a violation of the atomicity guarantee. Possible scenarios are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some nodes may detect a violation of a uniqueness constraint or something similar and may have to abort, while other nodes are able to commit successfully.&lt;/li&gt;
&lt;li&gt;Some commit requests might get lost in the network, and may eventually abort due to a timeout, while other requests are successful.&lt;/li&gt;
&lt;li&gt;Some nodes may crash before the commit record is fully written and then have to roll back on recovery, while other nodes successfully commit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If some nodes commit a transaction while others abort it, the nodes will be in an inconsistent state. Note that a transaction commit on a node must be irrevocable. It cannot be retracted once it has been committed. The reason for this is that data becomes visible to other transactions once it has been committed by a transaction, and other clients may now rely on that data. Therefore, it&#39;s important that a node commits a transaction only when it is certain that all other nodes in the transaction will commit.&lt;/p&gt;
&lt;h4 id=&quot;introduction-to-two-phase-commit&quot;&gt;Introduction to Two-phase commit &lt;a class=&quot;direct-link&quot; href=&quot;#introduction-to-two-phase-commit&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Two-phase commit (or 2PC) is an algorithm used for achieving atomic transaction commit when multiple nodes are involved. &#39;Atomic&#39; in the sense that either all nodes commit or all abort.&lt;/p&gt;
&lt;p&gt;The key thing here is that the commit process is split into two phases: the &lt;em&gt;prepare&lt;/em&gt; phase and the &lt;em&gt;actual commit&lt;/em&gt; phase.&lt;/p&gt;
&lt;p&gt;It achieves atomicity across multiple nodes by introducing a new component known as &lt;em&gt;the coordinator&lt;/em&gt;. The coordinator can run in the same process as the service requesting the transaction or in an entirely different process. When the application is ready to commit a transaction, the two phases are as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The coordinator sends a &lt;em&gt;prepare&lt;/em&gt; request to all the nodes participating in the transaction, for which the nodes have to respond with essentially a &#39;YES&#39; or &#39;NO&#39; message.&lt;/li&gt;
&lt;li&gt;If all the participants reply &#39;YES&#39;, then the coordinator will send a &lt;em&gt;commit&lt;/em&gt; request in the second phase for them to actually perform the commit. However, if &lt;em&gt;any&lt;/em&gt; of the nodes reply &#39;NO&#39;, the coordinator sends an &lt;em&gt;abort&lt;/em&gt; request to all the participants.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In case it&#39;s still not clear how this protocol ensures atomicity while one-phase commit across multiple nodes does not, note that there are two essential &amp;quot;points of no return&amp;quot;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a participant responds with &amp;quot;YES&amp;quot;, it means that it must be able to commit under all circumstances. A power failure, crash, or memory issue cannot be an excuse for refusing to commit later. It &lt;em&gt;must&lt;/em&gt; definitely be able to commit the transaction without error if needed.&lt;/li&gt;
&lt;li&gt;When the coordinator decides and that decision is written to disk, the decision is irrevocable. It doesn&#39;t matter if the commit or abort request fails at first, it must be retried forever until it succeeds. If a participant crashes before it can complete the commit/abort request, the transaction will be committed in the meantime.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;coordinator-failure&quot;&gt;Coordinator Failure &lt;a class=&quot;direct-link&quot; href=&quot;#coordinator-failure&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;If any of the &lt;em&gt;prepare&lt;/em&gt; requests fails or times out during a 2PC, the coordinator will abort the transaction. If any commit or abort request fails, the coordinator will retry them indefinitely.&lt;/p&gt;
&lt;p&gt;If the coordinator fails before it can send a prepare request, a participant can safely abort the transaction. However, once a participant has received a prepare request and voted &amp;quot;YES&amp;quot;, it can no longer abort by itself. It has to wait to hear from the coordinator about whether or not it should commit the transaction. The &lt;em&gt;downside&lt;/em&gt; of this is that if the coordinator crashes or the network fails after a participant has responded &amp;quot;YES&amp;quot;, the participant can do nothing but wait. In this state, it is said to be &lt;em&gt;in doubt&lt;/em&gt; or &lt;em&gt;uncertain.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The reason why a participant has to wait for the coordinator in the event of a failure is that it does not know whether the failure extends to all participants or just itself. It&#39;s possible that the network failed after the commit request was sent to one of the participants. If the &lt;em&gt;in doubt participants&lt;/em&gt; then decide to abort after a timeout due to not receiving from the coordinator, it will leave the database in an inconsistent state.&lt;/p&gt;
&lt;p&gt;In principle, the in doubt participants could communicate among themselves to find out how each participant voted and then come to an agreement, but that is not part of the 2PC protocol.&lt;/p&gt;
&lt;p&gt;This possibility of failure is why the coordinator must write its decision to a transaction log on disk before sending the request to the participants. When it recovers from a failure, it can read its transaction log to determine the status of all in-doubt transactions. Transactions without a commit record in the coordinator&#39;s log are aborted. In essence, the commit point of 2PC is a regular single-node atomic commit on the coordinator.&lt;/p&gt;
&lt;h5 id=&quot;three-phase-commit&quot;&gt;Three-phase commit &lt;a class=&quot;direct-link&quot; href=&quot;#three-phase-commit&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Two-phase commit is referred to as a &lt;em&gt;blocking&lt;/em&gt; atomic commit protocol because of the fact that it can get stuck waiting for the coordinator to recover.&lt;/p&gt;
&lt;p&gt;An alternative to 2PC that has been proposed is an algorithm called &lt;em&gt;three-phase commit (3PC).&lt;/em&gt; The idea here is that it assumes a network with bounded delays and nodes with bounded response times. This means that when a delay exceeds that bound, a participant can safely assume that the coordinator has crashed.&lt;/p&gt;
&lt;p&gt;However, most practical systems have unbounded network delays and process pauses, and so it cannot guarantee atomicity. If we wrongly declare the coordinator to be dead, the coordinator could resume and end up sending commit or abort requests, even when the participants have already decided. &lt;em&gt;(&lt;strong&gt;Q&lt;/strong&gt;: I wonder if this is something that can be avoided by ensuring that once a coordinator has been declared dead for a particular transaction, it cannot come back and send requests? Might be possible through some form of sequence numbers).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This difficulty in coming up with a &lt;em&gt;perfect failure detector&lt;/em&gt; is why 2PC continues to be used today.&lt;/p&gt;
&lt;h3 id=&quot;distributed-transactions-in-practice&quot;&gt;Distributed Transactions in Practice &lt;a class=&quot;direct-link&quot; href=&quot;#distributed-transactions-in-practice&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Distributed transactions, especially those implemented with two-phase commit, are contentious because of the performance implications and operational problems they cause. This has led to many cloud services choosing not to implement them.&lt;/p&gt;
&lt;p&gt;However, despite these limitations, it&#39;s useful to examine them in more detail as there are lessons that can be learned from them.&lt;/p&gt;
&lt;p&gt;There are two types of distributed transaction which often get conflated:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Database-internal distributed transactions:&lt;/em&gt; This refers to transactions performed by a distributed database that spans multiple replicas or partitions. VoltDB and MySQL Cluster&#39;s NDB storage engine support such transactions. Here, all the nodes participating in the transaction are running the same database software.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Heterogenous distributed transactions:&lt;/em&gt; Here, the participants are two or more different technologies. For example, we could have two databases from different vendors, or even non-database systems such as message brokers. Although the systems may be entirely different under the hood, a distributed transaction has to ensure atomic commit across these systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;exactly-once-message-processing&quot;&gt;Exactly-once message processing &lt;a class=&quot;direct-link&quot; href=&quot;#exactly-once-message-processing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;With heterogeneous transactions, we can integrate diverse systems in powerful ways. For example, we can perform a transaction that spans across a message queue and a database. Say we want to acknowledge a message from a queue as processed if and only if the transaction for processing the message was successfully committed, we could perform this using distributed transactions. This can be implemented by atomically committing the message acknowledgment and the database writes in a single transaction.&lt;/p&gt;
&lt;p&gt;If the transaction fails and the message is not acknowledged, the message broker can safely redeliver the message later.&lt;/p&gt;
&lt;p&gt;An advantage of atomically committing a message together with the side effects of its processing is that it ensures that the message is &lt;em&gt;effectively&lt;/em&gt; processed exactly once. If the transaction fails, the effects of processing the message can simply be rolled back.&lt;/p&gt;
&lt;p&gt;However, this is only possible if all the systems involved in the transaction are able to use the same atomic commit protocol. For example, if a side effect of processing a message involves sending an email and the email server does not support two-phase commit, it will be difficult to roll-back the email. Processing the message multiple times may involve sending multiple emails.&lt;/p&gt;
&lt;p&gt;Next, we&#39;ll discuss the atomic commit protocol that allows such heterogeneous distributed transactions.&lt;/p&gt;
&lt;h4 id=&quot;xa-transactions&quot;&gt;XA Transactions &lt;a class=&quot;direct-link&quot; href=&quot;#xa-transactions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;XA (&lt;em&gt;eXtended Architecture)&lt;/em&gt; is a standard for implementing two-phase commit across heterogeneous technologies.&lt;/p&gt;
&lt;p&gt;XA is a C API for interacting with a transaction coordinator, but bindings for the API exist in other languages.&lt;/p&gt;
&lt;p&gt;It assumes that communication between your application and the participant databases/messaging services is done through a network driver (like JDBC) or a client library which supports XA. If the driver does support XA, it will call the XA API to find out whether an operation should be part of a distributed transaction - and if so, it sends the necessary information to the participant database server. The driver also exposes callbacks needed by the coordinator to interact with the participant, through which it can ask a participant to prepare, commit, or abort.&lt;/p&gt;
&lt;p&gt;The transaction coordinator is what implements the XA API. The coordinator is usually just a library that&#39;s loaded into the same process as the application issuing the transaction. It keeps track of the participants involved in a transaction, their responses after asking them to prepare, and then uses a log to keep track of its commit/abort decision for each transaction.&lt;/p&gt;
&lt;p&gt;Note that a participant database cannot contact the coordinator directly. All of the communication must go through its client library through the XA callbacks.&lt;/p&gt;
&lt;h4 id=&quot;holding-locks-while-in-doubt&quot;&gt;Holding locks while in doubt &lt;a class=&quot;direct-link&quot; href=&quot;#holding-locks-while-in-doubt&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The reason we care so much about transactions not being stuck in doubt is &lt;em&gt;locking.&lt;/em&gt; Transactions often need to take row-level locks on any rows they modify, to prevent dirty writes. These locks must be held until the transaction commits or aborts.&lt;/p&gt;
&lt;p&gt;If we&#39;re using a two-phase commit protocol and the coordinator crashes, the locks will be held until the coordinator is restarted. No other transaction can modify these rows while the locks are held.&lt;/p&gt;
&lt;p&gt;The impact of this is that it can lead to large parts of your application being unavailable: If other transactions want to access the rows held by an in-doubt transaction, they will be blocked until the transaction is resolved.&lt;/p&gt;
&lt;h4 id=&quot;limitations-of-distributed-transactions&quot;&gt;Limitations of distributed transactions &lt;a class=&quot;direct-link&quot; href=&quot;#limitations-of-distributed-transactions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;While XA transactions are useful for coordinating transactions across heterogeneous data systems, we have seen that they can introduce major operational problems. One key insight here is that the transaction coordinator is a kind of database itself (in the sense that it keeps track of a transaction log which is durably persisted), and so it needs to be treated with the same level of importance as other databases. Some of the other limitations of distribute transactions are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2PC needs &lt;em&gt;all&lt;/em&gt; participants to respond before it can commit a transaction. As a result, if &lt;em&gt;any&lt;/em&gt; part of the system is broken, the transaction will fail. This means that distributed transactions have a tendency of &lt;em&gt;amplifying failures&lt;/em&gt;, which is not what we want when building fault-tolerant systems.&lt;/li&gt;
&lt;li&gt;If the coordinator is not replicated across multiple machines, it becomes a single point of failure for the system.&lt;/li&gt;
&lt;li&gt;XA needs to be compatible across a wide range of data systems and so it is a lowest common denominator meaning that it cannot have implementations that are specific to any system. For example, it cannot detect deadlocks across different systems, as that would require a standardized protocol with which different systems can inform each other on what locks are being held by another transaction. It also cannot work with &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#serializable-snapshot-isolation&quot;&gt;Serializable Snapshot Isolation&lt;/a&gt;, as we would need a protocol for identifying conflicts across multiple systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;fault-tolerant-consensus&quot;&gt;Fault-Tolerant Consensus &lt;a class=&quot;direct-link&quot; href=&quot;#fault-tolerant-consensus&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In simple terms, consensus means getting several nodes to agree on something. For example, if we have several people concurrently trying to book the same meeting room or the same username, we can use a consensus algorithm to determine which one should be the winner.&lt;/p&gt;
&lt;p&gt;In formal terms, we describe the consensus problem like this: One or more nodes may &lt;em&gt;propose&lt;/em&gt; values, and the role of the consensus algorithm is to &lt;em&gt;decide&lt;/em&gt; on one of those values. In the case of booking a meeting room, each node handling a user request may propose the username of the user making the request, and the consensus algorithm will decide on which user will get the room.&lt;/p&gt;
&lt;p&gt;A consensus algorithm must satisfy the following properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Uniform agreement:&lt;/em&gt; No two nodes decide differently.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Integrity:&lt;/em&gt; No node decides twice.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Validity:&lt;/em&gt; If a node decides a value &lt;em&gt;v,&lt;/em&gt; then &lt;em&gt;v&lt;/em&gt; was proposed by some node.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Termination:&lt;/em&gt; Every node that does not crash eventually decides some value.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The core idea of consensus is captured in the uniform agreement and integrity properties: everyone must decide on the same outcome, and once the outcome has been decided, you cannot change your mind.&lt;/p&gt;
&lt;p&gt;The validity property is mostly to rule out trivial solutions such as an algorithm that will always decide &lt;em&gt;null&lt;/em&gt; regardless of what was proposed. An algorithm like that would satisfy the first two properties, but not the validity property.&lt;/p&gt;
&lt;p&gt;The termination property is what ensures fault tolerance in consensus-based systems. Without this property, we could designate one node as the &amp;quot;dictator&amp;quot; and let it make all the decisions. However, if that node fails, the system will not be able to make a decision. We saw this situation in the case of two-phase commit which leaves participants in doubt.&lt;/p&gt;
&lt;p&gt;What the termination property means is that a consensus algorithm cannot sit idle and do nothing forever i.e. it must make progress. If some nodes fail, the other nodes must reach a decision. &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#safety-and-liveness&quot;&gt;Note that termination is a liveness property, while the other three are safety properties.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The consensus system model assumes that when a node &amp;quot;crashes&amp;quot;, it disappears and never comes back. That means that any algorithm which must wait for a node to recover will not satisfy the termination property. However, note that this is subject to the assumption that fewer than half of the nodes crashed.&lt;/p&gt;
&lt;p&gt;Note that the distinction between the safety and the liveness properties means that even if the termination property is not met, it cannot corrupt the consensus system by causing it to make invalid decisions. In addition, most consensus algorithms assume that there are no &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#byzantine-faults&quot;&gt;Byzantine faults&lt;/a&gt;. This means that if a node is Byzantine-faulty, it may break the safety properties of the protocol.&lt;/p&gt;
&lt;h4 id=&quot;consensus-algorithms-and-total-order-broadcast&quot;&gt;Consensus algorithms and total order broadcast &lt;a class=&quot;direct-link&quot; href=&quot;#consensus-algorithms-and-total-order-broadcast&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The most popular fault-tolerant consensus algorithms are Paxos, Zab, Raft, and Viewstamped Replication. However, most of these algorithms do not directly make use of the formal model described above i.e. proposing and deciding on a single value, while satisfying the liveness and safety properties. What these algorithms do is that they decide on a &lt;em&gt;sequence&lt;/em&gt; of values, which makes them &lt;em&gt;total order broadcast&lt;/em&gt; algorithms.&lt;/p&gt;
&lt;p&gt;Recall from the discussion earlier that the following properties must be met for total order broadcast:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Messages must be delivered to all nodes in the same order.&lt;/li&gt;
&lt;li&gt;No messages are lost.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we look closely at these properties, total order broadcast can be seen as performing several rounds of consensus as all the nodes have to &lt;em&gt;agree&lt;/em&gt; on what message goes next in the total order sequence. Each consensus decision can be seen as corresponding to one message delivery.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Viewstamped Replication, Raft, and Zab implement total order broadcast directly, because that is more efficient than doing repeated rounds of one-value-at-a-time consensus. In the case of Paxos, this optimization is known as Multi-Paxos.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 9474-9476). O&#39;Reilly Media. Kindle Edition.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;single-leader-replication-and-consensus&quot;&gt;Single-leader replication and consensus &lt;a class=&quot;direct-link&quot; href=&quot;#single-leader-replication-and-consensus&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We&#39;ve learned about single-leader replication takes all the writes to the leader and applies them to the followers in the same order, thereby keeping the replicas up to date. This is the same idea as total order broadcast, but interestingly, we haven&#39;t discussed consensus yet in the context of single-leader replication.&lt;/p&gt;
&lt;p&gt;Consensus in single-leader replication depends on how the leader is chosen. If the leader is always chosen by a manual operator, there&#39;s a risk that it will not satisfy the termination property of consensus if the leader is unavailable for any reason.&lt;/p&gt;
&lt;p&gt;Alternatively, some databases perform automatic leader election and failover by promoting a new leader if the old leader fails. However, in these systems, there is a risk of split-brain (where two nodes could think they&#39;re the leader) and so we still need all the nodes to &lt;em&gt;agree&lt;/em&gt; on who the leader is.&lt;/p&gt;
&lt;p&gt;So it looks like:&lt;/p&gt;
&lt;p&gt;Consensus algorithms are actually total order broadcast algorithms -&amp;gt; total order broadcast algorithms are like single leader replication -&amp;gt; single-leader replication needs consensus to determine the leader - &amp;gt; (repeat cycle)&lt;/p&gt;
&lt;p&gt;How do we break this cycle where it looks like to solve consensus, we must first solve consensus? We&#39;ll discuss that next.&lt;/p&gt;
&lt;h4 id=&quot;epoch-numbering-and-quorums&quot;&gt;Epoch numbering and quorums &lt;a class=&quot;direct-link&quot; href=&quot;#epoch-numbering-and-quorums&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The consensus protocols discussed above all use a leader internally. However, a key to note is that they don&#39;t guarantee that a leader is unique. They provide a weaker guarantee instead: The protocols define a monotonically increasing &lt;em&gt;epoch number&lt;/em&gt; and the guarantee is that within each epoch, the leader is unique.&lt;/p&gt;
&lt;p&gt;Whenever the current leader is thought to be dead, the nodes start a vote to elect a new leader. In each election round, the epoch number is incremented. If we have two leaders belonging to different epochs, the one with the higher epoch number will prevail.&lt;/p&gt;
&lt;p&gt;Before a leader can decide anything, it must be sure that there is no leader with a higher epoch number than it. It does this by collecting votes from a &lt;em&gt;quorum&lt;/em&gt; (typically the majority, but not always) of nodes for every decision that it wants to make. A node will vote for a proposal &lt;em&gt;only&lt;/em&gt; if it is not aware of another leader with a higher epoch.&lt;/p&gt;
&lt;p&gt;Therefore, we have two voting rounds in consensus protocols: one to elect a leader, and another to vote on a leader&#39;s proposal. The important thing is that there must be an overlap in the quorum of nodes that participate in both voting rounds. If a vote on a proposal succeeds, then at least one of the nodes that voted for it must have also been voted in the most recent leader election.&lt;/p&gt;
&lt;p&gt;The biggest differences between 2PC and fault-tolerant consensus algorithms are that the coordinator in 2PC is not elected, and the latter only requires votes from a majority of nodes unlike in 2PC where all the participants must say &amp;quot;YES&amp;quot;. In addition, consensus algorithms define a recovery process to get the nodes into a consistent state after a new leader is elected. These differences are what make consensus algorithms more fault-tolerant.&lt;/p&gt;
&lt;h4 id=&quot;limitations-of-consensus&quot;&gt;Limitations of consensus &lt;a class=&quot;direct-link&quot; href=&quot;#limitations-of-consensus&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Consensus algorithms have numerous advantages like bringing concrete safety properties, providing total order broadcast, and therefore linearizable operations in a fault-tolerant way, but they are not used everywhere because they come with a cost.&lt;/p&gt;
&lt;p&gt;A potential downside of consensus systems is that they require a strict majority to operate. This means that to tolerate one failure, you need three nodes, and to tolerate two failures, a minimum of five nodes are needed.&lt;/p&gt;
&lt;p&gt;Another challenge is that they rely on timeouts to detect failed nodes, and so in a system with variable network delays, it&#39;s possible for a node to falsely think that the leader has failed. This won&#39;t affect the safety properties, but it can lead to frequent leader elections which could harm system performance.&lt;/p&gt;
&lt;h3 id=&quot;membership-and-coordination-services&quot;&gt;Membership and Coordination Services &lt;a class=&quot;direct-link&quot; href=&quot;#membership-and-coordination-services&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Zookeeper and etcd are typically described as &amp;quot;distributed key-value stores&amp;quot; or &amp;quot;coordination and configuration services&amp;quot;. These services look like databases which for which you can read and write the value of a given key, or iterate over keys.&lt;/p&gt;
&lt;p&gt;However, it&#39;s important to note that these systems are not designed to be used as a general-purpose database. Zookeeper and etcd are designed to hold small amounts of data &lt;em&gt;in memory,&lt;/em&gt; all of your application&#39;s data cannot be stored there. This small amount of data is then replicated across all the nodes using a &lt;em&gt;fault-tolerant total order broadcast algorithm.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Some of the features provided by these services are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Linearizable atomic operations&lt;/em&gt;: Zookeeper can be used to implement distributed locks using an atomic compare-and-set operation. If several nodes concurrently try to obtain a lock on a row, it can help to guarantee that only one of them will succeed and the operation will be atomic and linearizable.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Total ordering of operations&lt;/em&gt;: We&#39;ve discussed &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#fencing-tokens&quot;&gt;fencing tokens&lt;/a&gt; before, which can be used to prevent the clients from conflicting with each other when they want to access a resource protected by a lock or lease. Zookeeper helps to provide this by giving each operation a monotonically increasing transaction ID and version number.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Failure detection:&lt;/em&gt; Clients maintain a long-lived session and Zookeeper servers, and both client and server periodically exchange &#39;heartbeats&#39; to check that the other node is alive. If the heartbeats cease for a duration longer than the session timeout, Zookeeper will declare the session to be dead.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Change notifications:&lt;/em&gt; With Zookeeper, clients can be made aware of when other nodes (clients) join the cluster since the new node will write to Zookeeper. A client can also be made aware of when another client leaves the cluster.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that only the linearizable atomic operations here require consensus, but the other features make Zookeeper useful for coordination among distributed systems. Also note that an application developer will rarely interact with Zookeeper. It is often relied on indirectly via another project like Kafka, Hbase, Hadoop YARN etc.&lt;/p&gt;
&lt;h4 id=&quot;allocating-work-to-nodes&quot;&gt;Allocating work to nodes &lt;a class=&quot;direct-link&quot; href=&quot;#allocating-work-to-nodes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When new nodes join a partitioned cluster, some of the partitions need to be moved from existing nodes to the new ones in order to rebalance the load. Similarly, when nodes fail or are removed from the cluster, the partitions that they held have to be moved to the remaining nodes. Zookeeper can help to achieve tasks like this through the use of atomic operations, change notifications and ephemeral nodes.&lt;/p&gt;
&lt;p&gt;An important thing to note is that Zookeeper typically manages data that is quite &lt;em&gt;slow-changing&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It represents information like “the node running on IP address 10.1.1.23 is the leader for partition 7,” and such assignments usually change on a timescale of minutes or hours.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 9615-9616). O&#39;Reilly Media. Kindle Edition.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It is not intended for storing the runtime state of an application, which is likely to change thousands or millions of times per second. Apache BookKeeper is a tool used to replicate runtime state to other nodes.&lt;/p&gt;
&lt;h4 id=&quot;service-discovery&quot;&gt;Service discovery &lt;a class=&quot;direct-link&quot; href=&quot;#service-discovery&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Service discovery is the process of finding out the IP address that you need to connect to in order to reach a service. Zookeeper, Consul, and etc are often used for service discovery.&lt;/p&gt;
&lt;p&gt;The main idea is that services will register their network endpoints in a service registry from which they can be discovered by other services. The read requests for a service&#39;s endpoint do not need to be linearizable (DNS is the traditional method of retrieving the IP address of a service name and for availability purposes, its reads are not linearizable).&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This will be the last set of notes I&#39;ll post from the book in a while. The last section of the book is on &amp;quot;Derived Data&amp;quot; and is a lot more practical than the theory we have discussed so far. In the meantime, I intend to post another set of notes from &lt;a href=&quot;https://pdos.csail.mit.edu/6.824/index.html&quot;&gt;this&lt;/a&gt; course which I will be starting soon.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Last updated on 19-06-2020 to fix a few typos.&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 9 - Consistency and Consensus (Part One)</title>
		<link href="https://timilearning.com/posts/ddia/part-two/chapter-9-1/"/>
		<updated>2020-03-14T22:19:35-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-two/chapter-9-1/</id>
		<content type="html">&lt;p&gt;Notes from Chapter 9 of Martin Kleppmann&#39;s &#39;Designing Data-Intensive Applications&#39; book. This chapter is split into two parts.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In this chapter, we focus on some of the abstractions that applications can rely on in building fault-tolerant distributed systems. One of these is Consensus. Once there&#39;s a consensus implementation, applications can use it for things like leader election and state machine replication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#consistency-guarantees&quot;&gt;Consistency Guarantees&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#linearizability&quot;&gt;Linearizability&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#linearizability-vs-serializability&quot;&gt;Linearizability vs Serializability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#relying-on-linearizability&quot;&gt;Relying on Linearizability&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#locking-and-leader-election&quot;&gt;Locking and leader election&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#constraints-and-uniqueness-guarantees&quot;&gt;Constraints and uniqueness guarantees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementing-linearizable-systems&quot;&gt;Implementing Linearizable Systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-cost-of-linearizability&quot;&gt;The Cost of Linearizability&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-cap-theorem&quot;&gt;The CAP Theorem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linearizability-and-network-delays&quot;&gt;Linearizability and network delays&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ordering-guarantees&quot;&gt;Ordering Guarantees&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#ordering-and-causality&quot;&gt;Ordering and Causality&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-causal-order-is-not-a-total-order&quot;&gt;The causal order is not a total order&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linearizability-is-stronger-than-causal-consistency&quot;&gt;Linearizability is stronger than causal consistency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#capturing-causal-dependencies&quot;&gt;Capturing causal dependencies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sequence-number-ordering&quot;&gt;Sequence Number Ordering&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#noncausal-sequence-number-generators&quot;&gt;Noncausal sequence number generators&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#lamport-timestamps&quot;&gt;Lamport Timestamps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#timestamp-ordering-is-not-sufficient&quot;&gt;Timestamp ordering is not sufficient&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;consistency-guarantees&quot;&gt;Consistency Guarantees &lt;a class=&quot;direct-link&quot; href=&quot;#consistency-guarantees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We&#39;ve discussed &lt;em&gt;eventual consistency&lt;/em&gt; in some of the earlier chapters as one of the consistency guarantees provided by some applications. It means that even though there might be delays in replicating data across multiple nodes, the data will &lt;em&gt;eventually&lt;/em&gt; get to all nodes.&lt;/p&gt;
&lt;p&gt;However, it is a very weak guarantee as it doesn&#39;t say &lt;em&gt;when&lt;/em&gt; the replicas will converge, it just says that they will converge.&lt;/p&gt;
&lt;p&gt;There are stronger consistency guarantees that can be provided, which we&#39;ll touch on in this chapter, but these come at a cost. These stronger guarantees often have worse performance or are less fault-tolerant than systems with weaker guarantees.&lt;/p&gt;
&lt;h3 id=&quot;linearizability&quot;&gt;Linearizability &lt;a class=&quot;direct-link&quot; href=&quot;#linearizability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The idea behind Linearizability is that the database should always appear as if there is only one copy of the data. This means that making the same request on multiple nodes should always give the same response as long as no update is made between those requests.&lt;/p&gt;
&lt;p&gt;It is also a &lt;em&gt;recency guarantee,&lt;/em&gt; meaning that the value read must be the most recent or up-to-date value, and is not from a stale cache. Basically, as soon as a client successfully completes a write, all other clients must see the value just written.&lt;/p&gt;
&lt;p&gt;If one client&#39;s read returns a new value, all subsequent reads must also return the new value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In the book, Linearizability is said to also be known as atomic consistency, strong consistency, immediate consistency or external consistency. However, &lt;a href=&quot;https://cloud.google.com/spanner/docs/true-time-external-consistency&quot;&gt;Google&#39;s Cloud Spanner&lt;/a&gt; has a different idea and distinguishes between some of those terms. This distinction is explained in &lt;a href=&quot;https://timilearning.com/posts/consistency-models/&quot;&gt;another post&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;linearizability-vs-serializability&quot;&gt;Linearizability vs Serializability &lt;a class=&quot;direct-link&quot; href=&quot;#linearizability-vs-serializability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Linearizability is a recency guarantee on reads and writes of a single object. This guarantee does not group multiple operations together into a transaction (meaning it cannot protect against a problem like &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#write-skew-and-phantoms&quot;&gt;write skew&lt;/a&gt;, where a transaction makes a write based on a value it read earlier that has now been updated by another concurrently running transaction).&lt;/p&gt;
&lt;p&gt;Serializability is an isolation property of transactions that guarantees that transactions behave the same as if they had executed in &lt;em&gt;some&lt;/em&gt; serial order i.e. each transaction is completed before the next one starts. There is no guarantee on &lt;em&gt;what&lt;/em&gt; serial order these transactions appear to run in, all that matters is that it is a serial order.&lt;/p&gt;
&lt;p&gt;When a database provides both serializability and linearizability, the guarantee is known as &lt;em&gt;strict serializability&lt;/em&gt; or &lt;em&gt;strong one-copy serializability&lt;/em&gt;. I believe external consistency and strict serializability provide the same guarantees.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#two-phase-locking-(2pl)&quot;&gt;Two Phase-Locking&lt;/a&gt; and &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#actual-serial-execution&quot;&gt;Actual Serial Execution&lt;/a&gt; are implementations of serializability that are also linearizable. However, &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#serializable-snapshot-isolation&quot;&gt;serializable snapshot isolation&lt;/a&gt; is not linearizable, since a transaction will be reading values from a consistent snapshot.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Question&lt;/em&gt;: If serializable snapshot isolation is well implemented by ensuring that it detects writes in a transaction that may affect prior reads (from a consistent snapshot) or that it detects stale reads, wouldn&#39;t that make it linearizable as one of these transactions will be aborted and will thus preserve the recency guarantee?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Answer&lt;/em&gt;: I&#39;m guessing the risk here is that the stale read might have returned a value now being used outside of the database, which then violates the linearizability guarantee.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that a stale read is &lt;em&gt;not&lt;/em&gt; a violation of serializability, see &lt;a href=&quot;https://fauna.com/blog/serializability-vs-strict-serializability-the-dirty-secret-of-database-isolation-levels&quot;&gt;here&lt;/a&gt;. If Transactions A &amp;amp; B are concurrent and Transaction A commits before Transaction B, serializability is still preserved if the database makes it look like the operations in Transaction B happened before those in Transaction A. The key thing is that the transactions appear to be executed one after the other.&lt;/p&gt;
&lt;h4 id=&quot;relying-on-linearizability&quot;&gt;Relying on Linearizability &lt;a class=&quot;direct-link&quot; href=&quot;#relying-on-linearizability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As good as linearizability is as a guarantee, it is not critical for all applications. However, there are examples of where linearizability is important for making a system work correctly, and we&#39;ll cover them here.&lt;/p&gt;
&lt;h5 id=&quot;locking-and-leader-election&quot;&gt;Locking and leader election &lt;a class=&quot;direct-link&quot; href=&quot;#locking-and-leader-election&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;A system with a single-leader replication model must ensure that there&#39;s only ever one leader at a time. One way to implement leader election is by using a lock. All the eligible nodes start up and try to acquire a lock and the successful one becomes the leader.&lt;/p&gt;
&lt;p&gt;This lock must be linearizable: once a node owns the lock, all the other nodes must see that it is that node that owns the lock.&lt;/p&gt;
&lt;p&gt;Apache ZooKeeper and etcd are often used to implement distributed locks and leader election.&lt;/p&gt;
&lt;h5 id=&quot;constraints-and-uniqueness-guarantees&quot;&gt;Constraints and uniqueness guarantees &lt;a class=&quot;direct-link&quot; href=&quot;#constraints-and-uniqueness-guarantees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;When multiple users are concurrently trying to register a value that must be unique, each user can be thought of as acquiring a lock on that value. E.g. a username or email address system.&lt;/p&gt;
&lt;p&gt;We see similar issues in examples like ensuring that a bank account never goes negative, not selling more items than is available in stock, not concurrently booking the same seat on a flight, or in a theater for two people. For these constraints to be implemented properly, there needs to be a single up to date value (the account balance, the stock level, the seat occupancy) that all nodes agree on.&lt;/p&gt;
&lt;p&gt;However, note that some of these constraints can be treated loosely and are not always critical, so linearizability may not be needed.&lt;/p&gt;
&lt;h4 id=&quot;implementing-linearizable-systems&quot;&gt;Implementing Linearizable Systems &lt;a class=&quot;direct-link&quot; href=&quot;#implementing-linearizable-systems&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Seeing that linearizability means the system behaves as if there is only one copy of the data, the simplest way to implement it will be to actually have just one copy of the data. However, that won&#39;t be fault-tolerant if the node that has the single copy becomes unavailable.&lt;/p&gt;
&lt;p&gt;Since replication is the most common way to make a system fault-tolerant, we&#39;ll compare different replication methods here and discuss whether they can be made linearizable.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Single-leader replication (potentially linearizable):&lt;/em&gt; If we make every read from the leader or from synchronously updated followers, the system has the potential to be linearizable. However, there is no absolute guarantee as the system can still be non-linearizable either by design (because it uses snapshot isolation) or due to concurrency bugs.&lt;/p&gt;
&lt;p&gt;Using the leader for reads also implies that there is an assumption that we&#39;ll always know who the leader is. Issues like split-brain can mean that a single-leader system can violate linearizability.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Multi-leader replication&lt;/em&gt; (&lt;em&gt;not linearizable):&lt;/em&gt; These systems are generally not linearizable since they can process writes concurrently, and the writes are typically asynchronously replicated to other nodes. It means that clients can view different values of a register (single object) if they read from different nodes.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Consensus Algorithms&lt;/em&gt; &lt;em&gt;(linearizable)&lt;strong&gt;:&lt;/strong&gt;&lt;/em&gt; We haven&#39;t dealt with this yet but these systems are typically linearizable. They are similar to single-leader replication, but they contain additional measures to prevent stale replicas and split-brain. As a result, consensus protocols are used to implement linearizable storage safely. Zookeeper and Etcd work this way.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Leaderless replication&lt;/em&gt; (&lt;em&gt;probably not linearizable)&lt;/em&gt;: Recall from &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-5/#leaderless-replication&quot;&gt;Chapter 5&lt;/a&gt; that these systems typically require quorum reads and writes where w + r &amp;gt; n. While these can be linearizable, they are almost certainly non-linearizable under certain circumstances, like when &amp;quot;Last write wins&amp;quot; is used as the conflict resolution method based on time-of-day clocks. These are typically non-linearizable because we know that clock timestamps are not guaranteed to be consistent with the actual ordering of events due to &lt;em&gt;clock skew.&lt;/em&gt; Another circumstance where non-linearizability is almost guaranteed is when &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-5/#sloppy-quorums-and-hinted-handoff&quot;&gt;sloppy quorums&lt;/a&gt; are used.&lt;/p&gt;
&lt;p&gt;Even with strict quorums, there is the possibility of non-linearizability due to concurrency bugs. If we have 3 nodes in a cluster and set w = 3 and r = 2, the quorum condition is met. However, if a client is writing to 3 nodes and two clients concurrently read from 2 of those 3 nodes, they may see different values for a register as a result of network delays in writing to all the nodes.&lt;/p&gt;
&lt;p&gt;However, it is possible to make these dynamo-style quorums linearizable at the cost of reduced performance. To do this, a reader must perform &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-5/#read-repair-and-anti-entropy&quot;&gt;read repair&lt;/a&gt; synchronously before returning results, and a writer must read the latest state of a quorum of nodes before sending its write.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-cost-of-linearizability&quot;&gt;The Cost of Linearizability &lt;a class=&quot;direct-link&quot; href=&quot;#the-cost-of-linearizability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;While linearizability is often desirable, the performance costs mean that it is not always an ideal guarantee.&lt;/p&gt;
&lt;p&gt;Consider a scenario where we have two data centers and there&#39;s a network interruption between those data centers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In a multi-leader database setup, the operations can continue in each data center normally since the writes can be queued up until the network link is restored and replication can happen asynchronously.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;In a single-leader setup, the leader must be in one of the data centers. Therefore, clients connected to a follower data center will not be able to contact the leader and cannot make any writes, nor any linearizable reads (their reads will be stale if the leader keeps getting updated). An application that requires linearizable reads and writes will become unavailable in the data centers which cannot contact the leader.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Clients that can contact the leader data center directly will not witness any problems, since the application continues to work normally there.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;the-cap-theorem&quot;&gt;The CAP Theorem &lt;a class=&quot;direct-link&quot; href=&quot;#the-cap-theorem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The CAP theorem is a popular theorem in Distributed Systems that is often misunderstood. It describes a trade-off in building distributed systems. In relation to the scenario above, this trade-off is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If an application &lt;em&gt;requires&lt;/em&gt; linearizability and some replicas are disconnected from other replicas due to a network problem, then those replicas cannot process requests while they are disconnected: the replicas must either wait until the network problem is fixed or return an error. These replicas are then &lt;em&gt;unavailable.&lt;/em&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;If the application &lt;em&gt;does not require&lt;/em&gt; linearizability, it can be written in a way that each replica can process requests independently even when disconnected from other replicas. Therefore, the application can remain available in the face of a network problem, but the behaviour is not &lt;em&gt;linearizable.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the original definition of the CAP Theorem, the behaviour described for Consistency is linearizability. Availability means that any non-failing node must return a response that contains the results of the requested work i.e., not a 500 error or a timeout message.&lt;/p&gt;
&lt;p&gt;Therefore, in the face of network partitions or faults, a system has to choose between either total availability or total linearizability. That&#39;s the CAP Theorem in simple terms.&lt;/p&gt;
&lt;p&gt;Applications that do not require linearizability are more tolerant of network problems since the nodes can continue to serve requests.&lt;/p&gt;
&lt;p&gt;Note that while the CAP Theorem has been useful, the definition is quite narrow in scope (it only considers Linearizability as the consistency model and network partitions (i.e. nodes in a network disconnected from each other) as the only types of faults, it says nothing about network delays or dead nodes.&lt;/p&gt;
&lt;p&gt;You can read a critique of the CAP theorem in &lt;a href=&quot;https://arxiv.org/abs/1509.05393&quot;&gt;this&lt;/a&gt; article,  which also proposes alternative ways to analyze systems.&lt;/p&gt;
&lt;h5 id=&quot;linearizability-and-network-delays&quot;&gt;Linearizability and network delays &lt;a class=&quot;direct-link&quot; href=&quot;#linearizability-and-network-delays&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Fault tolerance is not the only reason for dropping linearizability, performance is another reason why it sometimes gets dropped.&lt;/p&gt;
&lt;p&gt;Interestingly, RAM on a modern multi-core CPU is not linearizable. This means that if a thread running on one CPU core writes to a memory address, a thread on another CPU core is not guaranteed to read the latest value written (unless a fence or memory barrier is used).&lt;/p&gt;
&lt;p&gt;From &lt;a href=&quot;https://stackoverflow.com/questions/286629/what-is-a-memory-fence/286705#286705&quot;&gt;StackOverflow&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A memory fence/barrier is a class of instructions that mean memory reads/writes occur in the order you expect. For example a &#39;full fence&#39; means all reads/writes before the fence are committed before those after the fence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This happens because every CPU core has its own memory cache and store buffer, and memory access goes to the cache by default. Changes are asynchronously written out to main memory. Accessing data in the cache is faster than going to the main memory, so this feature is useful for good &lt;em&gt;performance&lt;/em&gt; on modern CPUs.&lt;/p&gt;
&lt;p&gt;We can&#39;t say that this tradeoff was made for availability purposes, because we wouldn&#39;t expect on CPU core to continue to function properly while disconnected from the rest of the computer.&lt;/p&gt;
&lt;p&gt;Linearizability is always slow, not just during a network fault. There&#39;s a proof in &lt;a href=&quot;http://courses.csail.mit.edu/6.852/01/papers/p91-attiya.pdf&quot;&gt;this&lt;/a&gt; paper that if you want linearizability, the response time of read and write requests is at least proportional to the uncertainty of delays in the network.&lt;/p&gt;
&lt;p&gt;The response time will certainly be high in networks with highly variable delays. Weaker consistency models can be much faster than linearizability and as it is with everything, there&#39;s always a tradeoff.&lt;/p&gt;
&lt;p&gt;In Chapter 12, there are some approaches suggested for avoiding linearizability without sacrificing correctness.&lt;/p&gt;
&lt;h2 id=&quot;ordering-guarantees&quot;&gt;Ordering Guarantees &lt;a class=&quot;direct-link&quot; href=&quot;#ordering-guarantees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ordering has been mentioned a lot in this book because it is such a fundamental idea in distributed systems. Some of the contexts in which we&#39;ve discussed it so far are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For single-leader replication. The main purpose of the leader is to determine the &lt;em&gt;order of writes&lt;/em&gt; in the replication log i.e. the order in which followers apply writes. Without a single leader, we can have conflicts due to concurrent operations.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7/#serializability&quot;&gt;Serializability&lt;/a&gt;: Serializability is about ensuring that transactions behave as if they were executed in &lt;em&gt;some sequential order.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Timestamps and clocks in distributed systems are an attempt to introduce order into a disorderly world e.g. to determine which one of two writes happened later.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;ordering-and-causality&quot;&gt;Ordering and Causality &lt;a class=&quot;direct-link&quot; href=&quot;#ordering-and-causality&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One of the reasons why ordering keeps coming up is that it helps preserve &lt;em&gt;causality.&lt;/em&gt; With causality, an ordering of events is guaranteed such that cause always comes before effect. If one event happened before another, causality will ensure that that relationship is captured i.e. the &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-5/#the-%22happens-before%22-relationship-and-concurrency&quot;&gt;happens-before relationship&lt;/a&gt;. This is useful because if one event happens as a result of another one, it can lead to inconsistencies in the system if that order is not captured. Some examples of this are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a question leads to an answer, then an observer should not see the answer before the question.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;When a row is first created and then updated, a replica should not see the instruction to update the row before the creation instruction.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;When we discussed snapshot isolation for transactions, we mentioned that the idea is for a transaction to read from a consistent snapshot. Consistent here means &lt;em&gt;consistent with causality&lt;/em&gt; i.e. when we read from a snapshot, the effects of all the operations that happened &lt;em&gt;causally&lt;/em&gt; before the snapshot was taken are visible in that snapshot, but no operations that happened causally afterward can be seen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A system that obeys the ordering imposed by causality is said to be &lt;em&gt;causally consistent.&lt;/em&gt; For example, snapshot isolation provides causal consistency, since when you read some data from it, you must also be able to see any data that causally precedes it (assuming it has not be deleted within the transaction).&lt;/p&gt;
&lt;h5 id=&quot;the-causal-order-is-not-a-total-order&quot;&gt;The causal order is not a total order &lt;a class=&quot;direct-link&quot; href=&quot;#the-causal-order-is-not-a-total-order&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;If elements are in a &lt;em&gt;total order,&lt;/em&gt; it means that they can always be compared. That is, with any two elements, you can always see which one is greater and which is smaller.&lt;/p&gt;
&lt;p&gt;With a &lt;em&gt;partial order,&lt;/em&gt; we can sometimes compare the elements and say which is bigger or smaller, but in other cases the elements are incomparable. For example, mathematical sets are not totally ordered. You can&#39;t compare {&lt;em&gt;a, b}&lt;/em&gt; with {&lt;em&gt;b, c}&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This difference between total order and a partial order is reflected when we compare Linearizability and Causality as consistency models:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Linearizability&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We have a &lt;em&gt;total order&lt;/em&gt; of operations in a linearizable system. If the system behaves as if there is only one copy of the data, and every operation is atomic (meaning we can always point to before and after that operation), then we can always say which operation happened first.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Causality&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Two operations are ordered if they are causally related (i.e. we can say which happened before the other), but are incomparable if they are concurrent. With concurrent operations, we can&#39;t say that one happened before the other.&lt;/p&gt;
&lt;p&gt;This definition means that &lt;em&gt;there is no concurrency in a linearizable database&lt;/em&gt;. We can always say which operations happened before the other.&lt;/p&gt;
&lt;p&gt;The version history of a system like Git is similar to a graph of causal dependencies. One commit often happens after another, but sometimes they branch off, and we create merges when those concurrently created commits are combined.&lt;/p&gt;
&lt;h5 id=&quot;linearizability-is-stronger-than-causal-consistency&quot;&gt;Linearizability is stronger than causal consistency &lt;a class=&quot;direct-link&quot; href=&quot;#linearizability-is-stronger-than-causal-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The relationship between linearizability and causal order is that linearizability &lt;em&gt;implies&lt;/em&gt; causality. Any system that is linearizable will preserve causality out of the box.&lt;/p&gt;
&lt;p&gt;This is part of what makes linearizable systems easy to understand. However, given the cost of linearizability that we&#39;ve discussed above, many distributed systems have dropped linearizability.&lt;/p&gt;
&lt;p&gt;Fortunately, linearizability is not the only way of preserving causality. &lt;a href=&quot;https://jepsen.io/consistency/models/causal&quot;&gt;Causal consistency&lt;/a&gt; is actually the strongest possible consistency model that does not slow down due to network delays, and also remains available in the face of network failures. The caveat here is that in the face of network failures, clients must stick to the same server, given that the server captures the effect of all operations that happened causally before the partition.&lt;/p&gt;
&lt;h5 id=&quot;capturing-causal-dependencies&quot;&gt;Capturing causal dependencies &lt;a class=&quot;direct-link&quot; href=&quot;#capturing-causal-dependencies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;&lt;em&gt;Causal consistency captures the notion that causally-related operations should appear in the same order on all processes—though processes may disagree about the order of causally independent operations - Jepsen&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For a causally consistent database, when a replica processes an operation, it needs to ensure that all the operations that happened before it have already been processed; if a preceding operation is missing, the system must hold off on processing the later one until the preceding operation has been processed.&lt;/p&gt;
&lt;p&gt;The hard part is determining how to describe the &amp;quot;knowledge&amp;quot; of a node in a system. If a node had seen the value of X when it issued the write Y, X and Y must be causally related.&lt;/p&gt;
&lt;p&gt;We discussed &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-5/#detecting-concurrent-writes&quot;&gt;&#39;Detecting Concurrent Writes&#39;&lt;/a&gt; earlier where we focused on causality in a leaderless datastore and detecting concurrent writes to the same key in order to prevent lost updates. For causal consistency though, we need to go beyond just keeping track of a single key, but instead tracking causal dependencies across the entire database.&lt;/p&gt;
&lt;p&gt;To determine causal ordering, the database needs to keep track of which version of the data was read by an application.&lt;/p&gt;
&lt;h4 id=&quot;sequence-number-ordering&quot;&gt;Sequence Number Ordering &lt;a class=&quot;direct-link&quot; href=&quot;#sequence-number-ordering&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A good way of keeping track of causal dependencies in a database is by using &lt;em&gt;sequence numbers&lt;/em&gt; or &lt;em&gt;timestamps&lt;/em&gt; to order the events. This timestamp can be a &lt;em&gt;logical clock&lt;/em&gt; which is an algorithm that generates monotonically increasing numbers for each operation. These sequence numbers provide a total order meaning that if we have two sequence numbers, we can always determine which is greater.&lt;/p&gt;
&lt;p&gt;The important thing is to create sequence numbers in a total order that is &lt;em&gt;consistent with causality&lt;/em&gt; meaning that if operation A causally happened before B, then the sequence number for A must be lower than that of B. We can order concurrent operations arbitrarily.&lt;/p&gt;
&lt;p&gt;With single-leader databases, the replication log defines a total order of write operations that is consistent with causality. Here, the leader can assign a monotonically increasing sequence number to each operation in the log. A follower that applies the writes in the order they appear in the replication log will always be in a causally consistent state.&lt;/p&gt;
&lt;h5 id=&quot;noncausal-sequence-number-generators&quot;&gt;Noncausal sequence number generators &lt;a class=&quot;direct-link&quot; href=&quot;#noncausal-sequence-number-generators&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;In a multi-leader or leaderless database, generating sequence numbers for operations can be done in different ways such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensuring that each node generates an independent set of sequence numbers e.g. if we have two nodes, one node can generate even numbers while the other can generate odd numbers.&lt;/li&gt;
&lt;li&gt;A timestamp from a time-of-day can be attached to each operation. We&#39;ve &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8/#timestamps-for-ordering-events.&quot;&gt;discussed&lt;/a&gt; why this is unreliable previously*.*&lt;/li&gt;
&lt;li&gt;We can preallocate blocks of sequence numbers. E.g node A could claim a block of numbers from 1 to 1000, and node B could claim the block from 1001 to 2000.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, while these options perform better than pushing all operations through a single leader which increments the counter, the problem with them is that they these sequence number are &lt;em&gt;not consistent with causality.&lt;/em&gt; They do not capture ordering across different nodes.&lt;/p&gt;
&lt;p&gt;If we used the third option, for example, an operation numbered at 1100 on node B could have happened before operation 50 on node A if they process a different number of operations per second. There is no way to capture that using these methods.&lt;/p&gt;
&lt;h5 id=&quot;lamport-timestamps&quot;&gt;Lamport Timestamps &lt;a class=&quot;direct-link&quot; href=&quot;#lamport-timestamps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;This is one of the most important topics in the field of distributed systems. It’s a simple method for generating sequence numbers across multiple nodes that &lt;em&gt;is&lt;/em&gt; consistent with causality.&lt;/p&gt;
&lt;p&gt;The idea here is that each node has a unique identifier, and also keeps a counter of the number of operations it has processed. The Lamport timestamp is then a pair of (counter, nodeID). Multiple nodes can have the same counter value, but including the node ID in the timestamp makes it unique.&lt;/p&gt;
&lt;p&gt;Lamport timestamps provide a total ordering: if there are two timestamps, the one with the greater counter value is the greater timestamp; if the counter values are the same, then we pick the one with the greater node ID as the greater timestamp.&lt;/p&gt;
&lt;p&gt;Quoting the book, what makes Lamport timestamps consistent with causality is the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every node and every client keeps track of the maximum counter value it has seen so far, and includes that maximum on every request. When a node receives a request or response with a maximum counter value greater than its own counter value, it immediately increases its own counter to that maximum.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With every operation, the node increases the maximum counter value it has seen by 1.&lt;/p&gt;
&lt;p&gt;Consider the diagram below:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/lamport.png&quot; alt=&quot;Lamport Timestamp Illustration&quot;&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
Figure 1 - Lamport Timestamps Illustration.
&lt;/p&gt;
&lt;p&gt;In this figure, the nodes and clients initially have a counter value of 0:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When client A first writes to node 1, node 1 increases its counter value to 1. (1, 1)
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Client A then writes to node 2, providing node 2 with its counter value of 1. Node 2&#39;s current counter value is 0, so it first sets its value to 1, increases it to 2 for the new operation and then returns the new value.  (2, 2)
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;When client B sends its request to node 2, node 2 has a greater counter value than client B, so it increases its current value to 3 for the new operation and returns it to client B. (3, 2)
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Finally, client A writes to node 1 and that returns a new counter value. (3, 1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A possible ordering of these operations is (1,1) -&amp;gt; (2, 2) -&amp;gt; (3, 1) -&amp;gt; (3,2), if in the case of the same counter value, our ordering scheme gives precedence to the node with the lower ID value.&lt;/p&gt;
&lt;p&gt;This ordering showcases a limitation of Lamport timestamps. Even though operation (3,2) appears to complete before (3,1), the ordering does not reflect that.&lt;/p&gt;
&lt;p&gt;The fact that those two have the same counter value means that they are concurrent and the operations do not know about each other, but Lamport timestamps must enforce a total ordering. With the ordering from Lamport timestamps, &lt;em&gt;you cannot tell whether two operations are concurrent or whether they are causally dependent.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Basically, if two events are causally related, the Lamport timestamp ordering will always obey causality. But if one event appears before another in the ordering, it does not mean that they are causally related.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://martinfowler.com/articles/patterns-of-distributed-systems/version-vector.html&quot;&gt;Version Vectors&lt;/a&gt; can help distinguish whether two operations are concurrent or whether one causally depends on the other, but Lamport timestamps have the advantage that they are more compact.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Aside&lt;/em&gt;: I think Lamport timestamp ordering is also sufficient to provide &lt;a href=&quot;https://jepsen.io/consistency/models/sequential&quot;&gt;sequential consistency&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Further Reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://deque.blog/2018/09/13/distributed-agreement-on-random-order-fun-with-lamport-timestamps/&quot;&gt;Distributed Agreement on Random Order – Fun with Lamport Timestamps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sergeiturukin.com/2017/06/29/eventual-consistency.html&quot;&gt;Consistency, causal and eventual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;timestamp-ordering-is-not-sufficient&quot;&gt;Timestamp ordering is not sufficient &lt;a class=&quot;direct-link&quot; href=&quot;#timestamp-ordering-is-not-sufficient&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Although Lamport timestamps are great for defining a total order that is consistent with causality, they do not solve some common problems in distributed systems.&lt;/p&gt;
&lt;p&gt;The key thing to note here is that they only define a total order of operations &lt;em&gt;after&lt;/em&gt; you have collected all the operations. If one operation needs to decide &lt;em&gt;right now&lt;/em&gt; whether a decision should be made, it might need to check with every other node that there&#39;s no concurrently executing operation that could affect its decision. Any of the other nodes being down will bring the system to a halt, which is not good for fault tolerance.&lt;/p&gt;
&lt;p&gt;For example, if two users concurrently try to create an account with the same username, only one of them should succeed. It might seem as though we could simply pick the one with the lower timestamp as the winner and let the one with the greater timestamp fail. However, if a node needs to decide &lt;em&gt;right now&lt;/em&gt;, it might simply not be aware that another node is in the process of concurrently creating an account, or might not know what timestamp the other node may assign to the operation.&lt;/p&gt;
&lt;p&gt;It&#39;s not enough to have a total ordering of operations, it&#39;s also important to know &lt;em&gt;when&lt;/em&gt; the order is finalized i.e. what that order is at each point in time.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-2/&quot;&gt;second part&lt;/a&gt; of these notes, we&#39;ll look at ways to solve the challenge of knowing the order of operations at each point in time.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Last Updated: 15-12-2022.&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Data Storage on Your Computer&#39;s Disk - Part 2; On Indexes</title>
		<link href="https://timilearning.com/posts/data-storage-on-disk/part-two/"/>
		<updated>2020-02-09T16:52:25-00:00</updated>
		<id>https://timilearning.com/posts/data-storage-on-disk/part-two/</id>
		<content type="html">&lt;p&gt;This is the second part of the series on &#39;Data Storage on Disk&#39;. This post will focus on database indexes and the underlying data structure of in many relational databases today: &lt;em&gt;B-Trees.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#recap-of-previous-post.&quot;&gt;Recap of Previous Post.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#heaps&quot;&gt;Heaps&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#navigating-through-a-heap&quot;&gt;Navigating Through a Heap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#indexes&quot;&gt;Indexes&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#b-trees&quot;&gt;B-Trees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%22so-how-does-the-index-relate-to-the-actual-data-being-stored%3F%22&quot;&gt;&amp;quot;So how does the index relate to the actual data being stored?&amp;quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#write-amplification-in-b-trees&quot;&gt;Write Amplification in B-Trees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#b%2B-trees&quot;&gt;B+ Trees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#on-heaps&quot;&gt;On Heaps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#on-indexes-and-b-trees&quot;&gt;On Indexes and B-Trees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;recap-of-previous-post.&quot;&gt;Recap of Previous Post. &lt;a class=&quot;direct-link&quot; href=&quot;#recap-of-previous-post.&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the &lt;a href=&quot;https://timilearning.com/posts/data-storage-on-disk/part-one/&quot;&gt;previous post&lt;/a&gt;, we learned that rows in your database table are mapped as &lt;em&gt;records&lt;/em&gt; internally, and those records are organized into &lt;em&gt;pages&lt;/em&gt; on your computer&#39;s disk. We also learned about Temporal and Spatial Locality, which are two principles that help determine how data pages are loaded from disk into the in-memory cache.&lt;/p&gt;
&lt;p&gt;We concluded by learning about write-ahead logs, and why they are particularly useful in the context of database transactions.&lt;/p&gt;
&lt;p&gt;In this post, we&#39;ll dive even deeper and learn about how records are organized within pages, how pages are organized within files on disk, and how that organization makes it faster to search for records in your database. In short, we&#39;ll learn what database indexes are.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In many relational databases today, pages belonging to a table can be organized on your computer&#39;s disk in two main ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As a Heap ( A table without a Clustered Index - &lt;em&gt;we&#39;ll learn what this means soon.&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;As a Clustered Index&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;heaps&quot;&gt;Heaps &lt;a class=&quot;direct-link&quot; href=&quot;#heaps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When the pages of a table are in a heap, it means that they are structured in the database without any &lt;em&gt;logical order&lt;/em&gt;. The records that belong to the pages in the heap also have no logical order. A row inserted in a heap table is &#39;heaped&#39; on the existing rows until a page gets filled up.&lt;/p&gt;
&lt;p&gt;To make this clearer, let&#39;s look at the image below which represents a Person table structured as a heap, with each row containing values for the Name, Age and Location columns. In this example, there is no obvious sort order of the rows. A table with a logical sort order would have these records sorted either alphabetically by the Name or Location columns, or numerically by the Age column.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/new-heap-2.png&quot; alt=&quot;Representation of records in a Heap table&quot;&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
Figure 1 - Representation of a records in a Heap table.
&lt;/p&gt;
&lt;h4 id=&quot;navigating-through-a-heap&quot;&gt;Navigating Through a Heap &lt;a class=&quot;direct-link&quot; href=&quot;#navigating-through-a-heap&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;With no logical order to how records are arranged, it becomes harder to locate individual records in a heap. Records are identified by a row ID (RID) which is a pointer made up of the file ID, page ID and slot ID that the record is located on, represented as (FileID:PageID:SlotID).&lt;/p&gt;
&lt;p&gt;To keep track of the location of data pages which belong to a heap table, SQLServer uses a special kind of page known as an Index Allocation Map (IAM) page. An IAM page can manage data of only one heap table, and each heap table in a database is assigned at least one IAM page.&lt;/p&gt;
&lt;p&gt;An IAM page tracks about 4GB of data which is equivalent to ~500K pages. If the size of the heap table data exceeds 4GB, another IAM page is allocated to the heap table to track the next 4GB of data. The first IAM page then contains a pointer to the next one.&lt;/p&gt;
&lt;p&gt;Note that an IAM page does not track individual data pages. Instead, it divides the pages into groups of eight known as &lt;em&gt;extents.&lt;/em&gt; An extent is the smallest unit that an IAM page will track. This abstraction makes it easier to manage the pages.&lt;/p&gt;
&lt;p&gt;To scan a heap table, SQLServer will point to the first IAM page of the table and then scan each data page in each extent that the IAM page tracks.&lt;/p&gt;
&lt;p&gt;The IAM page is useful here because it helps to link the data pages of heap table. As there is no link between the data pages, the IAM page is a way to navigate between the pages in the table.&lt;/p&gt;
&lt;p&gt;To find any row stored in a heap in the absence of an &lt;em&gt;index&lt;/em&gt; (I know I&#39;m getting ahead of myself here), the entire heap table must be scanned row-by-row. This is &lt;em&gt;less than ideal&lt;/em&gt; for a large table.&lt;/p&gt;
&lt;p&gt;In addition, when you make a query for a set of records in a heap table, there is no guaranteed order for the set of results because there is no order to how they are stored.&lt;/p&gt;
&lt;p&gt;Note that records are initially stored in the order of insertion to a table, but they can be moved around within the heap to store them more efficiently (e.g. if a record is updated, and they need more space after an update then they were initially allocated), and so the storage order is unpredictable.&lt;/p&gt;
&lt;p&gt;When a record is moved around in a heap, a &lt;em&gt;forwarding pointer&lt;/em&gt; is usually left behind in its old location. This pointer serves to redirect any references made to the record in its old position to the new position.&lt;/p&gt;
&lt;p&gt;We&#39;ll briefly resume the discussion on Heap Tables once we&#39;ve covered Clustered and Non-Clustered indexes below.&lt;/p&gt;
&lt;h3 id=&quot;indexes&quot;&gt;Indexes &lt;a class=&quot;direct-link&quot; href=&quot;#indexes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So far, we have learned that data rows in your database table are stored as records on disk and organized into data pages. These pages are part of what then make up a table.&lt;/p&gt;
&lt;p&gt;We&#39;ve also briefly covered the heap table as one way that pages are organized for a table. When discussing heaps, we saw that the lack of a logical order in arranging the records on a page (and the pages on the disk) makes it more difficult to search for a particular record in your table. You would have to scan each row one by one.&lt;/p&gt;
&lt;p&gt;Now, what if there was a way to organize your records on a page, and your pages on the disk to make it easy to find a record or group of records? Well of course there is, enter &lt;em&gt;Indexes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;An index in a database is similar to an index at the back of a textbook. If you&#39;re looking for a particular topic in your textbook and you&#39;re not sure of what page it&#39;s covered on, you would use the index at the back of the book because you know it&#39;s sorted alphabetically and it makes finding stuff easier.&lt;/p&gt;
&lt;p&gt;In databases, an index works the same way, except that the &#39;topic&#39; you&#39;re searching for in your textbook is now analogous to a &#39;value&#39; in your database column. When you create an index on a column, you are making all the values on that column ordered in a way that makes it easier to access each one.&lt;/p&gt;
&lt;p&gt;To understand how indexes work, let&#39;s talk about a data structure that is the backbone of many relational databases today known as the &lt;strong&gt;B-Tree.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;b-trees&quot;&gt;B-Trees &lt;a class=&quot;direct-link&quot; href=&quot;#b-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For the unfamiliar, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Tree_(data_structure)&quot;&gt;Tree&lt;/a&gt; is a data structure made up of elements known as &lt;em&gt;nodes.&lt;/em&gt; There is a node at the top known as the &lt;em&gt;root&lt;/em&gt; node. The root node can act as a &lt;em&gt;parent&lt;/em&gt; to other nodes known as its &lt;em&gt;children.&lt;/em&gt; Each child node can then be a parent to other nodes in the tree. This cycle continues until we get to the &lt;em&gt;leaf&lt;/em&gt; nodes. A leaf node is a node without any children. In the diagram below, Node A is the root node and Nodes D, E and F are the leaf nodes in that tree. Nodes B and C are what are known as &lt;em&gt;intermediate&lt;/em&gt; nodes.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/binaryTree.png&quot; alt=&quot;Tree Data Structure&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 2 - Tree Data Structure &lt;sup&gt;[1]&lt;/sup&gt;. &lt;/p&gt;
&lt;p&gt;A B-Tree is a special kind of tree that is widely used in implementing database indexes. Some of the properties which make it specific to this use case are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each node in a B-Tree is a database page (could be a data page or an index page).&lt;/li&gt;
&lt;li&gt;Each page is responsible for keeping track of an ordered range of keys. The keys correspond to different values for a column (or set of columns) that we create an index on and are typically arranged in ascending order.&lt;/li&gt;
&lt;li&gt;A page contains either the row for a specific key in its range or a reference to a child page where the key can be found.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In case you&#39;re wondering, no one knows for sure what the &#39;B&#39; in B-Tree stands for (except maybe the creators).&lt;/p&gt;
&lt;p&gt;Let&#39;s look at the diagram below to make this clearer:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/new-btree.png&quot; alt=&quot;Representation of records in a B-Tree&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 3 - Representation of a records in a B-Tree. &lt;/p&gt;
&lt;p&gt;This diagram is an example representing an index on a &#39;name&#39; column in a table. Suppose you want to retrieve the row which has the column&#39;s value as &#39;Dan&#39;. A search on this tree will proceed as follows:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step One&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Starting from the root page of the tree, we see that it contains the record for the name &#39;Bayo&#39; and references (&lt;em&gt;ref)&lt;/em&gt; to child pages for other records. If we were searching for &#39;Bayo&#39;, the search would end here! But we&#39;re not, and so the search continues.&lt;/p&gt;
&lt;p&gt;An interesting property of this tree is that child pages to the left of the current page contain keys which are less than the smallest key on the current page, and right side child page keys are greater than or equal to the largest key on the current page.&lt;/p&gt;
&lt;p&gt;With that in mind, &#39;Dan&#39; comes after &#39;Bayo&#39; in alphabetical order, and so we look at the right side child page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step Two&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On the next level of the tree, we see that &#39;Ben&#39; and &#39;Dele&#39; are the only name records located on the right side child page of the root. However, the page contains references to the names that come before &#39;Ben&#39;, fall between &#39;Ben&#39; and &#39;Dele&#39;, and come after &#39;Dele&#39;. &#39;Dan&#39; falls between those two names and so we follow the reference to the child page on the final level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step Three&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When we follow that &#39;ref&#39; entry, it leads us to a child page where the &#39;Dan&#39; entry can be found! That marks the end of our search. This child page is known as a &lt;em&gt;leaf&lt;/em&gt; page as it contains no references to other children.&lt;/p&gt;
&lt;p&gt;Now, this is a really simple example that does not cover all the nuances of a B-Tree, but the idea is the same even for more complex examples.&lt;/p&gt;
&lt;p&gt;Some key things to note are that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The root page can have more than one key on it.&lt;/li&gt;
&lt;li&gt;We eliminate half of the possibilities at each level of a search. This is the binary search algorithm in action.&lt;/li&gt;
&lt;li&gt;The advantage of the B-Tree over Heap Tables for searching is that it reduces our worst-case lookup time for a key from O(n) to O(log n). This means that while searching a heap table with a million records could involve a million operations in the worst case, searching a B-Tree with the same number of records will require only about 20 operations at worst.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%22so-how-does-the-index-relate-to-the-actual-data-being-stored%3F%22&quot;&gt;&amp;quot;So how does the index relate to the actual data being stored?&amp;quot; &lt;a class=&quot;direct-link&quot; href=&quot;#%22so-how-does-the-index-relate-to-the-actual-data-being-stored%3F%22&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This was a question that I had after first learning about B-Trees. I wasn&#39;t sure if the indexing structure contained the data itself, or if it was just a pointer to the data stored somewhere else.&lt;/p&gt;
&lt;p&gt;Well, the answer is that it can be either, depending on what type of index is used. Two of the ways in which an indexed database table can be organized are as a &lt;em&gt;clustered index&lt;/em&gt; and/or &lt;em&gt;non-clustered indexes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For a clustered index, the data associated with a key (column value) is stored on the same page as the key. The leaf nodes for a clustered index are always data pages. Once you can access a particular key, you have the information for the rest of the row stored on the same page. In MySQL and SQLServer, the primary key of a table is always a clustered index, and there&#39;s typically only one clustered index per table to avoid duplicating data.&lt;/p&gt;
&lt;p&gt;On the other hand, non-clustered indexes store the key and a &lt;em&gt;pointer&lt;/em&gt; to the underlying data located somewhere else. The leaf nodes here are not data pages, but index pages which contain a pointer for individual rows. This pointer can be to a &lt;em&gt;heap&lt;/em&gt; or to a &lt;em&gt;clustered index.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If the pointer is to a heap, then the pointer is the row identifier(RID) used to locate the record in the heap table.&lt;/p&gt;
&lt;p&gt;This is what makes the &lt;em&gt;forwarding pointer&lt;/em&gt; discussed under the &#39;Heaps&#39; section useful. Imagine that we have 20 references to a particular record in a heap across multiple non-clustered indexes. Without the forwarding pointer, if the record gets moved around, we would have to update the RID across multiple indexes. Fortunately, we can still retain our reference to the old RID in our indexes because of the presence of the forwarding pointer at that location.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the pointer is to a clustered index, then it points to the location of the primary key on the clustered index. MySQL&#39;s InnoDB storage engine and SQLServer point non-clustered indexes to a clustered index.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;write-amplification-in-b-trees&quot;&gt;Write Amplification in B-Trees &lt;a class=&quot;direct-link&quot; href=&quot;#write-amplification-in-b-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For all the advantages of the standard B-Tree over a Heap table, it is not a perfect structure. One disadvantage it has is that one write to the database may involve multiple write operations to the disk.&lt;/p&gt;
&lt;p&gt;To add a new key to a B-Tree, you first need to find the page that holds the range of keys that the key falls in. If the page does not have enough free space to hold the new key, the page is split into two &lt;em&gt;child pages&lt;/em&gt;, and is then updated to hold references to these new pages.&lt;/p&gt;
&lt;p&gt;The fact that one operation may require multiple pages to be overwritten is dangerous because it means that if the database crashes in the middle of overwriting the pages, the database is will now be in an inconsistent state.&lt;/p&gt;
&lt;p&gt;To prevent this inconsistency, B-Tree implementations also include a &lt;em&gt;write-ahead log.&lt;/em&gt; Before any changes are applied to pages in the tree, they must first be written to the durable write-ahead log.&lt;/p&gt;
&lt;p&gt;This event - where one write to the database can lead to multiple writes on disk - is known as &lt;em&gt;write amplification.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;b%2B-trees&quot;&gt;B+ Trees &lt;a class=&quot;direct-link&quot; href=&quot;#b%2B-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Many relational databases today actually use a variant of the B-Tree known as the B+ Tree. The B+ Tree representation of the standard B-Tree in Figure 3 is shown below.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;https://timilearning.com/uploads/bplustree.png&quot; alt=&quot;Representation of records in a B+Tree&quot;&gt; &lt;/p&gt; &lt;p align=&quot;center&quot;&gt; Figure 4 - Representation of a records in a B+ Tree. &lt;/p&gt;
&lt;p&gt;The main differences are these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Only&lt;/em&gt; the leaf nodes in a B+ tree contain data for the records. In a standard B-Tree, an internal/intermediate page may contain both the data for some records as well as references to child nodes. The advantage of not storing any data on the internal nodes is that more keys can fit on each page, which can lead to fewer page splits and reduce the depth of the tree.&lt;/li&gt;
&lt;li&gt;Each leaf page in a B+ Tree is linked to its neighbors.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This has the advantage that doing a full scan of all the records in a tree will only require a linear pass through the leaf nodes. Doing this in a B-Tree will require traversing through all the levels in the tree as each level can contain the data for a record.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The downside of B+ Trees compared to B-Trees is that unlike with B-Trees where you can have an &#39;early exit&#39; if you find a key&#39;s data on an internal page, you would have to traverse through all the levels of a B+ Tree to get to the leaf page which has data for a key. However, I reckon that majority of the keys will be on leaf pages for both trees anyway, which is why most databases opt for the B+ Tree instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We&#39;re not done with the discussion on B-Trees. In the next and final post of the series, we&#39;ll learn about LSM Trees, which are another type of indexing structure commonly used today. We&#39;ll compare them with B-Trees by exploring the pros and cons of each. We&#39;ll briefly learn about other types of indexes and some considerations when choosing indexes for your database table.&lt;/p&gt;
&lt;p&gt;[1] By Victor S.Adamchik, CMU, 2009 - Own work, &lt;a href=&quot;https://www.cs.cmu.edu/~adamchik/15-121/lectures/Trees/trees.html&quot; title=&quot;https://www.cs.cmu.edu/~adamchik/15-121/lectures/Trees/trees.html&quot;&gt;https://www.cs.cmu.edu/~adamchik/15-121/lectures/Trees/trees.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h5 id=&quot;on-heaps&quot;&gt;On Heaps &lt;a class=&quot;direct-link&quot; href=&quot;#on-heaps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dba.stackexchange.com/questions/28370/what-are-valid-usage-scenarios-for-heap-tables&quot;&gt;What are valid usage scenarios for Heap Tables?&lt;/a&gt; - StackExchange Discussion&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.sqlbadpractices.com/heap-tables/&quot;&gt;Heap Tables&lt;/a&gt; by Francois. Useful post on why not to use Heap Tables. The comments section is also interesting as it presents a different view.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/sql/relational-databases/indexes/heaps-tables-without-clustered-indexes?view=sql-server-ver15&quot;&gt;Heaps (Tables without Clustered Indexes)&lt;/a&gt; - Microsoft Docs&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2005/administrator/cc917672(v=technet.10)?redirectedfrom=MSDN&quot;&gt;SQL Server Best Practices Article&lt;/a&gt; by Burzin Patel and Sanjay Mishra. Contains an interesting analysis of the performance differences between heaps and clustered indexes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;on-indexes-and-b-trees&quot;&gt;On Indexes and B-Trees &lt;a class=&quot;direct-link&quot; href=&quot;#on-indexes-and-b-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://dataintensive.net/&quot;&gt;Designing Data-Intensive Applications&lt;/a&gt; by Martin Kleppmann.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.red-gate.com/simple-talk/sql/database-administration/sql-server-storage-internals-101/&quot;&gt;SQL Server Storage Internals 101&lt;/a&gt; by Mark S Rasmussen (Also useful for Heaps)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dba.stackexchange.com/a/23549&quot;&gt;Why we have Non-Clustered Indexes that point to Clustered Indexes &lt;/a&gt; - StackExchange Discussion.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://use-the-index-luke.com/sql/anatomy/the-tree&quot;&gt;Anatomy of an SQL Index&lt;/a&gt; by Markus Winand.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cs.usfca.edu/~galles/visualization/BTree.html&quot;&gt;B-Trees&lt;/a&gt; and &lt;a href=&quot;https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html&quot;&gt;B+ Trees&lt;/a&gt; visualizations - I found these very useful for understanding the topics.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/870218/differences-between-b-trees-and-b-trees&quot;&gt;B-Tree vs B+ Tree&lt;/a&gt; - Useful discussion on StackOverflow.&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 8 - The Trouble with Distributed Systems</title>
		<link href="https://timilearning.com/posts/ddia/part-two/chapter-8/"/>
		<updated>2019-12-26T21:19:03-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-two/chapter-8/</id>
		<content type="html">&lt;p&gt;Notes from Chapter 8 of Martin Kleppmann&#39;s &#39;Designing Data-Intensive Applications&#39; book&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In this chapter, we&#39;ll look at the things that may go wrong in distributed systems. We&#39;ll cover problems with network, clocks and timing issues, and other faults.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#faults-and-partial-failures&quot;&gt;Faults and Partial Failures&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#cloud-computing-and-supercomputing&quot;&gt;Cloud Computing and Supercomputing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#unreliable-networks&quot;&gt;Unreliable Networks&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#network-faults-in-practice&quot;&gt;Network Faults in Practice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#detecting-faults&quot;&gt;Detecting Faults&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#timeouts-and-unbounded-delays&quot;&gt;Timeouts and Unbounded Delays&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#network-congestion-and-queueing&quot;&gt;Network Congestion and Queueing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#_aside%3A_-tcp-vs-udp%28transmission-control-protocol-vs-user-datagram-protocol%29&quot;&gt;&lt;em&gt;Aside:&lt;/em&gt; TCP vs UDP(Transmission Control Protocol vs User-Datagram Protocol)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#synchronous-versus-asynchronous-networks&quot;&gt;Synchronous Versus Asynchronous Networks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#unreliable-clocks&quot;&gt;Unreliable Clocks&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#monotonic-vs-time-of-day-clocks&quot;&gt;Monotonic vs Time-of-Day Clocks&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#time-of-day-clocks&quot;&gt;Time-of-day clocks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#monotonic-clocks&quot;&gt;Monotonic Clocks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#clock-synchronization-and-accuracy&quot;&gt;Clock Synchronization and Accuracy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#relying-on-synchronized-clocks&quot;&gt;Relying on Synchronized Clocks&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#timestamps-for-ordering-events.&quot;&gt;Timestamps for ordering events.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#clock-readings-have-a-confidence-interval&quot;&gt;Clock readings have a confidence interval&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#synchronized-clocks-for-global-snapshots&quot;&gt;Synchronized clocks for global snapshots&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#process-pauses&quot;&gt;Process Pauses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#knowledge%2C-truth%2C-and-lies&quot;&gt;Knowledge, Truth, and Lies&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-truth-is-defined-by-the-majority&quot;&gt;The Truth is Defined by the Majority&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-leader-and-the-lock&quot;&gt;The Leader and the Lock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#fencing-tokens&quot;&gt;Fencing tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#byzantine-faults&quot;&gt;Byzantine Faults&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#weak-forms-of-lying&quot;&gt;Weak forms of lying&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#system-model-and-reality&quot;&gt;System Model and Reality&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#system-model-for-timing-assumptions&quot;&gt;System Model for Timing Assumptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#system-model-for-node-failures&quot;&gt;System Model for Node Failures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#correctness-of-an-algorithm&quot;&gt;Correctness of an algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#safety-and-liveness&quot;&gt;Safety and liveness&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mapping-system-models-to-the-real-world&quot;&gt;Mapping system models to the real world&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;faults-and-partial-failures&quot;&gt;Faults and Partial Failures &lt;a class=&quot;direct-link&quot; href=&quot;#faults-and-partial-failures&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Distributed systems differ from single node computers in that: unlike single node computers where either the system is completely working or completely broken, we can have partial failures in distributed systems.&lt;/p&gt;
&lt;p&gt;What makes partial failures more difficult to deal with is that they are &lt;em&gt;nondeterministic.&lt;/em&gt; It may sometimes work, and sometimes fail.&lt;/p&gt;
&lt;h4 id=&quot;cloud-computing-and-supercomputing&quot;&gt;Cloud Computing and Supercomputing &lt;a class=&quot;direct-link&quot; href=&quot;#cloud-computing-and-supercomputing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We have high-performance computing (HPC) and cloud computing on both extremes of philosophies for building large-scale applications.&lt;/p&gt;
&lt;p&gt;High performance computers or Supercomputers have thousands of CPUs used for computationally expensive tasks like weather forecasting. In general, a job will checkpoint the state of its computation and store it durably from time to time. If a node fails, the whole cluster is brought down. The state of computation is restarted from the last checkpoint. This makes supercomputers similar to single node computers.&lt;/p&gt;
&lt;p&gt;Nowadays, many internet services need high availability. It&#39;s not acceptable to bring down the cluster due to failure in a node.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;To make distributed systems work, we must endeavor to build a reliable system from unreliable components.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We sometimes like to assume that faults are rare and then don&#39;t account for them, but we must design with fault tolerance in mind.&lt;/p&gt;
&lt;p&gt;Building a reliable system from unreliable components is not a unique idea to distributed systems, and is used in other areas as well. E.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In information theory, the idea is to have reliable communication over an unreliable channel. We achieve this using error correcting codes.&lt;/li&gt;
&lt;li&gt;In networking, IP is unreliable as it may drop, delay, duplicate, or reorder packets. TCP provides a more reliable layer on top of that as it re-transmits missing packets, eliminates duplicates, and reassembles packets in the right order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that even though a system can be more reliable than the parts that it&#39;s made of, there&#39;s a limit to the level of reliability that can be attained. Error-correcting codes can only deal with a number of single-bit errors and TCP cannot remove delays in the network.&lt;/p&gt;
&lt;h3 id=&quot;unreliable-networks&quot;&gt;Unreliable Networks &lt;a class=&quot;direct-link&quot; href=&quot;#unreliable-networks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As stated earlier, this book focuses on shared-nothing systems which communicate with each other via a network. The advantage of this approach is that it is comparatively cheap, as it requires no special hardware. We can have a bunch of regular machines as part of the system.&lt;/p&gt;
&lt;p&gt;Note that the internet and most internal networks in datacenters are &lt;em&gt;asynchronous packet networks.&lt;/em&gt; This means that: one node can send a message to another node, but have no guarantee about when the message will arrive, or whether the message will actually arrive at all. Unfortunately, with this approach, many things could go wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The request may have been lost. E.g. The network cable might have been unplugged.&lt;/li&gt;
&lt;li&gt;The remote node may have successfully processed the request, but then the response got lost on the network. E.g. a misconfigured network switch.&lt;/li&gt;
&lt;li&gt;The remote node may have failed.&lt;/li&gt;
&lt;li&gt;The request may be waiting in a queue to be delivered later. E.g if the network or recipient is overloaded.&lt;/li&gt;
&lt;li&gt;The response from the remote node may have been delayed or will be delivered later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In essence, the sender is unable to tell whether the packet was delivered unless it receives a response message from the recipient. It&#39;s impossible to distinguish these issues in an asynchronous network.&lt;/p&gt;
&lt;p&gt;These issues are typically handled with a &lt;em&gt;timeout&lt;/em&gt;, but that still gives no information about the state of the request.&lt;/p&gt;
&lt;h4 id=&quot;network-faults-in-practice&quot;&gt;Network Faults in Practice &lt;a class=&quot;direct-link&quot; href=&quot;#network-faults-in-practice&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Although network faults may be rare, the fact that they can happen means that software needs to be able to handle them. Handling network faults does not necessarily mean &lt;em&gt;tolerating&lt;/em&gt; them, a simple approach can just be to show an error message to users. However, there has to be work done to know how the software reacts to network problems, and also ensure that the system can recover from them.&lt;/p&gt;
&lt;p&gt;It might be a good idea to deliberately trigger network problems and test the system&#39;s response - Chaos Monkey.&lt;/p&gt;
&lt;h4 id=&quot;detecting-faults&quot;&gt;Detecting Faults &lt;a class=&quot;direct-link&quot; href=&quot;#detecting-faults&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It&#39;s important to automatically detect network faults, as they might be linked to faulty nodes. Detecting faults quickly ensures that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A load balancer can stop sending requests to a dead node.&lt;/li&gt;
&lt;li&gt;A new follower can be promoted to a leader if the leader fails in a single-leader replication.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Due to the uncertainty about the network, it&#39;s difficult to tell whether a node is working or not. There are some specific ways to tell though, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the machine on which the node is running is reachable, but no process is listening on the destination port (e.g. because the process crashed), the OS will close or refuse TCP connections. However, if the node crashed while processing the request, there&#39;s no way of knowing how much data was processed by the remote node.&lt;/li&gt;
&lt;li&gt;If you have access to the management interface of the network switches in your datacenter, they can be queried to detect link failures at hardware level. Of course this is not applicable if you&#39;re connecting over the internet or have no access to the datacenter.&lt;/li&gt;
&lt;li&gt;In a situation where a process on a node is crashed, but the node&#39;s OS is still running, there can be a script which notifies other nodes when a node has crashed. This will allow another node to take over. This approach is used in Hbase.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;timeouts-and-unbounded-delays&quot;&gt;Timeouts and Unbounded Delays &lt;a class=&quot;direct-link&quot; href=&quot;#timeouts-and-unbounded-delays&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We have mentioned that timeouts are often used to detect a fault. However, there is no simple answer too how long a timeout should be. It simply depends.&lt;/p&gt;
&lt;p&gt;With a long timeout, it means there can be a wait until a node is declared dead. This means users will have to wait a while or see error messages.&lt;/p&gt;
&lt;p&gt;On the other hand, a short timeout means that nodes can be declared dead prematurely, even when it only suffers a temporary breakdown (e.g. due to a load spike on the node or the network). There are downsides of declaring a node dead prematurely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Its responsibilities need to be transferred to other nodes, which can place additional load on the other nodes and the network. This can lead to a cascading failure as other nodes can become slow to respond.&lt;/li&gt;
&lt;li&gt;If the node is in the middle of performing an action and another node takes over, the action may be performed twice.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In an ideal system, we could have a guarantee that the maximum delay for packets transmission will be &lt;em&gt;d&lt;/em&gt;, and the node always handles a response within time &lt;em&gt;r&lt;/em&gt;. In this kind of system, we could set a timeout for 2d + r and it&#39;ll be reasonable.&lt;/p&gt;
&lt;p&gt;However, in most systems, we do not have either of those guarantees. Asynchronous networks have &lt;em&gt;unbounded delays.&lt;/em&gt;&lt;/p&gt;
&lt;h5 id=&quot;network-congestion-and-queueing&quot;&gt;Network Congestion and Queueing &lt;a class=&quot;direct-link&quot; href=&quot;#network-congestion-and-queueing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Queueing is the most common cause of the variability of network packet delays (i.e. unbounded delays). Queues can be formed at different points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If several nodes try to send packets to the same destination, the packets must be queued up by the network switch and fed into the destination network link one by one. On a busy network link, a packet may have to wait a while before it can get a slot. A packet can even get dropped if the switch queue fills up, and it needs to be resent. This can happen even if the network is functioning properly.&lt;/li&gt;
&lt;li&gt;When a packet reaches its destination node, if all the CPU cores are currently too busy to handle the request, the request needs to be queued by the operating system until it can handle it.&lt;/li&gt;
&lt;li&gt;TCP performs &lt;em&gt;flow control&lt;/em&gt;, where a node limits its rate of sending to avoid overloading a network link or the receiving node. This means additional queueing at the sender even before the data enters the network.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;aside%3A-tcp-vs-udp(transmission-control-protocol-vs-user-datagram-protocol)&quot;&gt;&lt;em&gt;Aside:&lt;/em&gt; TCP vs UDP(Transmission Control Protocol vs User-Datagram Protocol) &lt;a class=&quot;direct-link&quot; href=&quot;#aside%3A-tcp-vs-udp(transmission-control-protocol-vs-user-datagram-protocol)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;TCP is a reliable network transmission protocol while UDP is unreliable. It means that TCP implements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flow Control&lt;/li&gt;
&lt;li&gt;Acknowledgement and Retransmission&lt;/li&gt;
&lt;li&gt;Sequencing: Ensuring that messages arrive in the right order even if packets are dropped.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any messages not acknowledged will be retransmitted in TCP.&lt;/p&gt;
&lt;p&gt;UDP is used in latency-sensitive applications like videoconferencing and Voice over IP, where there&#39;s less tolerance for delays. In UDP, delayed data is probably worthless so it does not try to retransmit it. E.g. in phone calls, instead of retransmitting, it simply fills the missing packet&#39;s time slot with silence. The retry happens at the human layer: &amp;quot;Could you repeat that please?&amp;quot;.&lt;/p&gt;
&lt;p&gt;In essence, timeouts should typically be chosen experimentally: measure the distribution of network round trip times over an extended period, and over many machines to determine the expected variability of delays.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Phi Accrual failure detector&lt;/em&gt; used in Akka and Cassandra measure response times and automatically adjust timeouts based on the observed response time distribution.&lt;/p&gt;
&lt;h4 id=&quot;synchronous-versus-asynchronous-networks&quot;&gt;Synchronous Versus Asynchronous Networks &lt;a class=&quot;direct-link&quot; href=&quot;#synchronous-versus-asynchronous-networks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A question that you might have is: &lt;em&gt;why don&#39;t we make the network reliable at a hardware level so the software does not need to worry about it?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To address this, it&#39;s worth looking at the traditional fixed-line telephone network (non-cellular, non-VoIP) which is apparently very reliable and rarely drops messages.&lt;/p&gt;
&lt;p&gt;The way it works is that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a call is made over the network, it creates a &lt;em&gt;circuit.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;This circuit has a fixed, guaranteed bandwidth for the call which remains in place until the call ends.&lt;/li&gt;
&lt;li&gt;This network is synchronous, and it does not suffer from queueing, since the required amount of space for the call has already been reserved. Because there is no queueing, it has a &lt;em&gt;bounded delay.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that this approach differs from a TCP connection. While there is a fixed amount of reserved bandwidth here that no one else can use while the circuit is established, TCP packets will typically grab whatever network bandwidth is available.&lt;/p&gt;
&lt;p&gt;Datacenter networks and the internet make use of the TCP approach of packet switching rather than establishing circuits, because they are optimizing for &lt;em&gt;bursty traffic.&lt;/em&gt; Unlike an audio or video call where the number of bits transferred per second is fairly constant, the traffic through the internet is unpredictable. We could be requesting a web page, or sending an email, or transferring a file etc. The goal is to just complete it as quickly as possible.&lt;/p&gt;
&lt;p&gt;Using circuits for bursty data transfer will waste network capacity and could make transfers unnecessarily slow, as we would have to guess how much bandwidth to allocate beforehand. TCP dynamically adapts the data transfer rate to the available network capacity.&lt;/p&gt;
&lt;p&gt;There&#39;s ongoing research to use &lt;em&gt;quality of service&lt;/em&gt; and &lt;em&gt;admission control&lt;/em&gt; to emulate circuit switching on packet networks, or provide statistically bounded delays.&lt;/p&gt;
&lt;h3 id=&quot;unreliable-clocks&quot;&gt;Unreliable Clocks &lt;a class=&quot;direct-link&quot; href=&quot;#unreliable-clocks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Clocks and time are important in distributed systems. Applications use clocks to answer questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Has a request timed out yet?&lt;/li&gt;
&lt;li&gt;When was a request received?&lt;/li&gt;
&lt;li&gt;How long did a user spend on a site?&lt;/li&gt;
&lt;li&gt;When does a cache entry expire? Etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some questions measure &lt;em&gt;duration&lt;/em&gt;, while some describe &lt;em&gt;points in time.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Time is tricky because each machine on a network has its own clock, and some may be faster or slower than others. Clocks can be synchronized to a degree though, by using the Network Time Protocol (NTP). It works by adjusting clocks using time reported from a group of servers. The group of servers get their time from a GPS receiver.&lt;/p&gt;
&lt;h4 id=&quot;monotonic-vs-time-of-day-clocks&quot;&gt;Monotonic vs Time-of-Day Clocks &lt;a class=&quot;direct-link&quot; href=&quot;#monotonic-vs-time-of-day-clocks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Modern computers have at least two different kinds of clocks: a &lt;em&gt;time-of-day clock&lt;/em&gt; and a &lt;em&gt;monotonic clock.&lt;/em&gt;&lt;/p&gt;
&lt;h5 id=&quot;time-of-day-clocks&quot;&gt;Time-of-day clocks &lt;a class=&quot;direct-link&quot; href=&quot;#time-of-day-clocks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;These are like standard clocks, which just return the current date and time according to a calendar. These clocks are typically synchronized with NTP, which means that timestamps should match across machine ideally.&lt;/p&gt;
&lt;p&gt;Note that if the local clock is too far ahead of NTP, it may appear to jump back to a previous point in time. It could also jump due to leap seconds. The tendency of these clocks to jump make them unsuitable for measuring elapsed time.&lt;/p&gt;
&lt;h5 id=&quot;monotonic-clocks&quot;&gt;Monotonic Clocks &lt;a class=&quot;direct-link&quot; href=&quot;#monotonic-clocks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;These clocks are suitable for measuring a duration like a timeout or response time. They are guaranteed to move forward in time (unlike a time-of-day clock which may jump back in time).&lt;/p&gt;
&lt;p&gt;System.nanoTime() in Java is a monotonic clock. With a monotonic clock, you can check the value at a point in time, perform an action, then check the value again and then use the difference between the two values to measure time elapsed between the two checks.&lt;/p&gt;
&lt;p&gt;Monotonic clocks are fine for measuring elapsed time, because they do not assume any synchronization between nodes&#39; clocks.&lt;/p&gt;
&lt;h4 id=&quot;clock-synchronization-and-accuracy&quot;&gt;Clock Synchronization and Accuracy &lt;a class=&quot;direct-link&quot; href=&quot;#clock-synchronization-and-accuracy&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Unlike monotonic clocks which don&#39;t need synchronization, time-of-day clocks must be synchronized with an NTP server or another external time source. However, NTP and hard clocks are not as reliable or accurate as one might hope. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The quartz clock in a computer isn&#39;t very accurate. It may go faster or slower than it should. It can vary depending on the temperature of the machine. Basically, it &lt;em&gt;drifts.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;If the computer&#39;s clock drifts too much from an NTP server, it may refuse to synchronize, or the computer&#39;s clock will be forcibly reset. Any application that&#39;s observing this app may see time go backward or jump forward.&lt;/li&gt;
&lt;li&gt;NTP synchronization is only as good as the network delay, so there&#39;s a limit to accuracy on a congested network with variable packet delays. An &lt;a href=&quot;https://iopscience.iop.org/0143-0807/23/4/103/&quot;&gt;experiment&lt;/a&gt; showed that only a minimum error of 35ms is achievable when synchronizing over the internet, though the error can be up to a second when we have occasional spikes in network delay.&lt;/li&gt;
&lt;li&gt;Leap seconds will lead to a minute that is 59 seconds or 61 seconds long. If a system isn&#39;t designed to handle leap seconds, this can lead to a crash. (&lt;a href=&quot;http://www.somebits.com/weblog/tech/bad/leap-second-2012.html&quot; title=&quot;http://www.somebits.com/weblog/tech/bad/leap-second-2012.html&quot;&gt;http://www.somebits.com/weblog/tech/bad/leap-second-2012.html&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The hardware clock is virtualized in virtual machines. When multiple virtual machines share a CPU core, each VM is paused for tens of milliseconds while another VM is running. For an application running on a VM, it can look like the clock suddenly jumped forward.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nevertheless, it&#39;s possible to achieve very good clock accuracy with significant investment into resources. For example, the MiFID II European regulation for financial companies mandates that HFT funds synchronize their clocks within 100 microseconds of UTC, to help debug market anomalies like &amp;quot;flash crashes&amp;quot;.&lt;/p&gt;
&lt;h4 id=&quot;relying-on-synchronized-clocks&quot;&gt;Relying on Synchronized Clocks &lt;a class=&quot;direct-link&quot; href=&quot;#relying-on-synchronized-clocks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;While clocks may seem simple and straightforward, they have a good number of pitfalls. Some of the issues that may arise are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Time of day clocks can move backward in time&lt;/li&gt;
&lt;li&gt;A day may not exactly have 86400 seconds&lt;/li&gt;
&lt;li&gt;The time on a node may differ from the time on another.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Like with unreliable networks, robust software must be prepared to deal with incorrect clocks. Dealing with incorrect clocks can be even trickier because the problems caused by this can easily go unnoticed. A faulty CPU or misconfigured network is easier to detect, as the system would not work at all. However, for a defective clock, things will generally look fine. We&#39;re more likely to experience silent and subtle data loss than a dramatic crash.&lt;/p&gt;
&lt;p&gt;Therefore, if a software requires synchronized clocks, it&#39;s essential to monitor the clock offsets between all machines. A node whose clock drifts too far from the others should be labelled as a dead node and removed from the cluster.&lt;/p&gt;
&lt;h5 id=&quot;timestamps-for-ordering-events.&quot;&gt;Timestamps for ordering events. &lt;a class=&quot;direct-link&quot; href=&quot;#timestamps-for-ordering-events.&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Time-of-day clocks are commonly used for ordering events in some systems and they often use the &lt;em&gt;last write wins&lt;/em&gt; conflict resolution strategy. Some of these systems are Cassandra and Riak, typically multi-leader replication and leaderless databases. Some implementations of this generate the timestamp on the client&#39;s side rather than on the server, but this does not change the problems of LWW which include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Writes can mysteriously disappear.&lt;/li&gt;
&lt;li&gt;It&#39;s impossible to distinguish between concurrent writes and causal writes (where one write depends on another)&lt;/li&gt;
&lt;li&gt;Two nodes can independently generate writes with the same timestamp.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Q&lt;/em&gt;: Could NTP synchronization be made accurate enough that such incorrect orderings cannot occur?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A&lt;/em&gt;: Probably not. NTP&#39;s synchronization accuracy is also limited by the network round-trip time, in addition to other error sources like quartz drift.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Logical clocks&lt;/em&gt; are a safer alternative for ordering events than an oscillating quartz crystal. They measure the relative ordering of events, rather than actual elapsed time which &lt;em&gt;physical clocks&lt;/em&gt; (like time-of-day and monotonic clocks).&lt;/p&gt;
&lt;h5 id=&quot;clock-readings-have-a-confidence-interval&quot;&gt;Clock readings have a confidence interval &lt;a class=&quot;direct-link&quot; href=&quot;#clock-readings-have-a-confidence-interval&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Clock readings typically have an uncertainty range, like a margin of error. However, most systems don&#39;t expose this uncertainty. An exception to this is Google&#39;s TrueTime API which is used in Spanner, and gives a confidence interval on the local clock.&lt;/p&gt;
&lt;h5 id=&quot;synchronized-clocks-for-global-snapshots&quot;&gt;Synchronized clocks for global snapshots &lt;a class=&quot;direct-link&quot; href=&quot;#synchronized-clocks-for-global-snapshots&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Snapshot isolation is commonly implemented by giving each transaction a montonically increasing ID. If a write happened later than the snapshot (i.e. it has a transaction ID greater than the snapshot), the write is invisible to the snapshot transaction. This is easier to manage on a single-node database, as we can use a simple counter.&lt;/p&gt;
&lt;p&gt;For a distributed database though, it is more difficult to coordinate a monotonically increasing transaction ID. The transaction ID must reflect causality. If transaction B reads a value written by transaction A, B must have a higher transaction ID than A for it to be consistent.&lt;/p&gt;
&lt;p&gt;If we didn’t have uncertainty about clock accuracy, the timestamps from the synchronized time-of-day clocks would be suitable as transaction IDs as later transactions will have a higher timestamp.&lt;/p&gt;
&lt;p&gt;However, Google&#39;s Spanner implements snapshot isolation across datacenters this way:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Spanner implements snapshot isolation across datacenters in this way. It uses the clock’s confidence interval as reported by the TrueTime API, and is based on the following observation: if you have two confidence intervals, each consisting of an earliest and latest possible timestamp (A = [A&lt;sub&gt;earliest&lt;/sub&gt;, A&lt;sub&gt;latest&lt;/sub&gt;] and B = [B&lt;sub&gt;earliest&lt;/sub&gt;, B&lt;sub&gt;latest&lt;/sub&gt;]), and those two intervals do not overlap (i.e., A&lt;sub&gt;earliest&lt;/sub&gt;&amp;lt; A&lt;sub&gt;latest&lt;/sub&gt; &amp;lt; B&lt;sub&gt;earliest&lt;/sub&gt; &amp;lt; B&lt;sub&gt;latest&lt;/sub&gt;), then B definitely happened after A — there can be no doubt. Only if the intervals overlap are we unsure in which order A and B happened.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 7547-7554). O&#39;Reilly Media. Kindle Edition.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To ensure that transaction timestamps reflect causality, Spanner waits for the length of the confidence interval before committing a read-write transaction. This means that any transaction that reads the data is at a sufficiently later time, so the confidence intervals do not overlap. For example, if the confidence interval is 7ms, a read-write transaction will wait for 7ms before committing. Remember that with snapshot isolation, a transaction can&#39;t read anything that wasn&#39;t committed when it started. Therefore, we can be sure that any transaction that reads the now committed read-write transaction happened at a sufficiently later time.&lt;/p&gt;
&lt;p&gt;To keep the wait time as small as possible, Google uses a GPS receiver in each datacenter, which allows clocks to be synchronized within about 7ms.&lt;/p&gt;
&lt;h4 id=&quot;process-pauses&quot;&gt;Process Pauses &lt;a class=&quot;direct-link&quot; href=&quot;#process-pauses&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A node in a distributed system must assume that its execution can be paused for a significant amount of time at any point, even in the middle of a function. When this pause happens, the rest of the system keeps moving and may declare the paused node dead because it&#39;s not responding. This paused node may eventually continue running, without noticing that it was asleep until it checks the clock later.&lt;/p&gt;
&lt;p&gt;A distributed system must tailor for these pauses which can be caused by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Garbage collectors which stop all running threads.&lt;/li&gt;
&lt;li&gt;In virtualized environments, a VM can be suspended and resumed e.g. for live migration of a VM from one host to another without a reboot. Suspending the VM means pausing the execution of all processes and saving memory contents to disk. Resuming it means restoring the memory contents and continuing execution.&lt;/li&gt;
&lt;li&gt;On laptops, the execution of a process could be paused and resumed arbitrarily e.g. when a user closes their laptop lid.&lt;/li&gt;
&lt;li&gt;IO operations could also lead to delays.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&#39;s active research into limiting the impact of Garbage Collection pauses. Some of the options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Treat GC pauses like brief planned node outages. When a node requires a GC pause, the runtime can warn the application and stop sending requests to that node. It could also wait for it to process outstanding requests and perform GC while no requests are in progress. This hides the GC pauses from clients and reduces high percentiles of response times. This approach is used in some latency-sensitive financial trading systems (&lt;a href=&quot;https://cdn2.hubspot.net/hubfs/1624455/Website_2016/Content/Whitepapers/Cinnober%20on%20GC%20pause%20free%20Java%20applications.pdf&quot; title=&quot;https://cdn2.hubspot.net/hubfs/1624455/Website_2016/Content/Whitepapers/Cinnober%20on%20GC%20pause%20free%20Java%20applications.pdf&quot;&gt;https://cdn2.hubspot.net/hubfs/1624455/Website_2016/Content/Whitepapers/Cinnober%20on%20GC%20pause%20free%20Java%20applications.pdf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Use the GC only for short-lived objects that are fast to collect, and restart processes periodically, before they accumulate enough long-lived objects to requires a full GC of long-lived objects. &lt;em&gt;A node can be restarted at a time, and traffic can be shifted away from the node before the planned restart, like in a rolling upgrade.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These options can&#39;t fully prevent GC pauses, but they can reduce their impact on the application.&lt;/p&gt;
&lt;h3 id=&quot;knowledge%2C-truth%2C-and-lies&quot;&gt;Knowledge, Truth, and Lies &lt;a class=&quot;direct-link&quot; href=&quot;#knowledge%2C-truth%2C-and-lies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So far, we have discussed some of the distributed systems problems that can occur, which include: unreliable networks, unreliable clocks, faulty nodes, processing pauses etc. We&#39;ve also discussed how distributed systems differ from programs running on a single node: there&#39;s no shared memory, there&#39;s only message passing via an unreliable network with variable delays.&lt;/p&gt;
&lt;p&gt;As a result of these issues, a node in a distributed system cannot &lt;em&gt;know&lt;/em&gt; anything for sure. It can only guess based on the messages it receives (or doesn&#39;t receive) via the network. There has to be a consensus.&lt;/p&gt;
&lt;p&gt;In this section, we&#39;ll explore the concept of knowledge and truth, and guarantees we can provide under certain assumptions in a distributed system.&lt;/p&gt;
&lt;h4 id=&quot;the-truth-is-defined-by-the-majority&quot;&gt;The Truth is Defined by the Majority &lt;a class=&quot;direct-link&quot; href=&quot;#the-truth-is-defined-by-the-majority&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A node cannot trust its assessment of a situation. A node may think it&#39;s the leader, while the other nodes have elected a new one; it may think it&#39;s alive, while other nodes have declared it dead. As a result, many distributed algorithms rely on a &lt;em&gt;quorum&lt;/em&gt; for making decisions i.e. decisions require a minimum number of votes from several nodes in order to reduce dependence on a single node.&lt;/p&gt;
&lt;p&gt;The quorum is typically an absolute majority of more than half the nodes. This is typically safe because there can only be one majority in a system at a time.&lt;/p&gt;
&lt;h5 id=&quot;the-leader-and-the-lock&quot;&gt;The Leader and the Lock &lt;a class=&quot;direct-link&quot; href=&quot;#the-leader-and-the-lock&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;A system often requires there to only be one of a particular thing. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only one leader for a database partition.&lt;/li&gt;
&lt;li&gt;Only one transaction is allowed to hold the lock for a particular resource or object.&lt;/li&gt;
&lt;li&gt;Only one user is allowed to register a particular username.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Due to the fact that a node can believe it’s the &amp;quot;chosen one&amp;quot; even when it isn&#39;t, the system must be designed to handle such situations and avoid problems like split-brain.&lt;/p&gt;
&lt;h5 id=&quot;fencing-tokens&quot;&gt;Fencing tokens &lt;a class=&quot;direct-link&quot; href=&quot;#fencing-tokens&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;One of the ways by which systems handle a situation where a node is under a false belief of being &amp;quot;the chosen one&amp;quot;, thereby disrupting the rest of the system, is by using fencing tokens.&lt;/p&gt;
&lt;p&gt;Basically, each time a lock server grants a lock or a lease, it also generates a fencing token (a number that increases every time a lock is granted). We can then require that any client which wants to send a write request to the storage service must include the current fencing token.&lt;/p&gt;
&lt;p&gt;The lock server will then perform validation on any request with the fencing token included and reject it if it has generated a fencing token with a higher number.&lt;/p&gt;
&lt;p&gt;For applications using ZooKeeper as a lock service, the transaction ID zxid or node version cversion can be used as the fencing token, since they are guaranteed to be monotonically increasing - which is a required property for a fencing token.&lt;/p&gt;
&lt;h4 id=&quot;byzantine-faults&quot;&gt;Byzantine Faults &lt;a class=&quot;direct-link&quot; href=&quot;#byzantine-faults&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Fencing tokens can help detect and block a node that is not deliberately acting in error (e.g. because it hasn&#39;t yet realized that its lease has expired). However, for a node that is deliberately acting in error, it could simply send messages with a fake fencing token.&lt;/p&gt;
&lt;p&gt;In this book, nodes are assumed to be unreliable but honest: any node that &lt;em&gt;does&lt;/em&gt; respond is assumed to be telling the truth to the bests of its knowledge.&lt;/p&gt;
&lt;p&gt;If there&#39;s a risk that nodes may &amp;quot;lie&amp;quot; (e.g. by sending corrupted messages or faulty responses), it becomes a much harder problem to deal with. That behavior is known as a &lt;em&gt;Byzantine fault&lt;/em&gt; and systems that are designed to handle these faults are &lt;em&gt;Byzantine Fault Tolerant Systems.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A system is Byzantine fault-tolerant if it continues to operate correctly even if some of the nodes are malfunctioning and not obeying the protocol, or if malicious attackers are interfering with the network.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 7812-7813). O&#39;Reilly Media. Kindle Edition.&lt;/p&gt;
&lt;p&gt;Dealing with Byzantine faults is relevant in specific circumstances like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In an aerospace environment, data in a computer&#39;s memory may become corrupted due to radiation, leading it to respond to other nodes in unpredictable ways. The system has to be equipped to handle this to prevent plane crashes. Therefore, flight control systems must tolerate Byzantine faults.&lt;/li&gt;
&lt;li&gt;In a system with multiple participating organizations (e.g peer-to-peer networks like Bitcoin), some participants may attempt to cheat or defraud others. It&#39;s not safe to simply trust another node&#39;s messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In most server-side data systems, however, the cost of deploying Byzantine fault-tolerant solutions makes them not practical. Web applications need some other controls though to prevent malicious behavior and that&#39;s why: input validation, sanitization and output escaping are important.&lt;/p&gt;
&lt;h5 id=&quot;weak-forms-of-lying&quot;&gt;Weak forms of lying &lt;a class=&quot;direct-link&quot; href=&quot;#weak-forms-of-lying&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;There are weaker forms of &amp;quot;lying&amp;quot; which are not full-blown Byzantine faults that we can protect against. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network packets get corrupted due to hardware issues or bugs in the OS. These are usually caught by the checksums built into TCP and UDP, but they sometimes evade detection. Checksums in the application-level protocol is a simple measure that can provide protection against such corruption.&lt;/li&gt;
&lt;li&gt;A publicly accessible application must carefully sanitize all inputs from users.&lt;/li&gt;
&lt;li&gt;NTP clients can be synchronized with multiple server addresses, instead of just one server. This makes NTP more robust. One erroneous server among multiple good ones will have minimal impact compared to if it was the only server.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;system-model-and-reality&quot;&gt;System Model and Reality &lt;a class=&quot;direct-link&quot; href=&quot;#system-model-and-reality&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When coming up with distributed systems algorithms, we need to write them in a way that doesn&#39;t depend too much on the hardware and software details. Basically, we need an abstraction for what the algorithm may assume, and the types of faults that we can expect in a system. This abstraction is known as a &lt;em&gt;system model.&lt;/em&gt;&lt;/p&gt;
&lt;h5 id=&quot;system-model-for-timing-assumptions&quot;&gt;System Model for Timing Assumptions &lt;a class=&quot;direct-link&quot; href=&quot;#system-model-for-timing-assumptions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Synchronous model:&lt;/em&gt; In this model, we assume that there&#39;s a bounded network delay, bounded process pause, and bounded clock error. That is, although there might be errors or delays, it will never exceed a fixed upper bound.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Partially synchronous model:&lt;/em&gt; This model assumes that the system behaves like a synchronous model &lt;em&gt;most of the time,&lt;/em&gt; but sometimes exceeds the bounds for network delay, process pauses and pause drifts. This is the &lt;strong&gt;most realistic&lt;/strong&gt; model for timing assumptions, since many systems work correctly most of the time but occasionally exceed the upper bound.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Asynchronous model:&lt;/em&gt; Here, the system is not allowed to make any timing assumptions. It does not have a clock, and so it doesn&#39;t use timeouts. This model is very restrictive.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;system-model-for-node-failures&quot;&gt;System Model for Node Failures &lt;a class=&quot;direct-link&quot; href=&quot;#system-model-for-node-failures&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Crash-stop faults:&lt;/em&gt; This model assumes that a node can fail only by crashing, and the node never comes back. That is, once it stops responding, its gone forever.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Crash-recovery faults:&lt;/em&gt; This model assumes that nodes may crash at any moment, but also start responding again after some unknown time. Nodes here are assumed to have stable storage that gets preserved across crashes, and the in-memory state is assumed to be lost.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Byzantine faults:&lt;/em&gt; Nodes may do anything, including trying to trick other nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For modeling real system, the most useful model is generally the partially synchronous model with crash-recovery faults.&lt;/p&gt;
&lt;h5 id=&quot;correctness-of-an-algorithm&quot;&gt;Correctness of an algorithm &lt;a class=&quot;direct-link&quot; href=&quot;#correctness-of-an-algorithm&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;For an algorithm to be &lt;em&gt;correct,&lt;/em&gt; it must have certain &lt;em&gt;properties.&lt;/em&gt; E.g. For a sorting algorithm to be correct, it must have certain properties expected of its output. Such as that the element further to the left is smaller than the element further to the right.&lt;/p&gt;
&lt;p&gt;Likewise, we can define properties for what it means for a distributed algorithm to be correct. For example, for generating fencing tokens, the algorithm may be required to satisfy the following properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Uniqueness:&lt;/em&gt; No two requests for a fencing token must return the same value.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Monotonic sequence:&lt;/em&gt; It must always increase.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Availability:&lt;/em&gt; A node that requests a fencing token and doesn&#39;t crash must eventually receive a response.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&#39;s possible for some properties to hold, while others don&#39;t. How do we know which distinguish between which properties must hold and which could tolerate caveats? The next section helps to answer that.&lt;/p&gt;
&lt;h5 id=&quot;safety-and-liveness&quot;&gt;Safety and liveness &lt;a class=&quot;direct-link&quot; href=&quot;#safety-and-liveness&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;There are two different kinds of properties that we can distinguish between: &lt;em&gt;safety&lt;/em&gt; and &lt;em&gt;liveness&lt;/em&gt; properties. In the example above for a fencing token, &lt;em&gt;uniqueness&lt;/em&gt; and &lt;em&gt;monotonic sequence&lt;/em&gt; are safety properties, while &lt;em&gt;availability&lt;/em&gt; is a liveness property.&lt;/p&gt;
&lt;p&gt;Safety properties are informally defined as: &lt;em&gt;nothing bad happens&lt;/em&gt;, while liveness properties are defined as &lt;em&gt;something good eventually happens.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;These informal definitions are subjective (what&#39;s good or bad, really) and it’s best not to read too much into them.&lt;/p&gt;
&lt;p&gt;The actual definitions of safety and liveness are precise and mathematical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;If a safety property is violated, we can point at a particular point in time at which it was broken (for example, if the uniqueness property was violated, we can identify the particular operation in which a duplicate fencing token was returned). After a safety property has been violated, the violation cannot be undone — the damage is already done.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;A liveness property works the other way round: it may not hold at some point in time (for example, a node may have sent a request but not yet received a response), but there is always hope that it may be satisfied in the future (namely by receiving a response).&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 7924-7927). O&#39;Reilly Media. Kindle Edition.&lt;/p&gt;
&lt;p&gt;For distributed algorithms, it is commonly required that safety properties &lt;em&gt;always&lt;/em&gt; hold, in all possible situations of a system model. Therefore, even in the occurrence of all nodes crashing, or entire network failures, the algorithm must always return a correct result.&lt;/p&gt;
&lt;p&gt;On the other hand, we are allowed to make caveats with liveness properties. E.g. we could say that a request will only receive a response if majority of nodes have not crashed, and only if the network recovers from an outage eventually.&lt;/p&gt;
&lt;h5 id=&quot;mapping-system-models-to-the-real-world&quot;&gt;Mapping system models to the real world &lt;a class=&quot;direct-link&quot; href=&quot;#mapping-system-models-to-the-real-world&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;While these theoretical abstractions are useful, reality must also be considered when designing algorithms. We may sometimes have to include code to handle situations where something that was assumed to be impossible actually happens.&lt;/p&gt;
&lt;p&gt;Proving the correctness of an algorithm does not mean that the implementation on a real system will always behave correctly. However, theoretical analysis is still a good first step because it can uncover problems that may remain hidden for a long time.&lt;/p&gt;
&lt;p&gt;Theoretical analysis and empirical testing are equally important.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 7 - Transactions</title>
		<link href="https://timilearning.com/posts/ddia/part-two/chapter-7/"/>
		<updated>2019-12-15T01:52:41-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-two/chapter-7/</id>
		<content type="html">&lt;p&gt;My notes from Chapter 7 of &#39;Designing Data-Intensive Applications&#39; by Martin Kleppmann.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-meaning-of-acid&quot;&gt;The Meaning of ACID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#single-object-and-multi-object-operations&quot;&gt;Single-Object and Multi-Object Operations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#weak-isolation-levels&quot;&gt;Weak Isolation Levels&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#read-committed&quot;&gt;Read Committed&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#dirty-reads&quot;&gt;Dirty Reads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dirty-writes&quot;&gt;Dirty Writes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementing-read-committed&quot;&gt;Implementing read committed&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#snapshot-isolation-and-repeatable-read&quot;&gt;Snapshot Isolation and Repeatable Read&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#implementing-snapshot-isolation&quot;&gt;Implementing snapshot isolation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#indexes-and-snapshot-isolation&quot;&gt;Indexes and snapshot isolation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#repeatable-read-and-naming-confusion&quot;&gt;Repeatable read and naming confusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#preventing-lost-updates&quot;&gt;Preventing Lost Updates&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#automatically-detecting-lost-updates&quot;&gt;Automatically detecting lost updates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#compare-and-set&quot;&gt;Compare-and-set&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conflict-resolution-and-replication&quot;&gt;Conflict resolution and replication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#write-skew-and-phantoms&quot;&gt;Write Skew and Phantoms&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#materializing-conflicts&quot;&gt;Materializing Conflicts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#serializability&quot;&gt;Serializability&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#actual-serial-execution&quot;&gt;Actual Serial Execution&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#encapsulating-transactions-in-stored-procedures&quot;&gt;Encapsulating transactions in stored procedures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#partitioning&quot;&gt;Partitioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#summary-of-serial-execution&quot;&gt;Summary of serial execution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#two-phase-locking-2pl&quot;&gt;Two-Phase Locking (2PL)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#implementation-of-two-phase-locking&quot;&gt;Implementation of two-phase locking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#performance-of-two-phase-locking&quot;&gt;Performance of two-phase locking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#predicate-locks&quot;&gt;Predicate Locks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#index-range-locks&quot;&gt;Index Range Locks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#serializable-snapshot-isolation&quot;&gt;Serializable Snapshot Isolation&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#decisions-based-on-an-outdated-premise&quot;&gt;Decisions based on an outdated premise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#performance-of-serializable-snapshot-isolation&quot;&gt;Performance of serializable snapshot isolation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Transactions were created to simplify the programming model for applications accessing a database.&lt;/p&gt;
&lt;p&gt;All the reads and writes in a transaction are executed as one operation: either the entire operation succeeds (&lt;em&gt;commit&lt;/em&gt;) or it fails (&lt;em&gt;abort&lt;/em&gt;, &lt;em&gt;rollback&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;NoSQL databases started gaining popularity in the late 2000s and aimed to improve the status quo of relational databases by offering new data models, and including replication and partitioning by default. However, many of these new models didn&#39;t implement transactions, or watered down the meaning of the word to describe a weaker set of guarantees than had previously been understood.&lt;/p&gt;
&lt;p&gt;As these distributed databases started to emerge, the belief that transactions oppose scalability became popular; that a system would have to abandon transactions in order to maintain good performance and high availability. This is not true though.&lt;/p&gt;
&lt;p&gt;Like every technical design choice, there are advantages and disadvantages of using transactions. It&#39;s a tradeoff.&lt;/p&gt;
&lt;h3 id=&quot;the-meaning-of-acid&quot;&gt;The Meaning of ACID &lt;a class=&quot;direct-link&quot; href=&quot;#the-meaning-of-acid&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;ACID&lt;/strong&gt; stands for &lt;em&gt;Atomicity, Consistency, Isolation,&lt;/em&gt; and &lt;em&gt;Durability.&lt;/em&gt; It is often used to describe the safety guarantees provided by transactions.&lt;/p&gt;
&lt;p&gt;However, different databases have different implementations of ACID, and there are ambiguous definitions of a term like Isolation. ACID has essentially become a marketing term now.&lt;/p&gt;
&lt;p&gt;Systems that don&#39;t meet the ACID criteria are sometimes called &lt;em&gt;BASE: Basically Available, Soft state,&lt;/em&gt; and &lt;em&gt;Eventual Consistency,&lt;/em&gt; which can mean almost anything you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Atomicity&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The word &lt;em&gt;atomic&lt;/em&gt; in general means something that cannot be broken down into smaller parts.&lt;/p&gt;
&lt;p&gt;In the context of a database transaction, atomicity refers to the ability to abort a transaction on error and have all the writes from the transaction discarded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Consistency in the context of ACID is an application-level constraint. It means that there are certain statements about your data (&lt;em&gt;invariants&lt;/em&gt;) that must always be true.&lt;/p&gt;
&lt;p&gt;It is up to the application to define what those invariants are so the transaction can preserve them correctly.&lt;/p&gt;
&lt;p&gt;Unlike the other terms, consistency is a property of the application not the database, but transactions enforce those rules. These invariants are typically enforced through database constraints like uniqueness.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Isolation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Isolation is the property of ensuring that concurrently executing transactions are isolated from each other i.e. they don&#39;t step on each other&#39;s toes. They pretend like they don&#39;t know about each other.&lt;/p&gt;
&lt;p&gt;Isolation ensures that when concurrently executing transactions are committed, the result is the same as if they had run &lt;em&gt;serially,&lt;/em&gt; even though they may have run concurrently.&lt;/p&gt;
&lt;p&gt;In practice though, serializable isolation is rarely used because of the performance penalty it carries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Durability&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Durability is the promise that when a transaction is committed successfully, any data that has been written will not be forgotten, even in the event of a hardware fault or database crashes.&lt;/p&gt;
&lt;p&gt;In single node databases, durability means that the data has been written to nonvolatile storage like a hard drive or SSD. It also usually involves a write-ahead log or similar which helps with recovery in case the data structures on disk are corrupted.&lt;/p&gt;
&lt;p&gt;In a replicated database, durability often means that data has been successfully copied to a number of nodes.&lt;/p&gt;
&lt;p&gt;However, perfect durability does not exist. If all the hard disks and backups are destroyed at the same time, there&#39;s nothing the database can do to save you. In replicated systems for example, faults can be correlated (say a power outage or a bug that crashes every node that has a particular input) and can knock out all replicas are once.&lt;/p&gt;
&lt;h3 id=&quot;single-object-and-multi-object-operations&quot;&gt;Single-Object and Multi-Object Operations &lt;a class=&quot;direct-link&quot; href=&quot;#single-object-and-multi-object-operations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The definitions of &lt;em&gt;atomicity&lt;/em&gt; and &lt;em&gt;isolation&lt;/em&gt; so far assume that several objects (rows, documents, records) will be modified at once. These are known as &lt;em&gt;multi-object transactions&lt;/em&gt; and are often needed if several pieces of data are to be kept in sync.&lt;/p&gt;
&lt;p&gt;We need a way to determine which read and write operations belong to the same transaction.&lt;/p&gt;
&lt;p&gt;In relational databases, this is done based on the client&#39;s TCP connection to the database server: on any particular connection, everything between BEGIN TRANSACTION and a COMMIT is considered to be part of the same transaction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Single-object writes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Atomicity and isolation also apply when a single object is being changed. E.g.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a 20KB JSON document is being written to a database and the network connection is interrupted after the first 10KB have been sent, does the database store the 10KB fragment of JSON?&lt;/li&gt;
&lt;li&gt;If power fails while the database is in the middle of overwriting the previous value on disk, will we have the previous and new values spliced together?&lt;/li&gt;
&lt;li&gt;If another client reads a document while it&#39;s being updated, will it see a partially updated value.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These issues are why storage engines almost universally aim to provide atomicity and isolation on the level of a single object (such as a key-value pair) on one node.&lt;/p&gt;
&lt;p&gt;Atomicity can be implemented by using a log for crash recovery, while isolation can be implemented using a lock on each object.&lt;/p&gt;
&lt;p&gt;These single object operations are useful, but are not transactions in the typical sense of the word. A transaction is generally considered as a mechanism for grouping multiple operations on multiple objects into a single unit of execution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The need for multi-object transactions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Some distributed datastores have abandoned multi-object transactions because they are difficult to implement across partitions, and can hinder performance when high availability/performance is required.&lt;/p&gt;
&lt;p&gt;There are some use cases where multi-object operations need to be coordinated e.g.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When we are adding new rows to a table which have references to a row in another table using foreign keys. The foreign keys have to be coordinated across the tables and must be correct and up to date.&lt;/li&gt;
&lt;li&gt;In a document data model, when denormalized information needs to be updated, several documents often need to be updated in one go.&lt;/li&gt;
&lt;li&gt;In databases with secondary indexes (i.e. almost everything except pure key-value stores), the indexes also need to be updated every time a value is changed. That is, the indexes needs to be updated with the new records.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These can be implemented without transactions, but error handling is more complex without atomicity and isolation (to prevent concurrency problems).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Handling Errors And Aborts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The idea of atomicity is to make it so that retrying failed transactions is safe. However, it&#39;s not so straightforward. There are a number of things to take into consideration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What if the transaction actually succeeded but the network failed while the server tried to acknowledge the successful commit to the client (so the client thinks it failed), retrying the transaction will cause it to be performed twice - unless there&#39;s a de-duplication mechanism in place.&lt;/li&gt;
&lt;li&gt;If the error is due to overload, retrying the transaction will only compound the problem.&lt;/li&gt;
&lt;li&gt;If a transaction has side effects outside of the database, those side effects may happen even if the transaction is aborted. E.g. Sending an email.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;weak-isolation-levels&quot;&gt;Weak Isolation Levels &lt;a class=&quot;direct-link&quot; href=&quot;#weak-isolation-levels&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Concurrency issues (e.g. race conditions) happen when one transaction reads data that is concurrently modified by another transaction, or when two transactions simultaneously modify the same data.&lt;/p&gt;
&lt;p&gt;Concurrency issues are often hard to reproduce or test.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Transaction Isolation&lt;/em&gt; is the means by which databases typically try to hide concurrency issues from application developers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Serializable Isolation&lt;/em&gt; is the ideal isolation required, as the database guarantees that transactions have the same effect as if they ran serially (one after another). However, this form has a performance cost which most databases don&#39;t want to pay. As a result, databases use weaker levels of isolation which prevent against some concurrency issues, but not all.&lt;/p&gt;
&lt;p&gt;Concurrency bugs caused by weak transaction isolation are not just theoretical. They are real issues which have led to loss of money, data corruption, and even an investigation by financial auditors. (e.g. &lt;a href=&quot;https://bitcointalk.org/index.php?topic=499580&quot; title=&quot;https://bitcointalk.org/index.php?topic=499580&quot;&gt;https://bitcointalk.org/index.php?topic=499580&lt;/a&gt;)&lt;/p&gt;
&lt;h4 id=&quot;read-committed&quot;&gt;Read Committed &lt;a class=&quot;direct-link&quot; href=&quot;#read-committed&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The core characteristics of this isolation level are that it prevents &lt;em&gt;dirty reads&lt;/em&gt; and &lt;em&gt;dirty writes.&lt;/em&gt;&lt;/p&gt;
&lt;h5 id=&quot;dirty-reads&quot;&gt;Dirty Reads &lt;a class=&quot;direct-link&quot; href=&quot;#dirty-reads&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;If an object has been updated in a transaction but has not yet been committed, the act of any transaction being able to see that uncommitted data is known as a &lt;em&gt;dirty read&lt;/em&gt; i.e. when reading data from the database, you will only see data that has been committed.&lt;/p&gt;
&lt;h5 id=&quot;dirty-writes&quot;&gt;Dirty Writes &lt;a class=&quot;direct-link&quot; href=&quot;#dirty-writes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;If an object has been updated in a transaction but has not yet been committed, the act of any transaction being able to overwrite the uncommitted value is a &lt;em&gt;dirty write.&lt;/em&gt; i.e. when writing to the database, you will only overwrite data that has been committed.&lt;/p&gt;
&lt;p&gt;While this isolation level is commonly used, it does not prevent certain race conditions. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Imagine that I read a value as &#39;30&#39; and increment it in one transaction, and another transaction reads that value &lt;em&gt;before&lt;/em&gt; the increment operation. If that new transaction also tries to increment it even after my transaction has committed, they would be incrementing it based on the earlier value seen as &#39;30&#39; before any locks were added when modifying the object. This is because the isolation level is implemented for &lt;em&gt;objects that have been modified&lt;/em&gt;. When the new transaction happens, the value has not yet been modified.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;implementing-read-committed&quot;&gt;Implementing read committed &lt;a class=&quot;direct-link&quot; href=&quot;#implementing-read-committed&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;This is the default setting in many databases like Oracle 11g, PostgreSQL, SQL Server and other databases.&lt;/p&gt;
&lt;p&gt;Most databases prevent dirty-writes by using row-level locks i.e. when a transaction wants to modify an object, it must first acquire a lock on the object. It must then hold the lock until the transaction is committed or aborted. Only one transaction can hold a lock at a time.&lt;/p&gt;
&lt;p&gt;Preventing dirty reads can also be implemented in a similar fashion. One can require that any transaction that wants to read an object should briefly acquire the lock and release it again after reading. This way, any write on an object that hasn&#39;t been committed cannot be read since the transaction that performed the write would still hold the lock.&lt;/p&gt;
&lt;p&gt;This approach of requiring locks before reading is inefficient is practice because one long-running write transaction can force other read-only transactions to wait for a long time. Because of this, dirty reads are prevented by most databases using this approach: &lt;em&gt;for every object that is written, the database remembers both the old committed value and the new value set by the transaction which holds the write lock. Any transactions that want to read the object are simply given the old value until the new value is committed.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;snapshot-isolation-and-repeatable-read&quot;&gt;Snapshot Isolation and Repeatable Read &lt;a class=&quot;direct-link&quot; href=&quot;#snapshot-isolation-and-repeatable-read&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;With the read committed isolation level, there is still room for concurrency bugs. One of the anomalies that can happen is &lt;em&gt;a non-repeatable read&lt;/em&gt; or a &lt;em&gt;read skew.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;read skew&lt;/em&gt; means that you might read the value of an object in one transaction before a separate transaction begins, and when that separate transaction ends, that value has changed into a new value. This happens because the read committed isolation only applies a lock on values that are about to be modified.&lt;/p&gt;
&lt;p&gt;Thus, a long running read-only transaction can have situations where the value of an object or multiple objects changes between when the transaction starts and when it ends, which can lead to inconsistencies.&lt;/p&gt;
&lt;p&gt;Basically, an example flow for this kind of anomaly is this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Transaction A begins and reads objects from DB&lt;/li&gt;
&lt;li&gt;Transaction B begins and updates some of those objects (this can happen since Transaction A won&#39;t have a lock on those objects, as it&#39;s read-only).&lt;/li&gt;
&lt;li&gt;If Transaction A re-reads those objects within the same transaction for whatever reason, those values will have changed and the earlier read is &lt;em&gt;non-repeatable.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;A Fuzzy or Non-Repeatable Read occurs when a value that has been read by a still in-flight transaction is overwritten by another transaction. Even without a second read of the value actually occurring this can still cause database invariants to be violated&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://blog.acolyer.org/2016/02/24/a-critique-of-ansi-sql-isolation-levels/&quot; title=&quot;https://blog.acolyer.org/2016/02/24/a-critique-of-ansi-sql-isolation-levels/&quot;&gt;https://blog.acolyer.org/2016/02/24/a-critique-of-ansi-sql-isolation-levels/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Read skew is considered acceptable under read committed isolation, but some situations cannot tolerate that temporary inconsistency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Backups:&lt;/strong&gt; A backup requires making a copy of a database which can take long hours. During this time, writes will be made to the database. It&#39;s possible that some parts of the backup will contain an older version of the data, and other parts will have a newer version. These inconsistencies will become permanent on the database level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Analytic queries and integrity checks:&lt;/strong&gt; Long running analytics queries could end up returning incorrect data if the data in the db has changed over the course of the run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To solve this problem, &lt;em&gt;Snapshot isolation&lt;/em&gt; is commonly used. The main idea is that each transaction reads a &lt;em&gt;consistent snapshot&lt;/em&gt; of the database - that is, &lt;em&gt;a transaction will only see all the data that was committed in the database at the start of the transaction.&lt;/em&gt; Even if another transaction changes the data, it won&#39;t be seen by the current transaction.&lt;/p&gt;
&lt;p&gt;This kind of isolation is especially beneficial for long-running, read only queries like backups and analytics, as the data on which they operate remains the same throughout the transaction.&lt;/p&gt;
&lt;h5 id=&quot;implementing-snapshot-isolation&quot;&gt;Implementing snapshot isolation &lt;a class=&quot;direct-link&quot; href=&quot;#implementing-snapshot-isolation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;A core principle of snapshot isolation is this:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Readers never block writers, and writers never block readers.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Implementations of snapshot isolation typically use write locks to prevent dirty writes, but have an alternate mechanism for preventing dirty reads.&lt;/p&gt;
&lt;p&gt;Write locks mean that a transaction that makes a write to an object can block the progress of another transaction that makes a write to the same object.&lt;/p&gt;
&lt;p&gt;To implement snapshot isolation, databases potentially keep multiple different committed version of the same object. Due to the fact that it maintains several versions of an object side by side, the technique is known as &lt;em&gt;multi-version concurrency control (MVCC).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For a database providing only read committed isolation, we would only need to keep two versions of an object: the committed version and the overwritten-but-uncommitted version. However, with snapshot isolation, we keep different versions of the same object. The scenario below explains why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If Transaction A has a snapshot of the database and Transaction B has the same snapshot of the database. If transaction A commits before Transaction B, the database still needs to keep track of the snapshot being used by Transaction B, and the new committed value of Transaction A.&lt;/li&gt;
&lt;li&gt;This can continue if there&#39;s a Transaction C, D, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#39;s why we could have multiple versions of the same object.&lt;/p&gt;
&lt;p&gt;Note that storage engines that support snapshot isolation typically use MVCC for their read committed isolation level as well.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;MVCC-based snapshot isolation is typically implemented&lt;/em&gt; by given each transaction a unique, always-increasing transaction ID. Any writes to the database by a transaction are tagged with the transaction ID of the writer. Each row in the table is tagged with a created_by and deleted_by field which has the transaction ID that performed the creation or deletion (when applicable).&lt;/p&gt;
&lt;p&gt;The transaction IDs are used as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;At the start of each transaction, the database notes all the other transactions that are in progress (i.e. not committed or aborted yet). Any writes made by these transactions are ignored, even if they commit later.&lt;/li&gt;
&lt;li&gt;Any writes made by transactions with a later transaction ID than the current one are also ignored, regardless of whether they have committed or not.&lt;/li&gt;
&lt;li&gt;All other writes are visible to the application&#39;s queries.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;indexes-and-snapshot-isolation&quot;&gt;Indexes and snapshot isolation &lt;a class=&quot;direct-link&quot; href=&quot;#indexes-and-snapshot-isolation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;One option with indexes and snapshot isolation is to have the index point to all the versions of an object and require any index query to filter out object versions which are not visible to the current transaction.&lt;/p&gt;
&lt;p&gt;Some databases like CouchDB and Datomic use an &lt;em&gt;append-only B-tree&lt;/em&gt; which does not overwrites pages of the tree when they are updated or modified, but creates a new copy of each modified page. PostgreSQL has optimizations to avoid index updates if different versions of the same object can fit on the same page.&lt;/p&gt;
&lt;h5 id=&quot;repeatable-read-and-naming-confusion&quot;&gt;Repeatable read and naming confusion &lt;a class=&quot;direct-link&quot; href=&quot;#repeatable-read-and-naming-confusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;SQL Isolation levels are not standardized, and so some databases refer to an implementation of snapshot isolation as &lt;em&gt;serializable&lt;/em&gt; (e.g. Oracle) or &lt;em&gt;repeatable read&lt;/em&gt; (e.g. PostgreSQL and MySQL).&lt;/p&gt;
&lt;p&gt;DB2 refers to serializability as &amp;quot;repeatable read&amp;quot;.&lt;/p&gt;
&lt;p&gt;In summary, naming here is a shitshow in database town.&lt;/p&gt;
&lt;h4 id=&quot;preventing-lost-updates&quot;&gt;Preventing Lost Updates &lt;a class=&quot;direct-link&quot; href=&quot;#preventing-lost-updates&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;So far, we have only discussed how to prevent dirty writes, but haven&#39;t spoken about another problem that could occur: the &lt;em&gt;lost update&lt;/em&gt; problem. Basically, if two writes concurrently update a value, what&#39;s going to happen?&lt;/p&gt;
&lt;p&gt;The lost update problem mainly occurs when an application &lt;em&gt;reads&lt;/em&gt; a value, &lt;em&gt;modifies&lt;/em&gt; it, and &lt;em&gt;writes&lt;/em&gt; back the modified value (called a &lt;em&gt;read-modify-write cycle)&lt;/em&gt;. If two transactions try to do this concurrently, one of the updates can be lost as the second write does not include the first modification.&lt;/p&gt;
&lt;p&gt;The key difference between this and a dirty write is that: &lt;em&gt;if you overwrite a value that has been committed, it&#39;s no longer a dirty write. A dirty write happens when in a transaction, you overwrite a value which has been updated in another uncommitted transaction.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This can happen in different scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a counter needs to be incremented. It requires reading the current value, calculating the new value, and writing back the updated value. If two transactions increment the counter by different values, one of those updates will be lost.&lt;/li&gt;
&lt;li&gt;Making a local change to a complex value. E.g. Adding an element to a list within a JSON document.&lt;/li&gt;
&lt;li&gt;Two users editing a wiki page at the same time, where each user&#39;s changed is saved by sending the entire page contents to the server, it will overwrite whatever is in the database.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A variety of solutions have been developed to deal with this scenario:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Atomic Write Operations:&lt;/strong&gt; Many databases support atomic updates, which remove the need for the read-modify-write cycles. Atomic updates look like:&lt;/p&gt;
&lt;p&gt;UPDATE counters SET counter= counter + 1 WHERE key = &#39;foo&#39;&lt;/p&gt;
&lt;p&gt;Atomic operations are usually implemented by taking an exclusive lock on the object when it&#39;s read to prevent any other transaction from reading it until the update has been applied.&lt;/p&gt;
&lt;p&gt;Another option for implementing atomic writes is to ensure that all atomic operations run on the same database thread.&lt;/p&gt;
&lt;p&gt;However, not all database updates fit into this model. Some updates require more complex logic and won&#39;t benefit from atomic writes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Explicit Locking:&lt;/strong&gt; Another option for preventing lost updates is to explicitly lock objects which are going to be updated. You may need to specify in your application&#39;s code through an ORM or directly in SQL that the rows returned from a query should be locked.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;automatically-detecting-lost-updates&quot;&gt;Automatically detecting lost updates &lt;a class=&quot;direct-link&quot; href=&quot;#automatically-detecting-lost-updates&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The methods discussed above (atomic operations and locks) are good ways of preventing lost updates as they force the read-modify-write cycles to occur sequentially. One alternative is to allow concurrent cycles to execute in parallel and let the transaction manager detect a lost update.&lt;/p&gt;
&lt;p&gt;When a lost update is detected, the transaction can be aborted and it can be forced to retry its read-modify-write cycle.&lt;/p&gt;
&lt;p&gt;This approach has an advantage that the check for lost updates can be performed efficiently with snapshot isolation.&lt;/p&gt;
&lt;p&gt;PostgreSQL, Oracle and SQL Server automatically detect lost updates.&lt;/p&gt;
&lt;p&gt;Another advantage is that application code does not have to use any special database features like atomic locks or explicit locking to prevent lost updates.&lt;/p&gt;
&lt;h5 id=&quot;compare-and-set&quot;&gt;Compare-and-set &lt;a class=&quot;direct-link&quot; href=&quot;#compare-and-set&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;In databases which don&#39;t provide transactions, an atomic compare-and-set operation is usually found.&lt;/p&gt;
&lt;p&gt;What this means is that when an operation wants to update a value, it reads the previous value and only completes if the value at the time of the update is the same as the value it read earlier.&lt;/p&gt;
&lt;p&gt;This is safe as long as the database is not comparing the current value against an old snapshot.&lt;/p&gt;
&lt;h5 id=&quot;conflict-resolution-and-replication&quot;&gt;Conflict resolution and replication &lt;a class=&quot;direct-link&quot; href=&quot;#conflict-resolution-and-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Locks and compare-and-set operations assume that there&#39;s a single up-to-date copy of the data. However, for databases with multi-leader or leaderless replication, there&#39;s no guarantee of a single up-to-date copy of data.&lt;/p&gt;
&lt;p&gt;A common approach in replicated databases is to allow concurrent writes create several conflicting versions of a value, and allow application code or special data structures to be used to resolve the conflicts.&lt;/p&gt;
&lt;h4 id=&quot;write-skew-and-phantoms&quot;&gt;Write Skew and Phantoms &lt;a class=&quot;direct-link&quot; href=&quot;#write-skew-and-phantoms&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To recap the two race conditions we have treated so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Dirty Writes:&lt;/em&gt; The ability of one running transaction to overwrite an update made by another running, uncommitted transaction. If a transaction updates multiple objects, dirty writes can lead to a bad outcome. There&#39;s the car example in the book of Alice and Bob buying a car in different transactions and having to update the listings and invoices tables. If Alice writes to the listings table first and Bob overrides it, but Bob writes to the invoices table first and Alice overrides it, we&#39;ll have inconsistent records in our tables as the records for that car should have the same recipient.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Lost Update:&lt;/em&gt; If two transactions happen concurrently and another commit firsts, the later one could overwrite an update made by the earlier transaction, which could lead to lost changes. E.g. Incrementing a counter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another anomaly that can happen is a &lt;em&gt;Write skew.&lt;/em&gt; Basically, if two transactions read from the same objects, and then update some of those objects (as different transactions may update different objects), a write skew can occur.&lt;/p&gt;
&lt;p&gt;Imagine a scenario where two transactions running concurrently first make a query, and then update a database object based on the result of the first query. The operations performed by the transaction to commit first may render the result of the query invalid for the later transaction.&lt;/p&gt;
&lt;p&gt;These transactions may update different objects (so it&#39;s neither a dirty write nor a lost update), but they&#39;ll still make the application function incorrectly. E.g. A meeting room booking app where two transactions running concurrently first see that a timespan was not booked, and then add a row each for different meetings. &lt;em&gt;This wouldn&#39;t be a problem if we could somehow have unique constraints on all the time ranges, but it&#39;s a problem if we don&#39;t.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Database constraints like uniqueness and foreign key constraints may help to enforce this, but they&#39;re not always applicable.&lt;/p&gt;
&lt;p&gt;Serializable isolation helps to prevent this. However, if it&#39;s not available, one way of preventing this is to explicitly lock the rows that a transaction depends on. Unfortunately, if the original query returns no rows (say it&#39;s checking for the absence of rows matching a condition), we can&#39;t attach locks to anything.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Another example:&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you&#39;re distributing students into the same class room and you want to make sure that no students with the same last name belong to the same class. Two concurrently executing transactions could first check that the condition is met, then insert separate rows for a surname. Of course, a simple solution to this is a uniqueness constraint.&lt;/p&gt;
&lt;p&gt;The effect, where a write in one transaction changes the result of a search query in another transaction, is called a &lt;strong&gt;&lt;em&gt;phantom.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h5 id=&quot;materializing-conflicts&quot;&gt;Materializing Conflicts &lt;a class=&quot;direct-link&quot; href=&quot;#materializing-conflicts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;As described above, we can reduce the effect of phantoms by attaching locks to the rows used in a transaction. However, if there&#39;s no object to which we can attach the locks (say if our initial query is searching for the absence of rows), we can artificially introduce locks.&lt;/p&gt;
&lt;p&gt;The approach of taking a phantom and turning it into a lock conflict on a concrete set of rows introduced in the database is known as &lt;em&gt;materializing conflicts.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This should be a last resort, as a serializable isolation level is much preferable in most cases.&lt;/p&gt;
&lt;h3 id=&quot;serializability&quot;&gt;Serializability &lt;a class=&quot;direct-link&quot; href=&quot;#serializability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Serializable isolation is regarded as the strongest isolation level. It guarantees that even though transactions may execute in parallel, the end result will be as if they executed one at a time, without any concurrency.&lt;/p&gt;
&lt;p&gt;Most databases use of the following three techniques for serializable isolation, which we will explore next:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Actual serial execution&lt;/em&gt; i.e. literally executing transactions in a serial order.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Two-phase locking&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Serializable snapshot isolation&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;actual-serial-execution&quot;&gt;Actual Serial Execution &lt;a class=&quot;direct-link&quot; href=&quot;#actual-serial-execution&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The simplest way to avoid concurrency issues is by removing concurrency entirely, and making sure transactions are executed in serial order, on a single thread.&lt;/p&gt;
&lt;p&gt;This idea only started being used in practice fairly recently, around 2007, when database designers saw that it was feasible to use a single-threaded loop for executing transactions and still get good performance. Two developments led to this revelation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RAM become cheap enough that it is now possible to fit an entire dataset in memory for many use cases. When the entire dataset needed for a transaction is stored in memory, transactions execute much &lt;em&gt;faster.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;They also realized that OLTP transactions are usually short and make only a small number of reads and writes. Unlike OLAP transactions which are typically run on a consistent snapshot, these transactions are short enough to be run one by one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This approach for serial transaction execution is implemented in Datomic, Redis, and others.&lt;/p&gt;
&lt;p&gt;This can even be faster than databases that implement concurrency, as there is no coordination overhead of locking.&lt;/p&gt;
&lt;p&gt;The downside is that its throughput is limited to a single CPU core (processor), except you can structure your transactions differently into partitions (discussed below).&lt;/p&gt;
&lt;h5 id=&quot;encapsulating-transactions-in-stored-procedures&quot;&gt;Encapsulating transactions in stored procedures &lt;a class=&quot;direct-link&quot; href=&quot;#encapsulating-transactions-in-stored-procedures&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Transactions are typically executed in a client/server style, one statement at a time: an application makes a query, reads the result, maybe makes another query depending on the first result etc.&lt;/p&gt;
&lt;p&gt;In this interactive style, a lot of time is spent in network communication between the application and the database.&lt;/p&gt;
&lt;p&gt;In a database with no concurrency that only processes one transaction at a time, the throughput won&#39;t be great, as the transaction will spend most of its time waiting for the next request.&lt;/p&gt;
&lt;p&gt;For this reason, databases which have single-threaded serial transaction processing do not allow interactive multi-statement transactions. Instead, all the requests in a transaction must be submitted at the same time as a &lt;em&gt;stored procedure.&lt;/em&gt; With this approach, there&#39;s only one network hop instead of having multiple.&lt;/p&gt;
&lt;p&gt;Stored procedures and in-memory data make executing transactions on a single thread become feasible.&lt;/p&gt;
&lt;h5 id=&quot;partitioning&quot;&gt;Partitioning &lt;a class=&quot;direct-link&quot; href=&quot;#partitioning&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;As mentioned earlier, executing transactions serially makes concurrency control simpler, but limits the throughput to the speed of a single CPU core on a single machine.&lt;/p&gt;
&lt;p&gt;What if we could take advantage of the presence of multiple cores and multiple node by partitioning the data?&lt;/p&gt;
&lt;p&gt;If we can partition the data so that each transaction only needs to read and write data within a single partition, then each partition can have its own transaction processing thread running independently from the others.&lt;/p&gt;
&lt;p&gt;This will likely involve some cross-partition coordination though, especially in the presence of secondary indexes and what not. Cross-partition coordination has much less throughput than single partitions.&lt;/p&gt;
&lt;h5 id=&quot;summary-of-serial-execution&quot;&gt;Summary of serial execution &lt;a class=&quot;direct-link&quot; href=&quot;#summary-of-serial-execution&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;It requires that each transaction is small and fast, else one slow transaction can stall all transaction processing.&lt;/li&gt;
&lt;li&gt;It&#39;s limited to use cases where the active dataset can fit in memory. A transaction that needs to access data not in memory can slow down processing.&lt;/li&gt;
&lt;li&gt;Write throughput must be low enough to be handled on a CPU core, or else transactions need to be partitioned without requiring cross-partition coordination.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;two-phase-locking-(2pl)&quot;&gt;Two-Phase Locking (2PL) &lt;a class=&quot;direct-link&quot; href=&quot;#two-phase-locking-(2pl)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For a long time (around 30 years), two-phase locking was the most widely used algorithm for serializability in databases.&lt;/p&gt;
&lt;p&gt;The key ideas behind two-phase locking are these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A transaction cannot write a value that has been read by another transaction.&lt;/li&gt;
&lt;li&gt;A transaction cannot read a value that has been written by another transaction. It must wait till the other transaction commits or aborts. Reading an old version of the object (like in snapshot isolation) is not acceptable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unlike snapshot isolation, readers can block writers here, and writers can block readers. (Recall that snapshot isolation has the mantra: &lt;em&gt;readers never block writers, and writers never block readers)&lt;/em&gt;&lt;/p&gt;
&lt;h5 id=&quot;implementation-of-two-phase-locking&quot;&gt;Implementation of two-phase locking &lt;a class=&quot;direct-link&quot; href=&quot;#implementation-of-two-phase-locking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The blocking of readers and writers is implemented by having a lock on each object used in a transaction. The lock can either be in &lt;em&gt;shared mode&lt;/em&gt; or in &lt;em&gt;exclusive mode.&lt;/em&gt; The lock is used as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a transaction wants to read an object, it must first acquire a shared mode lock. Multiple read-only transactions can share the lock on an object.&lt;/li&gt;
&lt;li&gt;When a transaction wants to write an object, it must acquire an exclusive lock on that object.&lt;/li&gt;
&lt;li&gt;If a transaction first reads and then writes to an object, it may upgrade its shared lock to an exclusive lock.&lt;/li&gt;
&lt;li&gt;After a transaction has acquired the lock, it must continue to hold the lock until the end of the transaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two-phase origin: First phase is acquiring the locks, second phase is when all the locks are released.&lt;/p&gt;
&lt;p&gt;If an exclusive lock already exists on an object, a transaction which wants to acquire a shared mode lock must wait for the lock to be released (when the transaction is aborted or committed), and vice versa.&lt;/p&gt;
&lt;p&gt;Since so many locks are in use, a deadlock can easily happen if transaction A is stuck waiting for transaction B to release its lock, and vice versa. E.g. If transaction A has a lock on table A, and needs table B, but transaction B already has a lock on table B but needs table A, we are in a deadlock situation.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Aside: Note that some databases perform row-level locks and others perform table-level locks.&lt;/em&gt;&lt;/p&gt;
&lt;h5 id=&quot;performance-of-two-phase-locking&quot;&gt;Performance of two-phase locking &lt;a class=&quot;direct-link&quot; href=&quot;#performance-of-two-phase-locking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Transaction throughput and response times of queries are significantly worse under two-phase locking than under weak isolation.&lt;/p&gt;
&lt;p&gt;This is as a result of the overhead of acquiring and releasing locks, but more importantly due to reduced concurrency as some transactions have wait for the others to complete. One slow transaction can cause the system to be slow.&lt;/p&gt;
&lt;p&gt;Deadlocks also occur more frequently here than in lock-based read committed isolation levels.&lt;/p&gt;
&lt;h5 id=&quot;predicate-locks&quot;&gt;Predicate Locks &lt;a class=&quot;direct-link&quot; href=&quot;#predicate-locks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The idea with predicate locks is to lock all the objects that meet a condition, even for objects that do not yet exist in the database. A condition is first run in a select query, and a predicate lock holds a lock on any objects that could meet that query.&lt;/p&gt;
&lt;h5 id=&quot;index-range-locks&quot;&gt;Index Range Locks &lt;a class=&quot;direct-link&quot; href=&quot;#index-range-locks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Predicate locks don&#39;t perform well, however, and checking for matching locks can become time consuming. Thus, most databases implement index-range locking.&lt;/p&gt;
&lt;p&gt;With index range locks, we don&#39;t just lock the objects which match a condition, we lock a bigger range of objects. For example, if an index is hit in the original query, we could lock any writes to that index entry, even if it doesn&#39;t match the condition.&lt;/p&gt;
&lt;p&gt;These are not as precise as predicate locks, but there&#39;s less overhead with checking for locks as more objects will be locked.&lt;/p&gt;
&lt;h4 id=&quot;serializable-snapshot-isolation&quot;&gt;Serializable Snapshot Isolation &lt;a class=&quot;direct-link&quot; href=&quot;#serializable-snapshot-isolation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Most of this chapter has been bleak, and has made it look like we either have serializable isolation with poor performance, or weak isolation levels that are prone to lost updates, dirty writes, write skews and phantoms.&lt;/p&gt;
&lt;p&gt;However, in 2008, &lt;a href=&quot;https://dl.acm.org/doi/10.1145/1620585.1620587&quot;&gt;Michael Cahill&#39;s PhD thesis&lt;/a&gt; introduced a new concept known as &lt;em&gt;serializable snapshot isolation.&lt;/em&gt; It provides full serializability at only a small performance penalty compared to snapshot isolation.&lt;/p&gt;
&lt;p&gt;The main idea here is that instead of holding locks on transactions, it allows transactions to continue to execute as normal until the stage where the transaction is about to commit, when it then decides whether the transaction executed in a serializable manner. This approach is known as &lt;em&gt;an optimistic concurrency control technique&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Optimistic in this sense means that instead of blocking if something potentially dangerous happens, transactions continue anyway, in the hope that everything will be fine. If everything isn&#39;t fine, it&#39;s only controlled at the time the transactions want to commit, after which it will be aborted. This approach differs from the &lt;em&gt;pessimistic technique&lt;/em&gt; used in Two-phase locking.&lt;/p&gt;
&lt;p&gt;The pessimistic approach believes that if anything can go wrong, it&#39;s better to wait until the situation is safe again (using locks) before doing anything.&lt;/p&gt;
&lt;p&gt;Optimistic concurrency control is an old idea, but in the right conditions (e.g. contention between transactions is not too high and there&#39;s enough spare capacity), they tend to perform better than pessimistic ones.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;SSI&lt;/em&gt; is based on snapshot isolation and obeys the rules that readers don’t block writers, and writers don&#39;t block readers. The main difference is that SSI adds an algorithm for detecting serialization conflicts among writes and determining which transactions to abort.&lt;/p&gt;
&lt;h5 id=&quot;decisions-based-on-an-outdated-premise&quot;&gt;Decisions based on an outdated premise &lt;a class=&quot;direct-link&quot; href=&quot;#decisions-based-on-an-outdated-premise&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;With write skew in snapshot isolation, the recurring pattern was this: a transaction reads some data from the database, examines the result of the query and takes some action based on the result of that query. However, the result from the original query may no longer be valid as at the time the transaction commits, because the data may have been modified in the meantime.&lt;/p&gt;
&lt;p&gt;The database has to be able to detect situations in which a transaction may have acted on outdated premise and abort the transaction in that case. To do this, there are two cases to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Detecting reads of a stale MVCC object version (uncommitted write occurred before the read).&lt;/li&gt;
&lt;li&gt;Detecting writes that affect prior reads (the write occurs after the read).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Detecting stale MVCC reads&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Basically, with snapshot isolation, there can be multiple versions of an object. When a transaction reads from a consistent snapshot in an MVCC database, it ignores writes that were made by transactions that had not committed at the time the snapshot was taken.&lt;/p&gt;
&lt;p&gt;This means that if Transaction A reads a value when there are uncommitted writes by Transaction B to that value, and transaction B commits before transaction A, then Transaction A may have performed some operations as a result of that earlier read which is no longer valid.&lt;/p&gt;
&lt;p&gt;To prevent this anomaly, a database needs to keep track of transactions which ignore another transaction&#39;s writes due to MVCC visibility rules. When the transaction that performed the read wants to commit, the database checks whether any of the ignored writes have been committed. If so, the transaction must be aborted.&lt;/p&gt;
&lt;p&gt;Some of the reasons why it waits for the transaction to commit, rather than aborting a transaction when a stale read is detected are that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The reading transaction might be a read-only transaction, in which case there&#39;s no risk of a write skew. The database has no way of knowing whether the transaction will later perform a write.&lt;/li&gt;
&lt;li&gt;There&#39;s no guarantee that the transaction that performed the uncommitted write will actually commit, so the read may not be a stale one at the end.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SSI avoids unnecessary aborts and thus preserves snapshot isolation&#39;s support for long-running reads from a consistent snapshot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Detecting writes that affect prior reads&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The key idea here is to keep track of which values (for example, track the indexes) have been read by what transactions. When a transaction writes to the database, it looks in the indexes for what other transactions have recently read the data. It then notifies the transactions that what they read may be out of date.&lt;/p&gt;
&lt;h5 id=&quot;performance-of-serializable-snapshot-isolation&quot;&gt;Performance of serializable snapshot isolation &lt;a class=&quot;direct-link&quot; href=&quot;#performance-of-serializable-snapshot-isolation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The advantage that this has over two-phase locking that one transaction does not need to be blocked when waiting for locks held by another transaction. Recall that like under snapshot isolation, readers don&#39;t block writers here and vice versa.&lt;/p&gt;
&lt;p&gt;This also has the advantage over serial execution that it is not limited to the throughput of a single CPU core.&lt;/p&gt;
&lt;p&gt;Due to the fact that the rate of aborts will affect the performance of SSI, it requires that read-write transactions be fairly short, as long running ones are more likely to run into conflicts. Long running read-only transactions may be fine.&lt;/p&gt;
&lt;p&gt;However, note that SSI is likely to be less sensitive to slow transactions than two-phase locking or serial execution.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 6 - Partitioning</title>
		<link href="https://timilearning.com/posts/ddia/part-two/chapter-6/"/>
		<updated>2019-12-15T01:47:10-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-two/chapter-6/</id>
		<content type="html">&lt;p&gt;My notes from Chapter 6 of &#39;Designing Data-Intensive Applications&#39; by Martin Kleppmann.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#partitioning-and-replication&quot;&gt;Partitioning and Replication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#partitioning-of-key-value-data&quot;&gt;Partitioning of Key-Value Data&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#partitioning-by-key-range&quot;&gt;Partitioning by Key Range&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#partitioning-by-hash-of-key&quot;&gt;Partitioning by Hash of Key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#skewed-workloads-and-relieving-hot-spots&quot;&gt;Skewed Workloads and Relieving Hot Spots&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#partitioning-and-secondary-indexes&quot;&gt;Partitioning and Secondary Indexes&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#partitioning-secondary-indexes-by-document&quot;&gt;Partitioning Secondary Indexes by Document&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#partitioning-secondary-indexes-by-term&quot;&gt;Partitioning Secondary Indexes by Term&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rebalancing-partitions&quot;&gt;Rebalancing Partitions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#operations-automatic-or-manual-rebalancing&quot;&gt;Operations: Automatic or Manual Rebalancing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#request-routing&quot;&gt;Request Routing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;This refers to breaking up a large data set into &lt;em&gt;partitions.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Partitioning is also known as &lt;em&gt;sharding.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Partitions are known as &lt;em&gt;shards&lt;/em&gt; in MongoDB, Elasticsearch and SolrCloud, &lt;em&gt;regions&lt;/em&gt; in Hbase, &lt;em&gt;tablets&lt;/em&gt; in Bigtable, &lt;em&gt;vnodes&lt;/em&gt; in Cassandra and Riak, and &lt;em&gt;vBucket&lt;/em&gt; in Couchbase.&lt;/li&gt;
&lt;li&gt;Normally, each piece of data belongs to only one partition.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Scalability&lt;/em&gt; is the main reason for partitioning data. It enables a large dataset to be distributed across many disks, and a query load can be distributed across many processors.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;partitioning-and-replication&quot;&gt;Partitioning and Replication &lt;a class=&quot;direct-link&quot; href=&quot;#partitioning-and-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Copies of each partition are usually stored on multiple nodes. Therefore, although a record belongs to only one partition, it may be stored on several nodes for fault tolerance.&lt;/p&gt;
&lt;p&gt;A node may also store more than one partition. If the leader-follower replication model is used for partitions, each node may be the leader for some partitions and a follower for other partitions.&lt;/p&gt;
&lt;h3 id=&quot;partitioning-of-key-value-data&quot;&gt;Partitioning of Key-Value Data &lt;a class=&quot;direct-link&quot; href=&quot;#partitioning-of-key-value-data&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The goal of partitioning is to spread data and query load evenly across nodes.&lt;/p&gt;
&lt;p&gt;If partitioning is unfair i.e. some partitions have more data than others, we call it &lt;em&gt;skewed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If the partitioning is skewed, it makes partitioning less effective as all the load could end up one partition, effectively making the other nodes idle. A partition with a disproportionately high load is called a &lt;em&gt;hot spot.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Assigning records to nodes randomly is the simplest approach for avoiding hot spots, but this has the downside that it will be more difficult to read a particular item since there&#39;s no way of knowing which node the item is on.&lt;/p&gt;
&lt;p&gt;There are other approaches for this though, and we&#39;ll talk about them.&lt;/p&gt;
&lt;h4 id=&quot;partitioning-by-key-range&quot;&gt;Partitioning by Key Range &lt;a class=&quot;direct-link&quot; href=&quot;#partitioning-by-key-range&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For key-value data where the key could be a primary key, each partition could be assigned a continuous range of keys, say a-d, e-g etc. This way, once we know the boundaries between the ranges, it&#39;s easy to determine which partition contains a given key.&lt;/p&gt;
&lt;p&gt;This range does not have to be evenly spaced. Some partitions could cover a wider range of keys than others, because the data may not be evenly distributed. Say we&#39;re indexing based on a name, one partition could hold keys for: u,v, w, x,y and z, while another could hold only a-c.&lt;/p&gt;
&lt;p&gt;To distribute data evenly, the partition boundaries need to adapt to the data.&lt;/p&gt;
&lt;p&gt;Hbase, Bigtable, RethinkDB etc use this partitioning strategy.&lt;/p&gt;
&lt;p&gt;Keys could also be kept in sorted order within each partition. This makes range-scans effective e.g. find all records that start with &#39;a&#39; .&lt;/p&gt;
&lt;p&gt;The downside of this partitioning strategy is that some access patterns can lead to hotspots. E.g . If the key is a timestamp, then partitions will correspond to ranges of time e.g. one partition by day. This means all the writes end up going to the same partition for each day, so that partition could be overloaded with writes while others sit idle.&lt;/p&gt;
&lt;p&gt;The solution for the example above is to use something apart from the timestamp as the primary key, but this is most effective if you know something else about the data. Say for sensor data, we could partition first by the sensor name and then by time. Though with this approach, you would need to perform a separate range query for each sensor name.&lt;/p&gt;
&lt;h4 id=&quot;partitioning-by-hash-of-key&quot;&gt;Partitioning by Hash of Key &lt;a class=&quot;direct-link&quot; href=&quot;#partitioning-by-hash-of-key&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Many distributed datastores use a hash function to determine the partition for a given key.&lt;/p&gt;
&lt;p&gt;When we have a suitable hash function for keys, each partition can be assigned a range of hashes, and every key whose hash falls within a partition&#39;s range will be stored for that partition.&lt;/p&gt;
&lt;p&gt;A good hash function takes skewed data and makes it uniformly distributed. The partition boundaries can be evenly spaced or chosen pseudo-randomly (with the latter being sometimes known as &lt;em&gt;consistent hashing)&lt;/em&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Aside:&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Consistent hashing, as defined by Karger et al, is a way of evenly distributing load across an internet-wide system of caches such as a content delivery network (CDN). It uses randomly chosen partition boundaries to avoid the need for central control or distributed consensus. Note that consistent here has nothing to do with replica consistency or ACID consistency, but rather describes a particular approach to rebalancing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;As we shall see in “Rebalancing Partitions”, this particular approach actually doesn’t work very well for databases, so it is rarely used in practice (the documentation of some databases still refers to consistent hashing, but it is often inaccurate). It&#39;s best to avoid the term &lt;em&gt;consistent hashing&lt;/em&gt; and just call it hash partitioning instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Kleppmann, Martin. Designing Data-Intensive Applications (Kindle Locations 5169-5174). O&#39;Reilly Media. Kindle Edition.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This approach has the downside that we lose the ability to do efficient range queries. Keys that were once adjacent are now scattered across all the partitions, so sort order is lost.&lt;/p&gt;
&lt;p&gt;Note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Range queries on the primary key are not supported by Riak, Couchbase, or Voldermort.&lt;/li&gt;
&lt;li&gt;MongoDB sends range queries to all the partitions.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Cassandra&lt;/em&gt; has a nice compromise for this. A table in Cassandra can be declared with a compound primary key consisting of several columns. Only the first part of the key is hashed, but the other columns act as a concatenated index. Thus, a query cannot search for a range of values within the first column of a compound key, but if it specifies a fixed value for the first column, it can perform an efficient range scan over the other columns of the key.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;skewed-workloads-and-relieving-hot-spots&quot;&gt;Skewed Workloads and Relieving Hot Spots &lt;a class=&quot;direct-link&quot; href=&quot;#skewed-workloads-and-relieving-hot-spots&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Even though hashing the key helps to reduce skew and the number of hot spots, it can&#39;t avoid them entirely. In an extreme case where reads and writes are for the same key, we still end up with all requests being routed to the same partition.&lt;/p&gt;
&lt;p&gt;This workload seems unusual, but it&#39;s not unheard of. Imagine a social media site where a celebrity has lots of followers. If the celebrity posts something and that post has tons of replies, all the writes for that post could potentially end up at the same partition.&lt;/p&gt;
&lt;p&gt;Most data systems today are not able to automatically compensate for a skewed workload. It&#39;s up to the application to reduce the skew. E.g if a key is known to be very hot, a technique is to add a random number to the beginning or end of the key to split the writes. This has the downside, though, that reads now have to do additional work to keep track of these keys.&lt;/p&gt;
&lt;h3 id=&quot;partitioning-and-secondary-indexes&quot;&gt;Partitioning and Secondary Indexes &lt;a class=&quot;direct-link&quot; href=&quot;#partitioning-and-secondary-indexes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The partition techniques discussed so far rely on a key-value data model, where records are only ever accessed by their primary key. In these situations, we can determine the partition from that key and use it to route read and write requests to the partition responsible for the key.&lt;/p&gt;
&lt;p&gt;If secondary indexes are involved though, this becomes more complex since they usually don&#39;t identify a record uniquely, but are a way of searching for all occurrences of a particular value.&lt;/p&gt;
&lt;p&gt;Many key value stores like Hbase and Voldermort have avoided secondary indexes because of their complexity, but some like Riak have started adding them because of their usefulness for data modelling.&lt;/p&gt;
&lt;p&gt;Secondary indexes are the bread &amp;amp; butter of search servers like Elasticsearch and Solr though. However, a challenge with secondary indexes is that they do not map neatly to partitions. Two main approaches to partitioning a database with secondary indexes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Document-based partitioning and&lt;/li&gt;
&lt;li&gt;Term-based partitioning.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;partitioning-secondary-indexes-by-document&quot;&gt;Partitioning Secondary Indexes by Document &lt;a class=&quot;direct-link&quot; href=&quot;#partitioning-secondary-indexes-by-document&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In this partitioning scheme, each document has a unique document ID. The documents are &lt;em&gt;initially&lt;/em&gt; partitioned by that ID, which could be based on a hash range or term range of the ID. Each partition then maintains its own secondary index, covering only the documents that fit within that partition. It does not care what data is stored in other partitions.&lt;/p&gt;
&lt;p&gt;A document-partitioned index is also known as a &lt;em&gt;local index.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The downside of this approach is that reads are more complicated. If we want to search by a term in the secondary index (say we&#39;re searching for all the red cars where there&#39;s an index on the color field), we would typically have to search through all the partitions, and then combine all the results we get back.&lt;/p&gt;
&lt;p&gt;This approach to querying a partitioned db is sometimes known as &lt;em&gt;scatter/gather,&lt;/em&gt; and it can make read queries on secondary indexes quite expensive. This approach is also prone to tail latency amplification (amplification in the higher percentiles), but it&#39;s widely used in Elasticsearch, Cassandra, Riak, MongoDB etc.&lt;/p&gt;
&lt;h4 id=&quot;partitioning-secondary-indexes-by-term&quot;&gt;Partitioning Secondary Indexes by Term &lt;a class=&quot;direct-link&quot; href=&quot;#partitioning-secondary-indexes-by-term&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In this approach, we keep a &lt;em&gt;global index&lt;/em&gt; of the secondary terms that covers data in all the partitions. However, we don&#39;t store the global index on one node, since it would likely become a bottleneck, but rather, we partition it. The global index must be partitioned, but it can be partitioned differently from the primary key index.&lt;/p&gt;
&lt;p&gt;The advantage of a global index over document-partitioned indexes is that reads are more efficient, since we don&#39;t need to scatter/gather over all partitions. A client only needs to make a request to the partition containing the desired document.&lt;/p&gt;
&lt;p&gt;However, the downside is that writes are slower and more complicated, because a write to a single document may now affect multiple partitions of the index.&lt;/p&gt;
&lt;p&gt;In practice, updates to global secondary indexes are often asynchronous (i.e. if you read the index shortly after a write, it may not have reflected a new change).&lt;/p&gt;
&lt;h3 id=&quot;rebalancing-partitions&quot;&gt;Rebalancing Partitions &lt;a class=&quot;direct-link&quot; href=&quot;#rebalancing-partitions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;How not to do it: hash mod n&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If we partition by &lt;em&gt;hash mod n&lt;/em&gt; where n is the number of nodes, we run the risk of excessive rebalancing. If the number of nodes N changes, most of the keys will need to be moved from one node to another. Frequent moves make rebalancing excessively expensive.&lt;/p&gt;
&lt;p&gt;Next we&#39;ll discuss approaches that don&#39;t move data around more than necessary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fixed Number of Partitions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This approach is fairly simple. It involves creating more partitions than nodes, and assigning several partitions to each node.&lt;/p&gt;
&lt;p&gt;If a node is added to a cluster, the new node &lt;em&gt;steals&lt;/em&gt; a few partitions from other nodes until partitions are fairly distributed again. Only entire partitions are moved between nodes, the number of partitions does not change, nor does the assignment of keys to partitions.&lt;/p&gt;
&lt;p&gt;What changes is the assignment of partitions to nodes. It may take some time to transfer a large amount of data over the network between nodes, so the old assignment of partitions is typically used for any reads/writes that happen while the transfer is in progress.&lt;/p&gt;
&lt;p&gt;This approach is used in Elasticsearch (where the number of primary shards is fixed on index creation), Riak, Couchbase and Voldermort.&lt;/p&gt;
&lt;p&gt;With this approach, it&#39;s imperative to choose the right number of partitions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dynamic partitioning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this rebalancing strategy, the number of partitions dynamically adjusts to fit the size of the data. This is especially useful for databases with key range partitioning, as a fixed number of partitions with fixed boundaries can be inconvenient if not configured correctly at first. All the data could end up on one node, leaving the other empty.&lt;/p&gt;
&lt;p&gt;When a partition grows to exceed a configured size, it is split into two partitions so that approximately half of the data ends up on each side of the split.&lt;/p&gt;
&lt;p&gt;Conversely, if lots of data is deleted and a partition shrinks below some threshold, it can be merged with an adjacent partition.&lt;/p&gt;
&lt;p&gt;An advantage of this approach is that the number of partitions adapts to the total data volume. If there&#39;s only a small amount of data, a small number of partitions is sufficient, so overheads are small.&lt;/p&gt;
&lt;p&gt;A downside of this approach is that an empty database starts off with a single partition, since there&#39;s no information about where to draw the partition boundaries. While the data set is small, all the writes will be processed by a single node while the others sit idle. Some databases like Hbase and MongoDB mitigate this by allowing an initial set of partitions to be configured on an empty database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partitioning proportionally to nodes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Some databases (e.g. Cassandra) have a third option of making the number of partitions proportional to the number of nodes. There is a fixed number of partitions &lt;em&gt;per node&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The size of each partition grows proportionally to the dataset size while the number of nodes remains unchanged, but when you increase the number of nodes, the partitions become smaller again.&lt;/p&gt;
&lt;p&gt;When a new node joins the cluster, it randomly chooses a fixed number of existing partitions to split, and then takes one half of the split, leaving the other half in place.&lt;/p&gt;
&lt;p&gt;This randomization can produce an unfair split, but databases like Cassandra have come up with algorithms to mitigate the effect of that.&lt;/p&gt;
&lt;h4 id=&quot;operations%3A-automatic-or-manual-rebalancing&quot;&gt;Operations: Automatic or Manual Rebalancing &lt;a class=&quot;direct-link&quot; href=&quot;#operations%3A-automatic-or-manual-rebalancing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Databases like Couchbase, Riak, and Voldermort are a good balance between automatic and manual rebalancing. They generate a suggested partitioned assignment automatically, but require an administrator to commit it before it takes effect.&lt;/p&gt;
&lt;p&gt;Fully automated rebalancing can be unpredictable. Rebalancing is an expensive operation because it requires rerouting requests and moving a large amount of data from one node to another. This process can overload the network or the nodes if not done carefully.&lt;/p&gt;
&lt;p&gt;For example, say one node is overloaded and is temporarily slow to respond to requests. The other nodes can conclude that the overloaded node is dead, and automatically rebalance the cluster to move load away from it. This puts additional load on the overloaded node, other nodes, and the network—making the situation worse and potentially causing a cascading failure.&lt;/p&gt;
&lt;h3 id=&quot;request-routing&quot;&gt;Request Routing &lt;a class=&quot;direct-link&quot; href=&quot;#request-routing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There&#39;s an open question that remains: when a client makes a request, how does it know which node to connect to? It&#39;s especially important as the assignment of partitions to nodes changes.&lt;/p&gt;
&lt;p&gt;This is an instance of the general problem of &lt;em&gt;service discovery i.e. locating things over a network.&lt;/em&gt; This isn&#39;t just limited to databases, any piece of software that&#39;s accessible over a network has this problem.&lt;/p&gt;
&lt;p&gt;There are different approaches to solving this problem on a high level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allow clients to contact any node (e.g. via a round-robin load balancer). If the node happens to own the partition to which the request applies, it can handle the request directly. Otherwise, it&#39;ll forward it to the relevant node.&lt;/li&gt;
&lt;li&gt;Send all requests from clients to a routing tier first, which determines what node should handle what request and forwards it accordingly. This routing tier acts as a partition-aware load balancer.&lt;/li&gt;
&lt;li&gt;Require that clients be aware of the partitioning and assignment of partitions to nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In all approaches, it&#39;s important for there to be a &lt;em&gt;consensus&lt;/em&gt; among all the nodes about which partitions belong to which nodes, especially as we tend to rebalance often.&lt;/p&gt;
&lt;p&gt;Many distributed systems rely on a coordination service like Zookeeper to keep track of this cluster metadata. This way, the other components of the system such as the routing tier or the partitioning-aware client can subscribe to this information in Zookeeper.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 5 - Replication</title>
		<link href="https://timilearning.com/posts/ddia/part-two/chapter-5/"/>
		<updated>2019-12-13T23:30:12-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-two/chapter-5/</id>
		<content type="html">&lt;p&gt;My notes from the fifth chapter of Martin Kleppmann&#39;s book: Designing Data Intensive Applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#leaders-and-followers&quot;&gt;Leaders and Followers&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#synchronous-versus-asynchronous-replication&quot;&gt;Synchronous Versus Asynchronous Replication&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#synchronous-replication&quot;&gt;Synchronous Replication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#asynchronous-replication&quot;&gt;Asynchronous Replication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#setting-up-new-followers&quot;&gt;Setting Up New Followers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#handling-node-outages&quot;&gt;Handling Node Outages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#scenario-a---follower-failure%3A-catch-up-recovery&quot;&gt;Scenario A - Follower Failure: Catch-up recovery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#scenario-b---leader-failure%3A-failover&quot;&gt;Scenario B - Leader failure: Failover&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementation-of-replication-logs&quot;&gt;Implementation of Replication Logs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#problems-with-replication-lag&quot;&gt;Problems with Replication Lag&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#other-consistency-levels&quot;&gt;Other Consistency Levels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#solutions-for-replication-lag&quot;&gt;Solutions for Replication Lag&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#multi-leader-replication&quot;&gt;Multi-Leader Replication&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#use-cases-for-multi-leader-replication&quot;&gt;Use Cases for Multi-Leader Replication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#handling-write-conflicts.&quot;&gt;Handling Write Conflicts.&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#synchronous-versus-asynchronous-conflict-detection&quot;&gt;Synchronous versus asynchronous conflict detection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conflict-avoidance&quot;&gt;Conflict Avoidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#converging-toward-a-consistent-state&quot;&gt;Converging toward a consistent state&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#custom-conflict-resolution-logic&quot;&gt;Custom Conflict Resolution Logic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#multi-leader-replication-topologies&quot;&gt;Multi-Leader Replication Topologies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#leaderless-replication&quot;&gt;Leaderless Replication&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#preventing-stale-reads&quot;&gt;Preventing Stale Reads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#read-repair-and-anti-entropy&quot;&gt;Read repair and anti-entropy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#quorums-for-reading-and-writing&quot;&gt;Quorums for reading and writing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#limitations-of-quorum-consistency&quot;&gt;Limitations of Quorum Consistency&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#monitoring-staleness&quot;&gt;Monitoring Staleness&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sloppy-quorums-and-hinted-handoff&quot;&gt;Sloppy Quorums and Hinted Handoff&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#multi-datacenter-operation&quot;&gt;Multi-datacenter operation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#detecting-concurrent-writes&quot;&gt;Detecting Concurrent Writes&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#last-write-wins-%28discarding-concurrent-writes%29&quot;&gt;Last write wins (discarding concurrent writes)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-%22happens-before%22-relationship-and-concurrency&quot;&gt;The &amp;quot;happens-before&amp;quot; relationship and concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#capturing-the-happens-before-relationship&quot;&gt;Capturing the happens-before relationship&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#merging-concurrently-written-values&quot;&gt;Merging Concurrently Written Values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#version-vectors&quot;&gt;Version Vectors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Replication involves keeping a copy of the same data on multiple machines connected via a network. Reasons for this involve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Increasing the number of machines that can handle failed requests - Leads to increased read throughput.&lt;/li&gt;
&lt;li&gt;To allow a system continue working in the event of failed parts - Leads to increased availability.&lt;/li&gt;
&lt;li&gt;To keep data geographically close to users - Reduced latency.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The challenge with replication lies in handling changes to replicated data. Three algorithms for replicating changes between nodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single leader&lt;/li&gt;
&lt;li&gt;Multi-leader&lt;/li&gt;
&lt;li&gt;Leaderless&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;leaders-and-followers&quot;&gt;Leaders and Followers &lt;a class=&quot;direct-link&quot; href=&quot;#leaders-and-followers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Every node that keeps a copy of data is a &lt;em&gt;replica&lt;/em&gt;. Obvious question is: how do we make sure that the data on all the replicas is the same? The most common approach for this is &lt;strong&gt;&lt;em&gt;leader-based replication&lt;/em&gt;.&lt;/strong&gt; In this approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Only the leader accepts writes.&lt;/li&gt;
&lt;li&gt;The followers read off a replication log and apply all the writes in the same order that they were processed by the leader.&lt;/li&gt;
&lt;li&gt;A client can query either the leader or any of its followers for read requests.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So here, the followers are read-only, while writes are only accepted by the leader.&lt;/p&gt;
&lt;p&gt;This approach is used by MySQL, PostgreSQL etc., as well as non-relational databases like MongoDB, RethinkDB, and Espresso.&lt;/p&gt;
&lt;h4 id=&quot;synchronous-versus-asynchronous-replication&quot;&gt;Synchronous Versus Asynchronous Replication &lt;a class=&quot;direct-link&quot; href=&quot;#synchronous-versus-asynchronous-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;With Synchronous replication, the leader must wait for a positive acknowledgement that the data has been replicated from at least one of the followers before terming the write as successful, while with Asynchronous replication, the leader does not have to wait.&lt;/p&gt;
&lt;h5 id=&quot;synchronous-replication&quot;&gt;Synchronous Replication &lt;a class=&quot;direct-link&quot; href=&quot;#synchronous-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The advantage of synchronous replication is that if the leader suddenly fails, we are guaranteed that the data is available on the follower.&lt;/p&gt;
&lt;p&gt;The disadvantage is that if the synchronous follower does not respond (say it has crashed or there&#39;s a network delay or something else), the write cannot be processed. A leader must block all writes and wait until the synchronous replica is available again. Therefore, it&#39;s impractical for all the followers to be synchronous, since just one node failure can cause the system to become unavailable.&lt;/p&gt;
&lt;p&gt;In practice, enabling synchronous replication on a database usually means that &lt;em&gt;one&lt;/em&gt; of the followers is synchronous, and the others are asynchronous. If the synchronous one is down, one of the asynchronous followers is made synchronous. This configuration is sometimes called &lt;em&gt;semi-synchronous&lt;/em&gt;.&lt;/p&gt;
&lt;h5 id=&quot;asynchronous-replication&quot;&gt;Asynchronous Replication &lt;a class=&quot;direct-link&quot; href=&quot;#asynchronous-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;In this approach, if the leaders fails and is not recoverable, any writes that have not been replicated to followers are lost.&lt;/p&gt;
&lt;p&gt;An advantage of this approach though, is that the leader can continue processing writes, even if all its followers have fallen behind.&lt;/p&gt;
&lt;p&gt;There&#39;s some research into how to prevent asynchronous-performance like systems from losing data if the leader fails. A new replication method called &lt;em&gt;Chain replication&lt;/em&gt; is a variant of synchronous replication that aims to provide good performance and availability without losing data.&lt;/p&gt;
&lt;h5 id=&quot;setting-up-new-followers&quot;&gt;Setting Up New Followers &lt;a class=&quot;direct-link&quot; href=&quot;#setting-up-new-followers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;New followers can be added to an existing cluster to replace a failed node, or to add an additional replica. The next question is how to ensure the new follower has an accurate copy of the leader&#39;s data?&lt;/p&gt;
&lt;p&gt;Two options that are not sufficient are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Just copying data files from one node to another. The data in the leader is always updating and a copy will see different versions at different points in time.&lt;/li&gt;
&lt;li&gt;Locking the database (hence making it unavailable for writes). This will go against the goal of high availability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&#39;s an option that works without downtime, which involves the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take a consistent snapshot of the leader&#39;s db at some point in time. It&#39;s possible to do this without taking a lock on the entire db. Most databases have this feature.&lt;/li&gt;
&lt;li&gt;Copy the snapshot to the follower node.&lt;/li&gt;
&lt;li&gt;The follower then requests all the data changes that happened since the snapshot was taken.&lt;/li&gt;
&lt;li&gt;When the follower has processed the log of changes since the snapshot, we say it has caught up.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In some systems, this process is fully automated, while in others, it is manually performed by an administrator.&lt;/p&gt;
&lt;h5 id=&quot;handling-node-outages&quot;&gt;Handling Node Outages &lt;a class=&quot;direct-link&quot; href=&quot;#handling-node-outages&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Any node can fail, therefore, we need to keep the system running despite individual node failures, and minimize the impact of a node outage. How do we achieve high availability with leader-based replication?&lt;/p&gt;
&lt;h5 id=&quot;scenario-a---follower-failure%3A-catch-up-recovery&quot;&gt;Scenario A - Follower Failure: Catch-up recovery &lt;a class=&quot;direct-link&quot; href=&quot;#scenario-a---follower-failure%3A-catch-up-recovery&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Each follower typically keeps a local log of the data changes it has received from the leader. If a follower node fails, it can compare its local log to the replication log maintained by the leader, and then process all the data changes that occurred when the follower was disconnected.&lt;/p&gt;
&lt;h5 id=&quot;scenario-b---leader-failure%3A-failover&quot;&gt;Scenario B - Leader failure: Failover &lt;a class=&quot;direct-link&quot; href=&quot;#scenario-b---leader-failure%3A-failover&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;This is trickier: One of the nodes needs to be promoted to be the new leader, clients need to be reconfigured to send their writes to the new leader, and the other followers need to start consuming data changes from the new leader. This whole process is called a &lt;em&gt;failover.&lt;/em&gt; Failover can be handled manually or automatically. An automatic failover consists of:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Determining that the leader has failed&lt;/em&gt;: Many things could go wrong: crashes, power outages, network issues etc. There&#39;s no foolproof way of determining what has gone wrong, so most systems use a timeout. If the leader does not respond within a given interval, it&#39;s assumed to be dead.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Choosing a new leader:&lt;/em&gt; This can be done through an election process (where the new leader is chosen by a majority of the remaining replicas), or a new leader could be appointed by a previously elected &lt;em&gt;controller node.&lt;/em&gt; The best candidate for leadership is typically the one with the most up-to-date data changes from the old leader (to minimize data loss)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Reconfiguring the system to use the new leader:&lt;/em&gt; Clients need to send write requests to the new leader, and followers need to process the replication log from the new leader. The system also needs to ensure that when the old leader comes back, it does not believe that it is still the leader. It must become a follower.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are a number of things that can go wrong during the failover process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For asynchronous systems, we may have to discard some writes if they have not been processed on a follower at the time of the leader failure. This violates clients&#39; durability expectations.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Discarding writes is especially dangerous if other storage systems are coordinated with the database contents. For example, say an autoincrementing counter is used as a MySQL primary key and a redis store key, if the old leader fails and some writes have not been processed, the new leader could begin using some primary keys which have already been assigned in redis. This will lead to inconsistencies in the data, and it&#39;s what happened to Github (&lt;a href=&quot;https://github.blog/2012-09-14-github-availability-this-week/&quot; title=&quot;https://github.blog/2012-09-14-github-availability-this-week/&quot;&gt;https://github.blog/2012-09-14-github-availability-this-week/&lt;/a&gt;).
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;In fault scenarios, we could have two nodes both believe that they are the leader: &lt;em&gt;split brain.&lt;/em&gt; Data is likely to be lost/corrupted if both leaders accept writes and there&#39;s no process for resolving conflicts. Some systems have a mechanism to shut down one node if two leaders are detected. This mechanism needs to be designed properly though, or what happened at Github can happen again( &lt;a href=&quot;https://github.blog/2012-12-26-downtime-last-saturday/&quot; title=&quot;https://github.blog/2012-12-26-downtime-last-saturday/&quot;&gt;https://github.blog/2012-12-26-downtime-last-saturday/&lt;/a&gt;)
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;It&#39;s difficult to determine the right timeout before the leader is declared dead. If it&#39;s too long, it means a longer time to recovery in the case where the leader fails. If it&#39;s too short, we can have unnecessary failovers, since a temporary load spike could cause a node&#39;s response time to increase above the timeout, or a network glitch could cause delayed packets. If the system is already struggling with high load or network problems, unnecessary failover can make the situation worse.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;implementation-of-replication-logs&quot;&gt;Implementation of Replication Logs &lt;a class=&quot;direct-link&quot; href=&quot;#implementation-of-replication-logs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Several replication methods are used in leader-based replication. These include:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;a) Statement-based replication:&lt;/strong&gt; In this approach, the leader logs every write request (statement) that it executes, and sends the statement log to every follower. Each follower parses and executes the SQL statement as if it had been received from a client.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A problem with this approach is that a statement can have different effects on different followers. A statement that calls a nondeterministic function such as NOW() or RAND() will likely have a different value on each replica.&lt;/li&gt;
&lt;li&gt;If statements use an auto-incrementing column, they must be executed in exactly the same order on each replica, or else they may have a different effect. This can be limiting when executing multiple concurrent transactions, as statements without any causal dependencies can be executed in any order.&lt;/li&gt;
&lt;li&gt;Statements with side effects (e.g. triggers, stored procedures) may result in different side effects occurring on each replica, unless the side effects are deterministic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some databases work around this issues by requiring transactions to be deterministic, or configuring the leader to replace nondeterministic function calls with a fixed return value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;b) Write-ahead log (WAL) shipping:&lt;/strong&gt; The log is an append-only sequence of bytes containing all writes to the db. Besides writing the log to disk, the leader can also send the log to its followers across the network.&lt;/p&gt;
&lt;p&gt;The main disadvantage of this approach is that the log describes the data on a low level. It details which bytes were changed in which disk blocks. This makes the replication closely coupled to the storage engine. Meaning that if the storage engine changes in another version, we cannot have different versions running on the leader and the followers, which prevents us from making zero-downtime upgrades.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;c) Logical (row-based) log replication:&lt;/strong&gt; This logs the changes that have occurred at the granularity of a row. Meaning that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For an inserted row, the log contains the new values of all columns.&lt;/li&gt;
&lt;li&gt;For a deleted row , the log contains enough information to identify the deleted row. Typically the primary key, but it could also log the old values of all columns.&lt;/li&gt;
&lt;li&gt;For an updated row, it contains enough information to identify the updated row, and the new values of all columns.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This decouples the logical log from the storage engine internals. Thus, it makes it easier for external applications (say a data warehouse for offline analysis, or for building custom indexes and caches) to parse. This technique is called &lt;em&gt;change data capture.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;d) Trigger-based replication:&lt;/strong&gt; This involves handling replication within the application code. It provides flexibility in dealing with things like: replicating only a subset of data, conflict resolution logic, replicating from one kind of database to another etc. &lt;em&gt;Trigger&lt;/em&gt; and &lt;em&gt;Stored procedures&lt;/em&gt; provide this functionality. This method has more overhead than other replication methods, and is more prone to bugs and limitations than the database&#39;s built-in replication.&lt;/p&gt;
&lt;h3 id=&quot;problems-with-replication-lag&quot;&gt;Problems with Replication Lag &lt;a class=&quot;direct-link&quot; href=&quot;#problems-with-replication-lag&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Eventual Consistency&lt;/strong&gt;: If an application reads from an asynchronous follower, it may see outdated information if the follower has fallen the leader. This inconsistency is a temporary state, and the followers will eventually catchup. That&#39;s &lt;em&gt;eventual consistency.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The delay between when a write happens on a leader and gets reflected on a follower is &lt;em&gt;replication lag&lt;/em&gt;.&lt;/p&gt;
&lt;h4 id=&quot;other-consistency-levels&quot;&gt;Other Consistency Levels &lt;a class=&quot;direct-link&quot; href=&quot;#other-consistency-levels&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There are a number of issues that can occur as a result of replication lag. In this section, I&#39;ll summarize them under the minimum consistency level needed to prevent it from happening.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;a) Reading Your Own Writes:&lt;/strong&gt; If a client writes a value to a leader and tries to read that same value, the read request might go to an asynchronous follower that has not received the write yet as a result of replication lag. The user might think the data was lost, when it really wasn&#39;t. The consistency level needed to prevent this situation is known as &lt;em&gt;read-after-write consistency&lt;/em&gt; or &lt;em&gt;read-your-writes consistency.&lt;/em&gt; It makes the guarantee that a user will always see their writes. There are a number of various techniques for implementing this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When reading a field that a user might have modified, read it from the leader, else read it from a follower. E.g. A user&#39;s profile on a social network can only be modified by the owner. A simple rule could be that user&#39;s profiles are always read from the leader, and other users&#39; profiles are read from a follower.&lt;/li&gt;
&lt;li&gt;Of course this won&#39;t be effective if most things are editable by the user, since it&#39;ll drive most reads to the leader. Another option is to keep track of the time of the update, and only read from followers whose last updated time is at least that. The timestamp could be a logical one, like a sequence of writes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&#39;s an extra complication with this if the same user is accessing my service across multiple devices say a desktop browser and a mobile app. They might be connected through different networks, yet we need to make sure they&#39;re in sync. This is known as &lt;em&gt;cross-device&lt;/em&gt; read-after-write consistency. This is more complicated for reasons like the fact that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can&#39;t use the last update time as suggested earlier, since the code on one device will not know about what updates have happened on the other device.&lt;/li&gt;
&lt;li&gt;If replicas are distributed across different datacenters, each device might hit different data datacenters which will have followers that may or may not have received the write. A solution to this is to force that all the reads from a user must be routed to the leader. This will of course introduce the complexity of routing all requests from all of a user&#39;s devices to the same datacenter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;b) Monotonic Reads:&lt;/strong&gt; An anomaly that can occur when reading from asynchronous followers is that it&#39;s possible for a user to see things &lt;em&gt;moving backward in time.&lt;/em&gt; Imagine a scenario where a user makes the same read multiple times, and each read request goes to a different follower. It&#39;s possible that a write has appeared on some followers, and not on others. Time might seem to go backwards sometimes when the user sees old data, after having read newer data.&lt;/p&gt;
&lt;p&gt;Monotonic reads is a consistency level that guarantees that a user will not read older data after having previously read newer data. This guarantee is stronger than eventual consistency, but weaker than strong consistency.&lt;/p&gt;
&lt;p&gt;A solution to this is that every read from a user should go to the same replica. The hash of a user&#39;s id could be used to determine what replica to go to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;c) Consistent Prefix Reads:&lt;/strong&gt; Another anomaly that can occur as a result of replication lag is a violation of causality. Meaning that a sequence of writes that occur in one order might be read in another order. This can especially happen in distributed databases where different partitions operate independently and there&#39;s no global ordering of writes. &lt;em&gt;Consistent prefix reads&lt;/em&gt; is a guarantee that prevents this kind of problem.&lt;/p&gt;
&lt;p&gt;One solution is to ensure that causally related writes are always written to the same partition, but this cannot always be done efficiently.&lt;/p&gt;
&lt;h4 id=&quot;solutions-for-replication-lag&quot;&gt;Solutions for Replication Lag &lt;a class=&quot;direct-link&quot; href=&quot;#solutions-for-replication-lag&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Application developers should ideally not have to worry about subtle replication issues and should trust that their databases &amp;quot;do the right thing&amp;quot;. This is why &lt;em&gt;transactions&lt;/em&gt; exist. They allow databases to provide stronger guarantees about things like consistency. However, many distributed databases have abandoned transactions because of the complexity, and have asserted that eventual consistency is inevitable. Martin discusses these claims later in the chapter.&lt;/p&gt;
&lt;h3 id=&quot;multi-leader-replication&quot;&gt;Multi-Leader Replication &lt;a class=&quot;direct-link&quot; href=&quot;#multi-leader-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The downside of single-leader replication is that all writes must go through that leader. If the leader is down, or a connection can&#39;t be made for whatever reason, you can&#39;t write to the database.&lt;/p&gt;
&lt;p&gt;Multi-leader/Master-master/Active-Active replication allows more than one node to accept writes. Each leader accepts writes from a client, and acts as a follower by accepting the writes on other leaders.&lt;/p&gt;
&lt;h4 id=&quot;use-cases-for-multi-leader-replication&quot;&gt;Use Cases for Multi-Leader Replication &lt;a class=&quot;direct-link&quot; href=&quot;#use-cases-for-multi-leader-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multi-datacenter operation:&lt;/strong&gt; Here, each datacenter can have its own leader. This has a better performance for writes, since every write can be processed in its local datacenter (as opposed to being transmitted to a remote datacenter) and replicated asynchronously to other datacenters. It also means that if a datacenter is down, each data center can continue operating independently of the others.&lt;/p&gt;
&lt;p&gt;Multi-leader replication has the disadvantage that the same data may be concurrently modified in two different datacenters, and so there needs to be a way to handle conflicts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clients with offline operation:&lt;/strong&gt; Some applications need to work even when offline. Say mobile apps for example, apps like Google Calendar need to accept writes even when the user is not connected to the internet. These writes are then asynchronously replicated to other nodes when the user is connected again. In this setup, each device stores data in its local database. Meaning that each device essentially acts like a leader. CouchDB is designed for this mode of operation apparently.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Collaborative Editing:&lt;/strong&gt; Real-time collaborative editing applications like Confluence and Google Docs allow several people edit a document at the same time. This is also a database replication problem. Each user that edits a document has their changes saved to a local replica, from which it is then replicated asynchronously.&lt;/p&gt;
&lt;p&gt;For faster collaboration, the unit of change can be a single keystroke. That is, after a keystroke is saved, it should be replicated.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;handling-write-conflicts.&quot;&gt;Handling Write Conflicts. &lt;a class=&quot;direct-link&quot; href=&quot;#handling-write-conflicts.&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Multi-leader replication has the big disadvantage that write conflicts can occur, which requires conflict resolution.&lt;/p&gt;
&lt;p&gt;If two users change the same record, the writes may be successfully applied to their local leader. However, when the writes are asynchronously replicated, a conflict will be detected. This does not happen in a single-leader database.&lt;/p&gt;
&lt;h5 id=&quot;synchronous-versus-asynchronous-conflict-detection&quot;&gt;Synchronous versus asynchronous conflict detection &lt;a class=&quot;direct-link&quot; href=&quot;#synchronous-versus-asynchronous-conflict-detection&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;In theory, we could make conflict detection synchronous, meaning that we wait for the write to be replicated to all replicas before telling the user that the write was successful. Doing this will make one lose the main advantage of multi-leader replication though, which is allowing each replica to accept writes independently. Use single-leader replication if you want synchronous conflict detection.&lt;/p&gt;
&lt;h4 id=&quot;conflict-avoidance&quot;&gt;Conflict Avoidance &lt;a class=&quot;direct-link&quot; href=&quot;#conflict-avoidance&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Conflict avoidance is the simplest strategy for dealing with conflicts. Conflicts can be avoided by ensuring that all the writes for a particular record go through the same leader. For example, you can make all the writes for a user go to the same datacenter, and use the leader there for reading and writing. This of course has a downside that if a datacenter fails, traffic needs to be rerouted to another datacenter, and there&#39;s a possibility of concurrent writes on different leaders, which could break down conflict avoidance.&lt;/p&gt;
&lt;h4 id=&quot;converging-toward-a-consistent-state&quot;&gt;Converging toward a consistent state &lt;a class=&quot;direct-link&quot; href=&quot;#converging-toward-a-consistent-state&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A database must resolve conflicts in a convergent way, meaning that all the replicas must arrive at the same final value when all changes have been replicated.&lt;/p&gt;
&lt;p&gt;Various ways of achieving this are by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Giving each write a unique ID ( e.g. a timestamp, UUID etc.), pick the write with the highest ID as the winner, and throw away the other writes. If timestamp is used, that&#39;s &lt;em&gt;Last write wins&lt;/em&gt;, it&#39;s popular, but also prone to data loss.&lt;/li&gt;
&lt;li&gt;Giving each replica a unique ID, and letting writes from the higher-number replica always take precedence over writes from a lower-number replica. This is also prone to data loss.&lt;/li&gt;
&lt;li&gt;Recording the conflict in an explicit data structure that preserves the information, and writing application code that resolves the conflict at some later time (e.g. by prompting the user).&lt;/li&gt;
&lt;li&gt;Merge the values together e.g. ordering them alphabetically.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;custom-conflict-resolution-logic&quot;&gt;Custom Conflict Resolution Logic &lt;a class=&quot;direct-link&quot; href=&quot;#custom-conflict-resolution-logic&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The most appropriate conflict resolution method may depend on the application, and thus, multi-leader replication tools often let users write conflict resolution logic using application code. The code may be executed on read or on write:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;On write:&lt;/em&gt; When the database detects a conflict in the log of replicated changes, it calls the conflict handler. The handler typically runs in a background process and must execute quickly. It has no user interaction.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;On Read:&lt;/em&gt; Conflicting writes are stored. However, when the data is read, the multiple versions of the data are returned to the user, either for the user to resolve them or for automatic resolution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Automatic conflict resolution is a difficult problem, but there are some research ideas being used today:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Conflict-free replicated datatypes (CRDTs) - Used in Riak 2.0&lt;/li&gt;
&lt;li&gt;Mergeable persistent data structure - Similar to Git. Tracks history explicitly&lt;/li&gt;
&lt;li&gt;Operational transformation: Algorithm behind Google Docs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&#39;s still an open area of research though.&lt;/p&gt;
&lt;h4 id=&quot;multi-leader-replication-topologies&quot;&gt;Multi-Leader Replication Topologies &lt;a class=&quot;direct-link&quot; href=&quot;#multi-leader-replication-topologies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A &lt;em&gt;replication topology&lt;/em&gt; is the path through which writes are propagated from one node to another. The most general topology is &lt;em&gt;all-to-all,&lt;/em&gt; where each leader sends its writes to every other leader. Other types are &lt;em&gt;circular topology&lt;/em&gt; and &lt;em&gt;star topology.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;All-to-all topology is more fault tolerant than the circular and star topologies because in those topologies, one node failing can interrupt the flow of replication messages across other nodes, making them unable to communicate until the node is fixed.&lt;/p&gt;
&lt;h3 id=&quot;leaderless-replication&quot;&gt;Leaderless Replication &lt;a class=&quot;direct-link&quot; href=&quot;#leaderless-replication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this replication style, the concept of a leader is abandoned, and any replica can typically accept writes from clients directly.&lt;/p&gt;
&lt;p&gt;This style is used by Amazon for its in-house &lt;em&gt;Dynamo&lt;/em&gt; system. Riak, Cassandra and Voldermort also use this model. These are called &lt;em&gt;Dynamo&lt;/em&gt; style systems.&lt;/p&gt;
&lt;p&gt;In some leaderless implementations, the client writes directly to several replicas, while in others there&#39;s a coordinator node that does this on behalf of the client. Unlike a leader database though, this coordinator does not enforce any ordering of the writes.&lt;/p&gt;
&lt;h4 id=&quot;preventing-stale-reads&quot;&gt;Preventing Stale Reads &lt;a class=&quot;direct-link&quot; href=&quot;#preventing-stale-reads&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Say there are 3 replicas and one of the replicas goes down. A client could write to the system and have 2 of the replicas successfully acknowledge the write. However, when the offline node gets back up, anyone who reads from it may get stale responses.&lt;/p&gt;
&lt;p&gt;To prevent stale reads, as well as writing to multiple replicas, the client may also read from multiple replicas in parallel. Version numbers are attached to the result to determine which value is newer.&lt;/p&gt;
&lt;h4 id=&quot;read-repair-and-anti-entropy&quot;&gt;Read repair and anti-entropy &lt;a class=&quot;direct-link&quot; href=&quot;#read-repair-and-anti-entropy&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When offline nodes come back up, the replication system must ensure that all data is eventually copied to every replica. Two mechanisms used in Dynamo-style datastores are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Read repair&lt;/em&gt;: When data is read from multiple replicas and the system detects that one of the replicas has a lower version number, the data could be copied to it immediately. This works for frequently read values, but has the downside that any data that is not frequently read may be missing from some replicas and thus have reduced durability.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Anti-entropy process&lt;/em&gt;: In addition to the above, some databases have a background process that looks for differences in data between replicas and copies any missing data from one replica to another. This process does not copy writes in any particular order, and there may be a notable delay before data is copied.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;quorums-for-reading-and-writing&quot;&gt;Quorums for reading and writing &lt;a class=&quot;direct-link&quot; href=&quot;#quorums-for-reading-and-writing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Quorum reads and writes refer to the minimum number of votes for a read or a write to be valid. If there are &lt;em&gt;n&lt;/em&gt; replicas, every write must be confirmed by at least &lt;em&gt;w&lt;/em&gt; nodes to be considered successful, and every read must be confirmed by at least &lt;em&gt;r&lt;/em&gt; nodes to be successful. The general rule that the number chosen for &lt;em&gt;r&lt;/em&gt; and &lt;em&gt;w&lt;/em&gt; should obey is that:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;w + r &amp;gt; n.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This way, we can typically expect an up-to-date value when reading because at least one of the &lt;em&gt;r&lt;/em&gt; nodes we&#39;re reading from must overlap with the &lt;em&gt;w&lt;/em&gt; nodes (barring sloppy quorums which I discussed below).&lt;/p&gt;
&lt;p&gt;The parameters &lt;em&gt;n, w,&lt;/em&gt; and &lt;em&gt;r&lt;/em&gt; are typically configurable. A common choice is to make n an odd number such that w = r = (n + 1)/2. These numbers can be varied though. For a workload with few writes and many reads, it may make sense to set w = n and r = 1. Of course this has the disadvantage of reduced availability for writes if just one node fails.&lt;/p&gt;
&lt;p&gt;Note that &lt;em&gt;n&lt;/em&gt; does not always refer to the number of nodes in the cluster, it may just be the number of nodes that any given value must be stored on. This allows datasets to be partitioned. I cover Partitioning in the &lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6/&quot;&gt;next post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;With &lt;em&gt;w&lt;/em&gt; and &lt;em&gt;r&lt;/em&gt; being less than &lt;em&gt;n,&lt;/em&gt; we can still process writes if a node is unavailable.&lt;/li&gt;
&lt;li&gt;Reads and writes are always sent to all n replicas in parallel, &lt;em&gt;w&lt;/em&gt; and &lt;em&gt;r&lt;/em&gt; determine how many nodes we wait for i.e., how many nodes need to report success before we consider the read or write to be successful.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;limitations-of-quorum-consistency&quot;&gt;Limitations of Quorum Consistency &lt;a class=&quot;direct-link&quot; href=&quot;#limitations-of-quorum-consistency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Quorums don&#39;t necessarily have to be majorities i.e. &lt;em&gt;w + r &amp;gt; n.&lt;/em&gt; What matters is that the sets of nodes used by the read and write operations overlap in at least one node.&lt;/p&gt;
&lt;p&gt;We could also set &lt;em&gt;w&lt;/em&gt; and &lt;em&gt;r&lt;/em&gt; to smaller numbers, so that &lt;em&gt;w + r ≤ n.&lt;/em&gt; With this, reads and writes are still sent to n nodes, but a smaller number of successful responses is required for the operation to succeed. However, you are also more likely to read stale values, as it&#39;s more likely that a read did not include the node with the latest value.&lt;/p&gt;
&lt;p&gt;The upside of the approach though is that it allows lower latency and higher availability: if there&#39;s a network interruption and many replicas become unreachable, there&#39;s a higher chance that reads and writes can still be processed.&lt;/p&gt;
&lt;p&gt;Even if we configure our database such that &lt;em&gt;w + r &amp;gt; n&lt;/em&gt; , there are still edge cases where stale values may be returned. Possible scenarios are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a sloppy quorum is used, the nodes for reading and writing may not overlap. Sloppy quorums are discussed further down.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;If two writes occur concurrently, it&#39;s still not clear which happened first. Therefore, the database may wrongly return the more stale one. If we pick a winner based on a timestamp (last write wins), writes can be lost due to clock skew.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;If a write happens concurrently with a read, the write may be reflected on only some of the replicas. It&#39;s unclear whether the read will return the old or new value.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;In a non-transaction model, if a write succeeds on some replicas but fails on others, it is not rolled back on the replicas where it succeeded.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From these points and others not listed, there is no absolute guarantee that quorum reads return the latest written value. These style of databases are optimized for use cases that can tolerate eventual consistency. Stronger guarantees require transactions or consensus.&lt;/p&gt;
&lt;h5 id=&quot;monitoring-staleness&quot;&gt;Monitoring Staleness &lt;a class=&quot;direct-link&quot; href=&quot;#monitoring-staleness&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;It&#39;s important to monitor whether databases are returning up-to-date results, even if the application can tolerate stale reads. If a replica falls behind significantly, the database should alert you so that you can investigate the cause.&lt;/p&gt;
&lt;p&gt;For leader-based replication, databases expose metrics for the replication lag. It&#39;s possible to do this because writes are applied to the leader and followers in the same order. We can determine how far behind a follower has fallen from a leader by subtracting it&#39;s position from the leader&#39;s current position.&lt;/p&gt;
&lt;p&gt;This is more difficult in leaderless replication systems as there is no fixed order in which writes are applied. There&#39;s some research into this, but it&#39;s not common practice yet.&lt;/p&gt;
&lt;h4 id=&quot;sloppy-quorums-and-hinted-handoff&quot;&gt;Sloppy Quorums and Hinted Handoff &lt;a class=&quot;direct-link&quot; href=&quot;#sloppy-quorums-and-hinted-handoff&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Databases with leaderless replication are appealing for use cases where high availability and low latency is required, as well as the ability to tolerate occasional stale reads. This is because they can tolerate failure of individual nodes without needing to failover since they&#39;re not relying on one node. They can also tolerate individual nodes going slow, as long as &lt;em&gt;w&lt;/em&gt; or &lt;em&gt;r&lt;/em&gt; nodes have responded.&lt;/p&gt;
&lt;p&gt;Note that the quorums described so far are not as fault tolerant as they can be. If any of the designated &lt;em&gt;n&lt;/em&gt; nodes is unavailable for whatever reason, it&#39;s less likely that you&#39;ll be able to have &lt;em&gt;w&lt;/em&gt; or &lt;em&gt;r&lt;/em&gt; nodes reachable, making the system unavailable. Nodes being unavailable can be caused by anything, even something as simple as a network interruption.&lt;/p&gt;
&lt;p&gt;To make the system more fault tolerant, instead of returning errors to all requests for which can&#39;t reach a quorum of &lt;em&gt;w&lt;/em&gt; or &lt;em&gt;r&lt;/em&gt; nodes, the system could accept reads and writes on nodes that are reachable, even if they are not among the designated &lt;em&gt;n&lt;/em&gt; nodes on which the value usually lives. This concept is known as a &lt;em&gt;sloppy quorum.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;With a sloppy quorum, during network interruptions, reads and writes still require &lt;em&gt;r&lt;/em&gt; and &lt;em&gt;w&lt;/em&gt; successful responses, but they do not have to be among the designated &lt;em&gt;n&lt;/em&gt; &amp;quot;home&amp;quot; nodes for a value. These are like temporary homes for the value.&lt;/p&gt;
&lt;p&gt;When the network interruption is fixed, the writes that were temporarily accepted on behalf of another node are sent to the appropriate &amp;quot;home&amp;quot; node. This is &lt;em&gt;hinted handoff.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Sloppy quorums are particularly useful for increasing write availability. However, it also means that even when &lt;em&gt;w + r &amp;gt; n,&lt;/em&gt; there is a possibility of reading stale data, as the latest value may have been temporarily written to some values outside of &lt;em&gt;n.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Sloppy quorum is more of an assurance of durability, than an actual quorum.&lt;/p&gt;
&lt;h5 id=&quot;multi-datacenter-operation&quot;&gt;Multi-datacenter operation &lt;a class=&quot;direct-link&quot; href=&quot;#multi-datacenter-operation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;For datastores like Cassandra and Voldermort which implement leaderless replication across multiple datacenters, the number of replicas &lt;em&gt;n&lt;/em&gt; includes replicas in all datacenter.&lt;/p&gt;
&lt;p&gt;Each write is also sent to all datacenters, but it only waits for acknowledgement from a quorum of nodes within its local datacenter so that it&#39;s not affected by delays and interruptions on the link between multiple datacenters.&lt;/p&gt;
&lt;h4 id=&quot;detecting-concurrent-writes&quot;&gt;Detecting Concurrent Writes &lt;a class=&quot;direct-link&quot; href=&quot;#detecting-concurrent-writes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In dynamo-style databases, several clients can concurrently write to the same key. When this happens, we have a conflict. We&#39;ve briefly touched on conflict resolution techniques already, but we&#39;ll discuss them in more detail.&lt;/p&gt;
&lt;h5 id=&quot;last-write-wins-(discarding-concurrent-writes)&quot;&gt;Last write wins (discarding concurrent writes) &lt;a class=&quot;direct-link&quot; href=&quot;#last-write-wins-(discarding-concurrent-writes)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;One approach for conflict resolution is the last write wins approach. It involves forcing an arbitrary ordering on concurrent writes (could be by using timestamps), picking the most &amp;quot;recent&amp;quot; value, and discarding writes with an earlier timestamp.&lt;/p&gt;
&lt;p&gt;This helps to achieve the goal of eventual convergence across the data in replicas, at the cost of durability. If there were several concurrent writes the same key, only one of the writes will survive and the others will be discarded, even if all the writes were reported as successful.&lt;/p&gt;
&lt;p&gt;Last write wins (LWW) is the only conflict resolution method supported by Apache Cassandra.&lt;/p&gt;
&lt;p&gt;If losing data is not acceptable, LWW is not a good choice for conflict resolution.&lt;/p&gt;
&lt;h5 id=&quot;the-%22happens-before%22-relationship-and-concurrency&quot;&gt;The &amp;quot;happens-before&amp;quot; relationship and concurrency &lt;a class=&quot;direct-link&quot; href=&quot;#the-%22happens-before%22-relationship-and-concurrency&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Whenever we have two operations A and B, there are three possibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Either A happened before B&lt;/li&gt;
&lt;li&gt;Or B happened before A&lt;/li&gt;
&lt;li&gt;Or A and B are concurrent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We say that an operation A happened before operation B if either of the following applies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;B knows about A&lt;/li&gt;
&lt;li&gt;B depends on A&lt;/li&gt;
&lt;li&gt;B builds upon A&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thus, if we cannot capture this relationship between A and B, we say that they are concurrent. If they are concurrent, we have a conflict that needs to be resolved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Exact time does not matter for defining concurrency, two operations are concurrent if they are both unaware of each other, regardless of the physical time which they occurred. Two operations can happen sometime apart and still be concurrent, as long as they are unaware of each other.&lt;/p&gt;
&lt;h5 id=&quot;capturing-the-happens-before-relationship&quot;&gt;Capturing the happens-before relationship &lt;a class=&quot;direct-link&quot; href=&quot;#capturing-the-happens-before-relationship&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;In a single database replica, version numbers are used to determine concurrency.&lt;/p&gt;
&lt;p&gt;It works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each key is assigned a version number, and that version number is &lt;em&gt;incremented&lt;/em&gt; every time that key is written, and the database stores the version number along with the value written. That version number is returned to a client.&lt;/li&gt;
&lt;li&gt;A client must read a key before writing. &lt;em&gt;When it reads a key, the server returns the latest version number together with the values that have not been overwritten.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;When a client wants to write a new value, it returns the last version number it received in the prior step alongside the write.&lt;/li&gt;
&lt;li&gt;If the version number being passed with a write is higher than the version number of other values in the db, it means the new write is aware of those values at the time of the write (since it was returned from the prior read), and can overwrite all values with that version number or below.&lt;/li&gt;
&lt;li&gt;If there are higher version numbers, the database must keep all values with the higher version number (because those values are concurrent with the incoming write- it did not know about them).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example scenario:&lt;/p&gt;
&lt;p&gt;If two clients are trying to write a value for the same key at the same time, both would first read the data for that key and get the latest version number of say: 3. If one of them writes first, the version number will be updated to 4 from the database end. However, since the slower one will pass a version number of 3, it means it is concurrent with the other one since it&#39;s not aware of the higher version number of 4.&lt;/p&gt;
&lt;p&gt;When a write includes the version number from a prior read, that tells us which previous state the write is based on.&lt;/p&gt;
&lt;h5 id=&quot;merging-concurrently-written-values&quot;&gt;Merging Concurrently Written Values &lt;a class=&quot;direct-link&quot; href=&quot;#merging-concurrently-written-values&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;With the algorithm described above, clients have to do the work of merging concurrently written values. Riak calls these values &lt;em&gt;siblings.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A simple merging approach is to take a union of the values. However, this can be faulty if one operation deleted a value but that value is still present in a sibling. To prevent this problem, the system must leave a marker &lt;em&gt;(tombstone)&lt;/em&gt; to indicate that an item has been removed when merging siblings.&lt;/p&gt;
&lt;p&gt;CRDTs are data structures that can automatically merge siblings in sensible ways, including preserving deletions.&lt;/p&gt;
&lt;h5 id=&quot;version-vectors&quot;&gt;Version Vectors &lt;a class=&quot;direct-link&quot; href=&quot;#version-vectors&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The algorithm described above used only a single replica. When we have multiple replicas, we use a version number &lt;em&gt;per replica&lt;/em&gt; &lt;strong&gt;and&lt;/strong&gt; &lt;em&gt;per key&lt;/em&gt; and follow the same algorithm. Note that each replica also keeps track of the version numbers seen from each of the other replicas. With this information, we know which values to overwrite and which values to keep as siblings.&lt;/p&gt;
&lt;p&gt;The collection of version numbers from all the replicas is called a &lt;em&gt;version vector.&lt;/em&gt; Dotted version vectors are a nice variant of this used in Riak: &lt;a href=&quot;https://riak.com/posts/technical/vector-clocks-revisited-part-2-dotted-version-vectors/&quot; title=&quot;https://riak.com/posts/technical/vector-clocks-revisited-part-2-dotted-version-vectors/&quot;&gt;https://riak.com/posts/technical/vector-clocks-revisited-part-2-dotted-version-vectors/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Version vectors are also sent to clients when values are read, and need to be sent back to the database when a value is written.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Version vectors enable us to distinguish between overwrites and concurrent writes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We also have Vector clocks, which are different from Version Vectors apparently: &lt;a href=&quot;https://haslab.wordpress.com/2011/07/08/version-vectors-are-not-vector-clocks/&quot; title=&quot;https://haslab.wordpress.com/2011/07/08/version-vectors-are-not-vector-clocks/&quot;&gt;https://haslab.wordpress.com/2011/07/08/version-vectors-are-not-vector-clocks/&lt;/a&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 4 - Encoding and Evolution</title>
		<link href="https://timilearning.com/posts/ddia/part-one/chapter-4/"/>
		<updated>2019-12-07T16:23:02-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-one/chapter-4/</id>
		<content type="html">&lt;p&gt;These are my notes from the fourth chapter of Martin Kleppmann&#39;s Designing Data Intensive Applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#formats-for-encoding-data&quot;&gt;Formats for Encoding Data&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#language-specific-formats&quot;&gt;Language-Specific Formats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#json-xml-and-binary-variants&quot;&gt;JSON, XML, and Binary Variants&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#binary-encoding&quot;&gt;Binary Encoding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#modes-of-dataflow&quot;&gt;Modes of Dataflow&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#dataflow-through-databases&quot;&gt;Dataflow Through Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dataflow-through-services-rest-and-rpc&quot;&gt;Dataflow Through Services: REST and RPC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#message-passing-dataflow&quot;&gt;Message-Passing Dataflow&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#advantages-of-a-message-broker&quot;&gt;Advantages of a message broker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#message-brokers&quot;&gt;Message brokers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#distributed-actor-frameworks&quot;&gt;Distributed actor frameworks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;We should aim to build systems that make it easy to adapt to change: &lt;strong&gt;Evolvability.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rolling upgrade:&lt;/strong&gt; Deploying a new version to a few nodes at a time, checking whether the new version is running smoothly, and gradually working your way through all the nodes.&lt;/p&gt;
&lt;p&gt;With rolling upgrades, new and old versions of the code, and old and new data formats may potentially all coexist in the system at the same time. For a system to run smoothly, compatibility needs to be in both directions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Backward compatibility:&lt;/strong&gt; Newer code can read data that was written by older code. Simpler to achieve.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Forward compatibility:&lt;/strong&gt; Older code can read data written by newer code. This is trickier because it requires older code to ignore additions made by a newer version of the code.&lt;/p&gt;
&lt;h1 id=&quot;formats-for-encoding-data&quot;&gt;Formats for Encoding Data &lt;a class=&quot;direct-link&quot; href=&quot;#formats-for-encoding-data&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Programs work with data that have at least 2 different representations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In memory data structures: optimized for efficient access and CPU manipulation&lt;/li&gt;
&lt;li&gt;Sequence of bytes (e.g. JSON) for transmitting over the network.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We need some kind of translation between the two representations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Encoding/Serialization/Marshalling&lt;/strong&gt; - Translation from in-memory representation to a byte sequence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decoding/ Deserialization&lt;/strong&gt; - Byte sequence to in-memory representation.&lt;/p&gt;
&lt;p&gt;There are a number of different libraries and encoding formats to choose from which we&#39;ll discuss next.&lt;/p&gt;
&lt;h2 id=&quot;language-specific-formats&quot;&gt;Language-Specific Formats &lt;a class=&quot;direct-link&quot; href=&quot;#language-specific-formats&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Different programming languages have their built-in support for encoding in-memory objects into byte sequences. Java has Serializable, Ruby has Marshal and so on. However, these language-specific encodings have their own problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The encoding is tied to a particular programming language, and reading the data in another language is difficult.&lt;/li&gt;
&lt;li&gt;Versioning data is often an afterthought. They often neglect the problems of backward and forward compatibility since they&#39;re intended for quick and easy use.&lt;/li&gt;
&lt;li&gt;Efficiency is also an afterthought. Java&#39;s serialization is notorious for bad performance and bloated encoding.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;json%2C-xml%2C-and-binary-variants&quot;&gt;JSON, XML, and Binary Variants &lt;a class=&quot;direct-link&quot; href=&quot;#json%2C-xml%2C-and-binary-variants&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JSON and XML are the obvious contenders for standard encodings. CSV is another option. These formats are widely known, widely supported, and almost as widely disliked.&lt;/p&gt;
&lt;p&gt;XML is often criticized for its verbose syntax. Apart from superficial syntactic issues, they also have subtle problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;There&#39;s ambiguity around how numbers are encoded.&lt;/em&gt; In XML and CSV, there&#39;s no distinction between a number and a string that happens to have digits. JSON distinguishes both, but does not distinguish integers and floating-point numbers, and does not specify a precision.&lt;/li&gt;
&lt;li&gt;No support for binary strings (sequences of bytes without a character encoding)&lt;/li&gt;
&lt;li&gt;Optional schema support for XML and JSON&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;binary-encoding&quot;&gt;Binary Encoding &lt;a class=&quot;direct-link&quot; href=&quot;#binary-encoding&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The choice of data format can have a big impact especially when the dataset is in the order of terabytes.&lt;/p&gt;
&lt;p&gt;JSON is less verbose than XML, but both still use a lot of space compared to binary formats. This has led to a number of binary encodings for JSON (BSON, BJSON, etc) and XML (WBXML, etc). BSON is used as the primary data representation in MongoDB for example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thrift and Protocol Buffers:&lt;/strong&gt; These are binary encoding libraries. Protocol Buffers were developed at Google, while Thrift was developed at Facebook.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avro:&lt;/strong&gt; Another binary encoding format different from the two above. This started out as a sub project of Hadoop.&lt;/p&gt;
&lt;p&gt;These encoding libraries have some interesting encoding rules which I skipped: &lt;a href=&quot;http://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.html&quot; title=&quot;http://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.html&quot;&gt;http://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;modes-of-dataflow&quot;&gt;Modes of Dataflow &lt;a class=&quot;direct-link&quot; href=&quot;#modes-of-dataflow&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Recall that it was stated earlier that to send data from one process to another with which you don&#39;t share memory, it needs to be encoded as a sequence of bytes.&lt;/p&gt;
&lt;p&gt;We also said that forward and backward compatibility are important.&lt;/p&gt;
&lt;p&gt;Here, we&#39;ll explore how data flows between processes:&lt;/p&gt;
&lt;h4 id=&quot;dataflow-through-databases&quot;&gt;Dataflow Through Databases &lt;a class=&quot;direct-link&quot; href=&quot;#dataflow-through-databases&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The process that writes to a database encodes the data, while the process the reads from it decodes it. It could be the same process doing both, or different processes.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Forward compatibility&lt;/em&gt; is required in databases: If different processes are accessing the database, and one of the processes is from a newer version of the application ( say during a rolling upgrade), the newer code might write a value to the database. Forward compatibility is the ability of the process running the &lt;em&gt;old&lt;/em&gt; code to be able to read the data written by the new code.&lt;/p&gt;
&lt;p&gt;We also need &lt;em&gt;backward compatibility&lt;/em&gt; so that code from a newer version of the app can read data written by an older version.&lt;/p&gt;
&lt;p&gt;Data outlives code and often times, there&#39;s a need to migrate data to a new schema. Avro has sophisticated schema evolution rules that can allow a database to appear as if was encoded with a single schema, even though the underlying storage may contain records encoded with previous schema versions.&lt;/p&gt;
&lt;h4 id=&quot;dataflow-through-services%3A-rest-and-rpc&quot;&gt;Dataflow Through Services: REST and RPC &lt;a class=&quot;direct-link&quot; href=&quot;#dataflow-through-services%3A-rest-and-rpc&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When there&#39;s communication between processes over a network, a common arrangement is to have two roles: &lt;em&gt;clients&lt;/em&gt; (e.g. web browser) and &lt;em&gt;servers.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The server typically exposes an API over the network for the client to make requests. This API is known as a &lt;em&gt;service.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A server can also be a client to another service. E.g. a web app server is usually a client to a database.&lt;/p&gt;
&lt;p&gt;A difference between a web app service and a database service is that there&#39;s usually tighter restriction on the former.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Service-oriented architecture (SOA)&lt;/em&gt;: Decomposing a large application into smaller components by area of functionality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Web Services&lt;/strong&gt;: If a service is communicated with using HTTP as the underlying protocol, it is called a &lt;em&gt;web service.&lt;/em&gt; Two approaches to web services are &lt;em&gt;REST&lt;/em&gt; and &lt;em&gt;SOAP&lt;/em&gt; (Simple Object Access Protocol).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RPC:&lt;/strong&gt; The RPC model tries to make a request to a remote network look the same as calling a function or method, within the same process ( &lt;em&gt;location transparency -&lt;/em&gt; In computer networks, location transparency is the use of names to identify network resources, rather than their actual location).&lt;/p&gt;
&lt;p&gt;There are certain problems with this approach though, which can be summarized under the fundamental fact that network calls are different from function calls. E.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A local function call is predictable and succeeds or fails depending on parameters under my control. A network call is unpredictable - the request or response packets can get lost, the remote machine may be slow etc.&lt;/li&gt;
&lt;li&gt;A local function call either returns a result, or throws an exception, or never returns (infinite loop). A network request has another possible outcome, it may return without a result, due to a timeout. There&#39;s no way of knowing whether the request got through or not.&lt;/li&gt;
&lt;li&gt;Retrying a failed network request could cause the action to be performed multiple times if the request actually got through, but the response was lost. Building a system for idempotence could prevent this though.&lt;/li&gt;
&lt;li&gt;The client and service may be implemented in different languages, so the RPC library would need to translate datatypes from one language to another. This is tricky because not all languages have the same types. This process does not exist in a single process written in a single language.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite these problems, RPC isn&#39;t going away. The new generation of RPC frameworks are explicit about the difference between a remote request and a local function call such as Finagle, &lt;a href=&quot;http://rest.li/&quot;&gt;Rest.li&lt;/a&gt; and GRPC.&lt;/p&gt;
&lt;p&gt;The main focus of RPC frameworks is on requests between services owned by the same organization, typically within the same datacenter.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Q - How exactly do RPCs differ from REST? Is it just the way the endpoints look?&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;message-passing-dataflow&quot;&gt;Message-Passing Dataflow &lt;a class=&quot;direct-link&quot; href=&quot;#message-passing-dataflow&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Asynchronous message-passing systems are somewhere between RPC and databases.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Similar to RPCs because a client&#39;s request is delivered to another process with low latency.&lt;/li&gt;
&lt;li&gt;Similar to databases in that a message is not sent via a direct network connection, but via an intermediary called a &lt;em&gt;message broker&lt;/em&gt; or &lt;em&gt;message queue.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;advantages-of-a-message-broker&quot;&gt;Advantages of a message broker &lt;a class=&quot;direct-link&quot; href=&quot;#advantages-of-a-message-broker&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;It can act as a buffer if the recipient is down or unable to receive messages due to overloading.&lt;/li&gt;
&lt;li&gt;The sender can act without knowing the IP address and port number of the recipient (which can change quite often - especially in a cloud deployment where VMs come and go)&lt;/li&gt;
&lt;li&gt;A message can be delivered to multiple recipients.&lt;/li&gt;
&lt;li&gt;It can retry message delivering to a crashed process and prevent lost messages.&lt;/li&gt;
&lt;li&gt;It decouples the sender from the recipient. The sender does not need to know anything about the recipient.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The communication pattern here is usually asynchronous - the sender does not wait for the message to be delivered, but simply sends it and forgets about it.&lt;/p&gt;
&lt;h5 id=&quot;message-brokers&quot;&gt;Message brokers &lt;a class=&quot;direct-link&quot; href=&quot;#message-brokers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The configuration settings of message brokers typically vary, but in general they&#39;re used like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A process sends a message to a named &lt;em&gt;queue&lt;/em&gt; or &lt;em&gt;topic&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;The broker ensures that the message is delivered to one or more &lt;em&gt;consumers&lt;/em&gt; or &lt;em&gt;subscribers&lt;/em&gt; to that queue or topic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A topic can have many producers and many consumers.&lt;/p&gt;
&lt;h5 id=&quot;distributed-actor-frameworks&quot;&gt;Distributed actor frameworks &lt;a class=&quot;direct-link&quot; href=&quot;#distributed-actor-frameworks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The &lt;em&gt;actor&lt;/em&gt; model is a programming model for concurrency in a single process. Each part of the system is represented as an actor. An actor is usually a client or an entity which communicates with other actors by sending and receiving asynchronous messages.&lt;/p&gt;
&lt;p&gt;In distributed actor frameworks, this model is especially useful for scaling an application across multiple nodes as the same message-passing mechanism is used, regardless of whether the sender and recipient are on the same or different nodes.&lt;/p&gt;
&lt;p&gt;This framework integrates the actor programming model and the message broker into a single framework. 3 popular distributed actor frameworks are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Akka&lt;/li&gt;
&lt;li&gt;Orleans&lt;/li&gt;
&lt;li&gt;Erlang OTP&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 3 - Storage and Retrieval</title>
		<link href="https://timilearning.com/posts/ddia/part-one/chapter-3/"/>
		<updated>2019-12-07T16:21:35-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-one/chapter-3/</id>
		<content type="html">&lt;p&gt;These are my notes from the third chapter of Martin Kleppmann&#39;s Designing Data Intensive Applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#storage-engines&quot;&gt;Storage Engines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#log-structured-storage-engines&quot;&gt;Log-Structured Storage Engines&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#indexing&quot;&gt;Indexing&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#hash-index&quot;&gt;Hash Index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sstables-and-lsm-trees&quot;&gt;SSTables and LSM-Trees&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#constructing-and-maintaining-sstables&quot;&gt;Constructing and maintaining SSTables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#making-an-lsm-tree-out-of-sstables&quot;&gt;Making an LSM-tree out of SSTables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#performance-optimizations&quot;&gt;Performance Optimizations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#b-trees&quot;&gt;B-Trees&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#making-b--trees-reliable&quot;&gt;Making B- Trees reliable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#b-tree-optimizations&quot;&gt;B-tree optimizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#comparing-b-trees-and-lsm-trees&quot;&gt;Comparing B-Trees and LSM-Trees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#advantages-of-lsm-trees&quot;&gt;Advantages of LSM Trees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#downsides-of-lsm-trees&quot;&gt;Downsides of LSM Trees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#other-indexing-structures&quot;&gt;Other Indexing Structures&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#storing-values-within-the-index&quot;&gt;Storing values within the index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#approach-2---heap-file&quot;&gt;Approach 2 - Heap file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#approach-1---actual-row&quot;&gt;Approach 1 - Actual row&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#covering-index&quot;&gt;Covering Index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#multi-column-indexes&quot;&gt;Multi-column indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#full-text-search-and-fuzzy-indexes&quot;&gt;Full-text search and fuzzy indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#keep-everything-in-memory&quot;&gt;Keep everything in memory&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#transaction-processing-vs-analytics&quot;&gt;Transaction Processing vs Analytics&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#data-warehousing&quot;&gt;Data Warehousing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#stars-and-snowflakes-schemas-for-analytics&quot;&gt;Stars and Snowflakes: Schemas for Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#column-oriented-storage&quot;&gt;Column Oriented Storage&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#column-compression&quot;&gt;Column Compression&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#column-oriented-storage-and-column-families&quot;&gt;Column-oriented storage and column families&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sort-order-in-column-storage&quot;&gt;Sort Order in Column Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further reading:&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This chapter is about how databases work under the hood.&lt;/p&gt;
&lt;p&gt;There&#39;s a difference between storage engines that are optimized for transactional workloads and those that are optimized for analytics.&lt;/p&gt;
&lt;h1 id=&quot;storage-engines&quot;&gt;Storage Engines &lt;a class=&quot;direct-link&quot; href=&quot;#storage-engines&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;There are two families of storage engines: log-structured storage engines (log structured merge trees), and page-oriented storage engines (b-trees). A storage engine’s job is to write things to disk on a single node.&lt;/p&gt;
&lt;h1 id=&quot;log-structured-storage-engines&quot;&gt;Log-Structured Storage Engines &lt;a class=&quot;direct-link&quot; href=&quot;#log-structured-storage-engines&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Many databases internally use a &lt;em&gt;log,&lt;/em&gt; an append-only data file for adding something to it. Each line in the log contains a key-value pair, separated by a comma (similar to a CSV file, ignoring escaping issues). The log does not have to be internally-readable, it might be binary and intended only for other programs to read.&lt;/p&gt;
&lt;h2 id=&quot;indexing&quot;&gt;Indexing &lt;a class=&quot;direct-link&quot; href=&quot;#indexing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An index is an additional structure derived from the primary data. Any kind of index usually slows down writes, since the index has to be updated every time data is written.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Well-chosen indexes speed up read queries, but every index slows down writes.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;hash-index&quot;&gt;Hash Index &lt;a class=&quot;direct-link&quot; href=&quot;#hash-index&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These are indexes for key-value data. For a data storage that consists of only appending to a file, a simple indexing strategy is to keep an in-memory hash map where the value for every key is a byte offset, which indicates where the key is located in the file.&lt;/p&gt;
&lt;p&gt;Bitcask (the default storage engine in Riak - Riak is a distributed datastore similar to Cassandra) uses the approach above. The only requirement is that all the keys fit in the available RAM as the hash map is kept completely in memory. The values don&#39;t have to fit in memory since they can be loaded from disk with a simple disk seek. Something like Bitcask is suitable for situations where the value for a key is updated frequently.&lt;/p&gt;
&lt;p&gt;The obvious challenge in appending to a file is that the file can grow too large and then we run out of disk space. A solution to this is to break the log into segments of a certain size. A segment file is closed when it reaches that size, and subsequent writes are made to a new segment.&lt;/p&gt;
&lt;p&gt;We can then perform &lt;em&gt;compaction&lt;/em&gt; on these segments. Compaction means keeping the most recent update for each key and throwing away duplicate keys. Compaction often makes segments smaller (relies on the assumption that a key is overwritten several times on average within one segment), and so we can &lt;em&gt;merge&lt;/em&gt; several segments together at the same time as performing the compaction.&lt;/p&gt;
&lt;p&gt;Basically, we compact and merge segment files together. The merged segment is written to a new file. This can happen as a background process, so the old segment files can still serve read and write requests until the merging process is complete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Each segment will have its own in-memory hash table.&lt;/strong&gt; To find a value for a key, we&#39;ll check the most recent segment. If it&#39;s not there, we&#39;ll check the second-most-recent segment, and so on.&lt;/p&gt;
&lt;p&gt;There are certain practical issues that must be considered in a real life implementation of this hash index in a log structure. Some of them are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;File Format&lt;/em&gt;: As opposed to using a CSV format, it&#39;s faster and simpler to use a binary format that first encodes the length of a string in bytes, followed by the raw string.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Deleting Records&lt;/em&gt;: To delete a key and its value, it&#39;s not practical to search for all the occurrences of that key in the segments. What happens is that a special deletion record is appended to the data file (sometimes called a &lt;em&gt;tombstone).&lt;/em&gt; When log segments are merged, the tombstone tells the merging process to discard any previous values for the deleted key.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Crash Recovery&lt;/em&gt;: If the database is restarted, the in-memory hash maps will be lost. In principle, the segment&#39;s hash maps can be restored by reading the entire segment files and constructing the hash maps from scratch. This might take a while though, so could make server restarts painful. &lt;strong&gt;Bitcask&#39;s&lt;/strong&gt; approach to recovery is by storing a snapshot of each segment&#39;s hash map on disk, which can be loaded into memory more quickly&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Concurrency Control:&lt;/em&gt; Since writes are appended in a sequential order, a common implementation is to have only one writer thread. Data files are append-only and otherwise immutable, so they can be read concurrently by multiple threads.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are good reasons why an append-only log is a good choice, as opposed to a storage where files are updated in place with the new value overwriting the old one. Some of those reasons are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Appending and segment merging are sequential write operations, which are generally faster than random writes, especially on magnetic spinning-disk hard drives.&lt;/li&gt;
&lt;li&gt;Concurrency and crash recovery are simpler if segment files are append-only or immutable. For crash recovery, you don&#39;t need to worry if a crash happened while a value was being overwritten, leaving you with partial data.&lt;/li&gt;
&lt;li&gt;Merging old segments avoids the problem of data files getting fragmented over time. Fragmentation occurs on a hard drive, a memory module, or other media when data is not written closely enough physically on the drive. Those fragmented, individual pieces of data are referred to generally as fragments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Basically, when data files are far from each other, it&#39;s a form of fragmentation.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are limitations to the hash table index though, some of them are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The hash table must fit in memory, so it&#39;s not efficient if there are a large number of keys. An option is to maintain a map on disk, but it doesn&#39;t perform so well. It requires a lot of random access I/O, is expensive to grow when it becomes full, and hash collisions require fiddly logic.&lt;/li&gt;
&lt;li&gt;Range queries are not efficient. You have to look up each key individually in the map.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, in this approach, writes are made to the segments on a disk while the hash table index being stored is in-memory.&lt;/p&gt;
&lt;h3 id=&quot;sstables-and-lsm-trees&quot;&gt;SSTables and LSM-Trees &lt;a class=&quot;direct-link&quot; href=&quot;#sstables-and-lsm-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In log segments with hash indexes, each key-value pair appears in the order that it was written, and values later in the log take precedence over values for the same key earlier in the log. Apart from that, the order of key-value pairs in the file is irrelevant.&lt;/p&gt;
&lt;p&gt;There is a change to this approach in a &lt;em&gt;Sorted String Table&lt;/em&gt; format, or SSTable for short. Here, it is required that the sequence of key-value pairs is sorted by key. Hence, new key-value pairs cannot be appended to the segment immediately. There are several advantages to this over log segments with hash indexes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Merging segments is simple and efficient. The approach here is similar to the mergesort algorithm. The same principle as the log with hash indexes applies if a key is duplicated across several segments. We keep the most recent one and discard the others.&lt;/li&gt;
&lt;li&gt;You don&#39;t need to keep an index of all the keys in memory. Because the file is sorted, if you&#39;re looking for the offset of a particular key, it won&#39;t be difficult to find that offset once you can determine the offset of keys that are smaller and larger than it in the ordering.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You still need an in-memory index to tell you the offsets of some keys, but it can be sparse.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read requests often need to scan over several key-value pairs, therefore it is possible to group the records into a block and compress it before writing it to disk. Each entry of the sparse index then points to the start of a compressed block. This has the advantage of saving disk space and reducing the IO bandwidth.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SSTables store their keys in blocks, and have an internal index, so even though a single SSTable may be very large (gigabytes in size), only the index and the relevant block needs to be loaded into memory.&lt;/p&gt;
&lt;h4 id=&quot;constructing-and-maintaining-sstables&quot;&gt;Constructing and maintaining SSTables &lt;a class=&quot;direct-link&quot; href=&quot;#constructing-and-maintaining-sstables&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It&#39;s possible to maintain a sorted structure on disk( see B-Trees) but maintaining it in memory is easier and is the approach described here. The approach is to use well-known tree data structures such as red-black trees or AVL trees into which keys can be inserted in any order and read back in sorted order.&lt;/p&gt;
&lt;p&gt;So the storage engine works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a write comes in, it is written to an in-memory balanced tree data structure. The in-memory tree is sometimes called a &lt;em&gt;memtable.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;When the memtable exceeds a threshold, write it out to disk as an SSTable file. This operation is efficient because the tree already maintains the key-value pairs sorted by key. The new SSTable file then becomes the most recent segment of the database. While the SSTable is being written out to disk, writes can continue to a new memtable instance.&lt;/li&gt;
&lt;li&gt;To serve a read request, first check for the key in the memtable. If it&#39;s not there, check the most recent segment, then the next-older segment etc&lt;/li&gt;
&lt;li&gt;From time to time, run a merging and compaction process in the background to combine segment files and to discard overwritten or deleted values.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An obvious problem with this approach is that if the database crashes, the most recent writes (which are in the memtable but not yet written to disk) will disappear. To avoid that problem, one approach is to keep a separate log on disk to which every write is immediately appended. This separate log is not in sorted order, but that&#39;s irrelevant because the content can easily be sorted in a memtable. The corresponding log can be discarded every time the memtable is written out to an SSTable.&lt;/p&gt;
&lt;h4 id=&quot;making-an-lsm-tree-out-of-sstables&quot;&gt;Making an LSM-tree out of SSTables &lt;a class=&quot;direct-link&quot; href=&quot;#making-an-lsm-tree-out-of-sstables&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The algorithm described above is used in LevelDB and RocksDB. Key-value storage engine libraries are designed to be embedded into other applications. Among other things, LevelDB can be used in Riak as an alternative to Bitcask as its storage engine.&lt;/p&gt;
&lt;p&gt;This indexing structure was originally described under the name &lt;em&gt;Log-Structured Merge-Tree.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Lucene, which is an indexing engine for full-text search uses a similar method for storing its &lt;em&gt;term dictionary.&lt;/em&gt; A full-text index is more complex than a key-value index but is based on a similar idea: given a word in a search query, find all the documents that mention the word. It&#39;s usually implemented with a key-value structure where the key is a word ( a &lt;em&gt;term&lt;/em&gt;) and the value is a list of IDs of all the documents that contain the word (the &lt;em&gt;postings list).&lt;/em&gt; In Lucene, the mapping from term to postings list is kept in SSTable-like sorted files that are merged in the background as needed.&lt;/p&gt;
&lt;h4 id=&quot;performance-optimizations&quot;&gt;Performance Optimizations &lt;a class=&quot;direct-link&quot; href=&quot;#performance-optimizations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The LSM-tree algorithm can be slow when looking up keys that do not exist in the database: you first have to check the memtable, then all the segments all the way up to the oldest (possibly having to read from disk for each one) to be certain that the key does not exist. In order to optimize this access, storage engines often make use of &lt;em&gt;Bloom filters.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;Bloom filter&lt;/em&gt; is a memory-efficient data structure for approximating the contents of a set. It can tell you if a key does not appear in a database, thus saving you from unnecessary disk reads for nonexistent keys.&lt;/p&gt;
&lt;p&gt;There are also strategies to determine the order and timing of how SSTables are compacted and merged. Two most common options are &lt;em&gt;size-tiered&lt;/em&gt; and &lt;em&gt;leveled&lt;/em&gt; compaction. LevelDB and RocksDB use leveled compaction, Hbase uses size-tiered and Cassandra supports both.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Size-Tiered Compaction&lt;/em&gt;: Here, newer and smaller SSTables are successively merged into older and larger SSTables.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Leveled Compaction&lt;/em&gt;&lt;strong&gt;:&lt;/strong&gt; The key range is split into smaller SSTables and older data is moved into separate &amp;quot;levels&amp;quot;. This allows compaction to proceed more incrementally and use less disk space. The levels are structured roughly so that each level is in total 10x as large as the level above it. New keys arrive at the highest layer, and as that level gets larger and larger and hits a threshold, some SSTables at that level get compacted into fewer (but larger) SSTables one level lower.&lt;/p&gt;
&lt;p&gt;Within a single level, SSTables are non-overlapping: one SSTable might contain keys covering the range (a,b), the next (c,d), and so on. The key-space does overlap between levels: if you have two levels, the first might have two SSTables (covering the ranges above), but the second level might have a single SSTable over the key space (a,e). Looking for the key &#39;aardvark&#39; may require looking in two SSTables: the (a,b) SSTable in Level 1, and the (a,e) SSTable in Level 2.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Basically, a level has many SSTables.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;b-trees&quot;&gt;B-Trees &lt;a class=&quot;direct-link&quot; href=&quot;#b-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;B-trees are a popular indexing structure. Like SSTables, they keep key-value pairs sorted by key, but the similarity ends there.&lt;/p&gt;
&lt;p&gt;Log-structured indexes break the database down into segments, however B-trees break the database down into fixed size &lt;em&gt;blocks&lt;/em&gt; or &lt;em&gt;pages.&lt;/em&gt; Each page can be identified with its address or location on disk, which allows one page to refer to another. Pages are usually small in size, typically 4kb compared to segments which can be several megabytes. Pages are stored on disk.&lt;/p&gt;
&lt;p&gt;One page is designated as the root of the B-tree; whenever you want to look up a key in the index, you start here. The page contains several keys and references to child pages. Each child is responsible for a continuous range of keys, and the keys between the references indicate where the boundaries between those ranges lie.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Branching factor:&lt;/em&gt; The number of references to child pages in one page the B-tree.&lt;/p&gt;
&lt;p&gt;To update the value of an existing key in a B-tree, you search for the leaf page containing that key, change the value in that page, and write the page back to disk (any references to that page remain valid). To add a new key, find the page whose range encompasses the new key and add it to that page. If there&#39;s no free space on that page, split the page into two half-full pages, and update the parent page to account for the new subdivision of key ranges.&lt;/p&gt;
&lt;h4 id=&quot;making-b--trees-reliable&quot;&gt;Making B- Trees reliable &lt;a class=&quot;direct-link&quot; href=&quot;#making-b--trees-reliable&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The main write operation of a B-tree is to overwrite a page on disk with new data. The assumption is that an overwrite does not change where a page is located i.e. all the references to a page typically remain intact when the page is overwritten. This differs from LSM trees where things are never updated in place, and are append only.&lt;/p&gt;
&lt;p&gt;Some operations require different pages to be overwritten e.g. when a page is split because an insertion caused it to be overfull. We&#39;ll need to write the two pages that were split and update the parent page with references to the two child pages. This operation is dangerous especially if the database crashes after only some pages have been written, this can lead to a corrupted index.&lt;/p&gt;
&lt;p&gt;A solution used to make databases resilient to crashes is to keep a &lt;strong&gt;&lt;em&gt;write-ahead log&lt;/em&gt;&lt;/strong&gt; on disk. It is an append-only file to which every B-tree modification must be written before it can be applied to the pages of the tree itself. It&#39;s used to restore the DB when it comes back from a crash.&lt;/p&gt;
&lt;p&gt;There are also concurrency issues associated with updating pages in place. If multiple threads access a B-tree at the same time, a thread may see the tree in an inconsistent state. The solution is usually implemented by protecting the tree&#39;s data structures with &lt;em&gt;latches&lt;/em&gt; (lightweight locks). This is not an issue with log structured approaches since all the merging happens in the background without interfering with incoming queries.&lt;/p&gt;
&lt;h4 id=&quot;b-tree-optimizations&quot;&gt;B-tree optimizations &lt;a class=&quot;direct-link&quot; href=&quot;#b-tree-optimizations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Different optimizations have been made with B-trees:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Additional pointers been added to the tree. E.g. a leaf page may have references to its sibling pages to the left and right, this allows scanning keys in order without jumping back to parent pages.&lt;/li&gt;
&lt;li&gt;Some databases use a copy-on-write scheme instead of overwriting pages and maintaining a WAL for crash recovery. What this means is that a modified page is written to a different location, and a new version of the parent pages in the tree is created, pointing at the new location.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;So, page structured storage engines are organized into fixed-size pages. These pages are all part of a tree called b-tree.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;SQLite, for example, has a btree for every table in the database, as well as a btree for every index in the database. For the indexes , the key stored on a page is the column value of the index, while the value is the row id where it can be found. For the table btree, the key is the rowid while I believe the value is all the data in that row: &lt;a href=&quot;https://jvns.ca/blog/2014/10/02/how-does-sqlite-work-part-2-btrees/&quot; title=&quot;https://jvns.ca/blog/2014/10/02/how-does-sqlite-work-part-2-btrees/&quot;&gt;https://jvns.ca/blog/2014/10/02/how-does-sqlite-work-part-2-btrees/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hackernoon.com/fundamentals-of-system-design-part-3-8da61773a631&quot; title=&quot;https://hackernoon.com/fundamentals-of-system-design-part-3-8da61773a631&quot;&gt;https://hackernoon.com/fundamentals-of-system-design-part-3-8da61773a631&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;comparing-b-trees-and-lsm-trees&quot;&gt;Comparing B-Trees and LSM-Trees &lt;a class=&quot;direct-link&quot; href=&quot;#comparing-b-trees-and-lsm-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As a rule of thumb, LSM trees are typically faster for writes, while B-trees are thought to be faster for reads. Reads are slower on LSM-trees because they have to check different data structures and SSTables at different stages of compaction.&lt;/p&gt;
&lt;h4 id=&quot;advantages-of-lsm-trees&quot;&gt;Advantages of LSM Trees &lt;a class=&quot;direct-link&quot; href=&quot;#advantages-of-lsm-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A B-tree index must write every piece of data at least twice: once to the write-ahead log, and once to the page itself (and perhaps again as pages are split). There&#39;s also overhead from having to write an entire page at a time, even if only few bytes in the page change. Log-structured indexes also rewrite data multiple times due to the repeated compaction and merging of SSTables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Write amplification:&lt;/em&gt;&lt;/strong&gt; When one write to the database results in multiple writes to the disk over the course of the database&#39;s lifetime. This is of particular concern on SSDs, which can only overwrite blocks a limited number of times before wearing out.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LSM-trees are typically able to sustain higher write throughput than B-trees partly because they sometimes have lower write amplification, and also because they sequentially write compact SSTable files rather than having to overwrite several pages in the tree. This is important on magnetic hard drives, where sequential writes are faster than random writes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;LSM-trees can be compressed better, and this often produces smaller files on disk than B-trees. B-trees leave some disk space used due to fragmentation: when a row cannot fit into an existing page or a page is split, some space in a page remains unused(Basically, if the existing space on the page cannot fit a new row, the row will be moved to a page). Sending and receiving smaller files over IO is useful if you bandwidth is limited.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On many SSDs, the firmware internally uses a log-structured algorithm to turn random writes into sequential writes on the underlying storage chips, so the impact of the storage engine&#39;s write pattern is less pronounced (point 2). Note that lower write amplification and reduced fragmentation is still advantageous on SSDs: representing data more compactly allows more read and write requests within the available I/O bandwidth.&lt;/p&gt;
&lt;h4 id=&quot;downsides-of-lsm-trees&quot;&gt;Downsides of LSM Trees &lt;a class=&quot;direct-link&quot; href=&quot;#downsides-of-lsm-trees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A downside of log-structured storage is that the compaction process happening can interfere with the performance of ongoing reads and writes. Storage engines typically try to perform compaction incrementally and without affecting concurrent access, but it can easily happen that a request needs to wait while the disk finishes an expensive compaction operation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;One other issue with compaction arises at high write throughput: The disk needs to share its finite write bandwidth between the initial write (logging and flushing a memtable to disk) and the compaction threads running in the background.&lt;/p&gt;
&lt;p&gt;Compaction has to keep up with the rate of incoming writes, even at high write throughput.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A key can exist in multiple places in different segments with LSM trees. This differs from B-trees where a key can only exist in one place. This prospect makes B-trees more appealing for strong transactional semantics - e.g. with b-trees transaction isolation can be implemented by attaching locks on a range of keys to a tree.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;other-indexing-structures&quot;&gt;Other Indexing Structures &lt;a class=&quot;direct-link&quot; href=&quot;#other-indexing-structures&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We&#39;ve mainly covered key-value indexes which are like a primary key index, but we can also have secondary indexes. You can typically create several secondary indexes on the same table in relational databases. A secondary index can be constructed from a key-value index.&lt;/p&gt;
&lt;p&gt;With secondary indexes, note that the indexed values are not necessarily unique. Several rows (documents, vertices) may exist under the same index entry. This can be expressed in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Making each value in the index a list of matching row identifiers.&lt;/li&gt;
&lt;li&gt;Making each key unique by appending a row identifier to it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both B-trees and log-structured indexes can be used as secondary indexes.&lt;/p&gt;
&lt;h4 id=&quot;storing-values-within-the-index&quot;&gt;Storing values within the index &lt;a class=&quot;direct-link&quot; href=&quot;#storing-values-within-the-index&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The key in an index is the column value that queries search for, but the value can be either:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The actual row (document, vertex) in question&lt;/li&gt;
&lt;li&gt;A reference to the row stored elsewhere. The rows in this case are stored somewhere known as a &lt;strong&gt;&lt;em&gt;heap file&lt;/em&gt;&lt;/strong&gt;, which stores data in no particular order (could be append-only, or may keep track of deleted rows in order to overwrite them with new data later). This approach is common because it avoids duplicating data in the presence of several secondary indexes. Each index just references a location in the heap file.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;approach-2---heap-file&quot;&gt;Approach 2 - Heap file &lt;a class=&quot;direct-link&quot; href=&quot;#approach-2---heap-file&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The heap file approach can be efficient when updating a value without changing the key, provided the new value is not larger than the old value. If it is larger, the record might need to be moved to a new location in the heap where there is enough space. When this happens, either all indexes need to be updated to point to the new heap location of the record, or a &lt;strong&gt;forwarding pointer&lt;/strong&gt; is left behind in the old heap location.&lt;/p&gt;
&lt;h4 id=&quot;approach-1---actual-row&quot;&gt;Approach 1 - Actual row &lt;a class=&quot;direct-link&quot; href=&quot;#approach-1---actual-row&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In some cases, the hop from the index to the heap file is too much of a performance penalty for reads, so the indexed row is stored directly within the index. This is known as a &lt;strong&gt;&lt;em&gt;clustered index.&lt;/em&gt;&lt;/strong&gt; In MySQL&#39;s InnoDB storage engine, the primary key of a table is always a clustered index, and secondary indexes refer to the primary key (rather than a heap file).&lt;/p&gt;
&lt;h4 id=&quot;covering-index&quot;&gt;Covering Index &lt;a class=&quot;direct-link&quot; href=&quot;#covering-index&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There&#39;s a compromise between a clustered index (storing all row data within the index) and a nonclustered index(storing only references to the data within the index) which is known as a &lt;em&gt;covering index&lt;/em&gt; or &lt;em&gt;index with included columns,&lt;/em&gt; which stores &lt;em&gt;some&lt;/em&gt; of a table&#39;s columns within the index. With this approach, some queries can be answered using the index alone.&lt;/p&gt;
&lt;h4 id=&quot;multi-column-indexes&quot;&gt;Multi-column indexes &lt;a class=&quot;direct-link&quot; href=&quot;#multi-column-indexes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We&#39;ve only dealt with indexes which map a single key to a value so far. We need more than that to query multiple columns of a table, or multiple documents simultaneously.&lt;/p&gt;
&lt;p&gt;The most common type of multi-column index is a &lt;strong&gt;concatenated index.&lt;/strong&gt; This type of index combines several fields into one key by appending the columns. This kind is useless if you only want to search for the values for one of the columns. The columns should be in the order of the common search queries/pattern, because the index will sort by the first column.&lt;/p&gt;
&lt;p&gt;Another approach is a &lt;strong&gt;multi-dimensional index.&lt;/strong&gt; These kind are a more general way of querying several columns at once, which is useful for geospatial data, for example. Say you want to perform a search for records between both a longitude range &lt;strong&gt;and&lt;/strong&gt; a latitude range, an LSM tree or B-tree cannot answer that efficiently. You can get all the records within a range of latitudes (but at any longitude), and within a range of longitudes, but not both simultaneously.&lt;/p&gt;
&lt;p&gt;An option is to translate a two-dimensional location into a single number using a space-filling curve(?) and then use a regular B-tree index.&lt;/p&gt;
&lt;h4 id=&quot;full-text-search-and-fuzzy-indexes&quot;&gt;Full-text search and fuzzy indexes &lt;a class=&quot;direct-link&quot; href=&quot;#full-text-search-and-fuzzy-indexes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The indexes we&#39;ve discussed so far assume that we have exact data, and we know the exact values of a key, or a range of values of a key with a sort order. For dealing with things like searching &lt;em&gt;similar&lt;/em&gt; keys, such as misspelled words, we look at &lt;em&gt;fuzzy&lt;/em&gt; querying techniques.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Levenshtein automaton&lt;/em&gt;: Supports efficient search for words within a given edit distance.&lt;/p&gt;
&lt;h4 id=&quot;keep-everything-in-memory&quot;&gt;Keep everything in memory &lt;a class=&quot;direct-link&quot; href=&quot;#keep-everything-in-memory&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The data structures discussed so far provide answers to the limitations of disks. Disks are awkward to deal with compared to main memory. With both magnetic disks and SSDs, data on disk must be laid out carefully to get good read and write performance. Disks have 2 significant advantages over main memory though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They are durable. Content is not lost if power is turned off.&lt;/li&gt;
&lt;li&gt;They have a lower cost per gigabyte than RAM.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There have been developments of &lt;em&gt;in-memory databases&lt;/em&gt; lately, especially since RAM has become cheaper and many datasets are not that big so keeping them in memory is feasible.&lt;/p&gt;
&lt;p&gt;In-memory databases aim for durability by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using special hardware (battery-powered RAM)&lt;/li&gt;
&lt;li&gt;Writing a log of changes to disk&lt;/li&gt;
&lt;li&gt;Writing periodic snapshots to disk&lt;/li&gt;
&lt;li&gt;Replicating the in-memory state to other machines.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;VoltDB, MemSQL and Oracle TimesTen are in-memory databases with a relational model.&lt;/p&gt;
&lt;p&gt;Interestingly, the performance advantage of in-memory databases is not due to the fact that they don&#39;t need to read from disk. A disk-based storage engine may never need to read from disk if there&#39;s enough memory, because the OS caches recently used data blocks in memory anyway. Rather, they can be faster because there&#39;s no overhead of encoding in-memory data structures in a form that can be written to disk.&lt;/p&gt;
&lt;p&gt;Besides performance, an interesting area for in-memory databases is that they allow for the use of data models that are difficult to implement with disk-based indexes. E.g. Redis offers a db-like interface to data structures such as priority queues and sets.&lt;/p&gt;
&lt;p&gt;Recent research indicates that in-memory database architecture can be extended to support datasets larger than the available memory, without bringing back the overheads of a disk-centric architecture. This approach works by evicting the least recently used data from memory to disk when there&#39;s not enough memory, and loading it back into memory when it&#39;s accessed again in the future.&lt;/p&gt;
&lt;h3 id=&quot;transaction-processing-vs-analytics&quot;&gt;Transaction Processing vs Analytics &lt;a class=&quot;direct-link&quot; href=&quot;#transaction-processing-vs-analytics&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Transaction: A group of reads and writes that form a logical unit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OLAP&lt;/strong&gt;: Online Analytics Processing. Refers to queries generally performed by business analytics that scan over a huge number of record and calculating aggregate statistics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OLTP:&lt;/strong&gt; Online Transaction Processing. Interactive queries which typically return a small number of records.&lt;/p&gt;
&lt;p&gt;In the past, OLTP-type queries and OLAP-type queries were performed on the same databases. However, there&#39;s been a push for OLAP-type queries to be run on &lt;strong&gt;data warehouses.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;data-warehousing&quot;&gt;Data Warehousing &lt;a class=&quot;direct-link&quot; href=&quot;#data-warehousing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A data warehouse is a separate DB that analysts can query without affecting OLTP operations. The data warehouse contains a read-only copy of the data in all the various OLTP systems in the company. Data is extracted from OLTP databases and loaded into the warehouse using an ETL (&lt;em&gt;Extract-Transform-Load)&lt;/em&gt; process.&lt;/p&gt;
&lt;p&gt;It turns out that the indexing algorithms discussed so far work well for OLTP, but not so much for answering analytics queries.&lt;/p&gt;
&lt;p&gt;Transaction processing and data warehousing databases look similar, but the latter is optimized for analytics queries. They are both often accessible through a common SQL interface though.&lt;/p&gt;
&lt;p&gt;A number of SQL-on-Hadoop data warehouses have emerged such as Apache Hive, Spark SQL, Cloudera Impala etc.&lt;/p&gt;
&lt;h4 id=&quot;stars-and-snowflakes%3A-schemas-for-analytics&quot;&gt;Stars and Snowflakes: Schemas for Analytics &lt;a class=&quot;direct-link&quot; href=&quot;#stars-and-snowflakes%3A-schemas-for-analytics&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Many data warehouses are used in a fairly formulaic style - &lt;em&gt;star schema&lt;/em&gt; (or dimensional modelling). The name &amp;quot;star schema&amp;quot; comes from the fact that when the table relationships are visualized, the fact tables is in the middle, surrounded by its dimension tables (these represent the who, what, where, when, how, and why); The connections to the these tables are like the rays of a star.&lt;/p&gt;
&lt;p&gt;We also have the &lt;em&gt;snowflake schema,&lt;/em&gt; where dimensions are further broken down into subdimensions.&lt;/p&gt;
&lt;h3 id=&quot;column-oriented-storage&quot;&gt;Column Oriented Storage &lt;a class=&quot;direct-link&quot; href=&quot;#column-oriented-storage&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In most OLTP databases, storage is laid out in a &lt;em&gt;row-oriented&lt;/em&gt; fashion: all the values from one row of a table are stored next to each other. Document databases are similar: an entire document is typically stored as one contiguous sequence of bytes.&lt;/p&gt;
&lt;p&gt;Analytics queries often access millions of rows, but few columns.&lt;/p&gt;
&lt;p&gt;The idea behind &lt;em&gt;column-oriented storage&lt;/em&gt; is straight forward: don&#39;t store all the values from one row together, but store all the values from each &lt;em&gt;column&lt;/em&gt; together instead. If each column is stored in a separate file, a query only needs to read and parse the columns that it is interested in, which can save work.&lt;/p&gt;
&lt;p&gt;The column-oriented storage layout relies on each column file containing the rows in the same order.&lt;/p&gt;
&lt;h4 id=&quot;column-compression&quot;&gt;Column Compression &lt;a class=&quot;direct-link&quot; href=&quot;#column-compression&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In addition to loading only the columns from disk that are required for a query, we can reduce the demands on disk throughput by compressing data. Column-oriented storage lends itself well to compression. Different compression techniques can be used such as &lt;em&gt;Bitmap encoding.&lt;/em&gt; The number of distinct values in a column is often small compared to the number of rows. Therefore, we can take a column with &lt;em&gt;n&lt;/em&gt; distinct values and turn it into &lt;em&gt;n&lt;/em&gt; separate bitmaps: one bitmap for each distinct value, with one bit for each row. The bit is 1 if the row has that value, and 0 if not.&lt;/p&gt;
&lt;p&gt;So I believe that in column oriented storage, each &#39;file&#39; for a column has one row, where the number of columns for that row will be the number of rows in a standard row-wise table, and columns contain the rows in the same order.&lt;/p&gt;
&lt;h4 id=&quot;column-oriented-storage-and-column-families&quot;&gt;Column-oriented storage and column families &lt;a class=&quot;direct-link&quot; href=&quot;#column-oriented-storage-and-column-families&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Cassandra and Hbase have a concept of &lt;em&gt;column families&lt;/em&gt;, which differs from being column oriented. Within each column family, they store all the columns from a row together, along with a row key, and do not use column compression.&lt;/p&gt;
&lt;h4 id=&quot;sort-order-in-column-storage&quot;&gt;Sort Order in Column Storage &lt;a class=&quot;direct-link&quot; href=&quot;#sort-order-in-column-storage&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It doesn&#39;t really matter in which order the rows are stored in a column store. It&#39;s easiest to store them in the order of insertion, but we can choose to impose an order.&lt;/p&gt;
&lt;p&gt;It won&#39;t make sense to sort each column individually though because we&#39;ll lose track of which columns belong to the same row. Rather, we sort the entire row at a time, even though it is stored by column. We can choose the &lt;em&gt;columns&lt;/em&gt; by which the table should be sorted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Several different sort orders&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://vldb.org/pvldb/vol5/p1790_andrewlamb_vldb2012.pdf&quot;&gt;&lt;strong&gt;C-stores&lt;/strong&gt;&lt;/a&gt; provide an extension to sorting on column stores. Different queries benefit from different sort orders, so why not store the same data sorted in several different ways?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Writing to Column-Oriented Storage&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Writes are more difficult with column-oriented storage. An update-in-place approach, like B-trees use, is not possible with compressed columns. To insert a row in the middle of a sorted table, you would likely have to rewrite all the column files.&lt;/p&gt;
&lt;p&gt;Fortunately, a good approach for writing has been discussed earlier: LSM-trees. All writes go to an in-memory store first, where they are added to a sorted structure and prepared for writing to disk. It doesn&#39;t matter whether the in-memory store is row-oriented or column-oriented.&lt;/p&gt;
&lt;h1 id=&quot;further-reading%3A&quot;&gt;Further reading: &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading%3A&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Oracle internals: &lt;a href=&quot;https://stackoverflow.com/a/40740893&quot; title=&quot;https://stackoverflow.com/a/40740893&quot;&gt;https://stackoverflow.com/a/40740893&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;How indexes work: &lt;a href=&quot;https://stackoverflow.com/questions/1108/how-does-database-indexing-work&quot; title=&quot;https://stackoverflow.com/questions/1108/how-does-database-indexing-work&quot;&gt;https://stackoverflow.com/questions/1108/how-does-database-indexing-work&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SQL Server indexes: &lt;a href=&quot;https://sqlity.net/en/2445/b-plus-tree/&quot; title=&quot;https://sqlity.net/en/2445/b-plus-tree/&quot;&gt;https://sqlity.net/en/2445/b-plus-tree/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;How data is stored on disk: &lt;a href=&quot;https://www.red-gate.com/simple-talk/sql/database-administration/sql-server-storage-internals-101/&quot; title=&quot;https://www.red-gate.com/simple-talk/sql/database-administration/sql-server-storage-internals-101/&quot;&gt;https://www.red-gate.com/simple-talk/sql/database-administration/sql-server-storage-internals-101/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Last updated on 03-03-2021&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 2 - Data Models and Query Languages</title>
		<link href="https://timilearning.com/posts/ddia/part-one/chapter-2/"/>
		<updated>2019-12-07T16:20:45-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-one/chapter-2/</id>
		<content type="html">&lt;p&gt;These are my notes from the second chapter of Martin Kleppmann&#39;s Designing Data Intensive Applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#relational-model-versus-document-model&quot;&gt;Relational Model Versus Document Model&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#relational-versus-document-databases-today&quot;&gt;Relational Versus Document Databases Today&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#schema-flexibility-in-the-document-model&quot;&gt;Schema Flexibility in the document model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#data-locality-for-queries&quot;&gt;Data locality for queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#convergence-of-document-and-relational-databases&quot;&gt;Convergence of document and relational databases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#query-languages-for-data&quot;&gt;Query Languages For Data&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#mapreduce-querying&quot;&gt;MapReduce Querying&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#graph-like-data-models&quot;&gt;Graph-Like Data Models&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This chapter surveys a couple of data models for storing and querying data, as well as different query languages.&lt;/p&gt;
&lt;h2 id=&quot;relational-model-versus-document-model&quot;&gt;Relational Model Versus Document Model &lt;a class=&quot;direct-link&quot; href=&quot;#relational-model-versus-document-model&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SQL is the best known data model today. Two of the earlier competitors of this model were the &lt;em&gt;network model&lt;/em&gt; and the &lt;em&gt;hierarchical&lt;/em&gt; model. NoSQL is the latest attempt to overthrow SQL&#39;s dominance. Some of the driving forces behind NoSQL&#39;s dominance are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A need for greater scalability than relational databases can easily achieve, including very large datasets or very high write throughput.&lt;/li&gt;
&lt;li&gt;Preference for free &amp;amp; open source software over commercial DB products.&lt;/li&gt;
&lt;li&gt;Some specialized query operations not well supported by the relational model.&lt;/li&gt;
&lt;li&gt;Frustration with the restrictiveness of relational schemas, and a desire for a more dynamic and expressive data model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NoSQL Databases are in two forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Document databases&lt;/strong&gt;: Targets use cases where data comes in self-contained documents and relationships between one document and another are rare.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Graph databases:&lt;/strong&gt; These go in the opposite direction, they target use cases where anything is potentially related to everything.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Impedance Mismatch&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An awkward translation layer is required between objects stored in relational tables and the application code. The disconnect between the models is called an &lt;em&gt;impedance mismatch.&lt;/em&gt; ORMs usually help with this but they don&#39;t hide all the differences between both models.&lt;/p&gt;
&lt;p&gt;Some developers feel that the JSON model reduces impedance mismatch between the application layer and the database layer.&lt;/p&gt;
&lt;p&gt;While relational models refer to a related item by a unique identifier called the &lt;em&gt;foreign key,&lt;/em&gt; document models use the &lt;em&gt;document reference.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;relational-versus-document-databases-today&quot;&gt;Relational versus document databases today &lt;a class=&quot;direct-link&quot; href=&quot;#relational-versus-document-databases-today&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Both data models have arguments going for them. For the relational model, it provides better support for joins, and many to one and many to many relationships.&lt;/p&gt;
&lt;p&gt;The document data model has the advantages of schema flexibility, better performance due to locality, and for some applications, it&#39;s closer to the data structures used by the application.&lt;/p&gt;
&lt;p&gt;If data in the application has a document-like structure (i.e. a tree of one-to-many relationships, where typically the entire tree is loaded at once), document model is a good idea. Document databases typically have poor support for many-to-many relationships. In an analytics application, for example, many to many relationships may never be needed.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Basically, for highly interconnected data, the document model is awkward, the relational model is acceptable, and graph models are the most natural.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;schema-flexibility-in-the-document-model&quot;&gt;Schema flexibility in the document model &lt;a class=&quot;direct-link&quot; href=&quot;#schema-flexibility-in-the-document-model&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Document databases have the advantage of having an implicit schema which is not enforced by the database: Also known as &lt;em&gt;schema-on-read&lt;/em&gt;. The structure of the data is implicit, and only interpreted when the data is read. This is contrasted with &lt;em&gt;schema-on-write&lt;/em&gt;,  where the schema is explicit and the database ensures all written data conforms to it.&lt;/p&gt;
&lt;h4 id=&quot;data-locality-for-queries&quot;&gt;Data locality for queries &lt;a class=&quot;direct-link&quot; href=&quot;#data-locality-for-queries&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There is a performance advantage to data locality in the document model if you need to access large parts of the document at the same time. Compared with many relational databases where data is usually spread among tables, there is less need for disk seeks and it takes less time.&lt;/p&gt;
&lt;p&gt;There are a couple of tools nowadays that offer this locality in a relational model, e.g. Google Spanner, Oracle (multi-table index cluster tables), and in a BigTable model, e.g. Cassandra and HBase (column-family concept).&lt;/p&gt;
&lt;h4 id=&quot;convergence-of-document-and-relational-databases&quot;&gt;Convergence of document and relational databases &lt;a class=&quot;direct-link&quot; href=&quot;#convergence-of-document-and-relational-databases&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Relational databases like PostgresSQL, MySQL and IBM DB2 have added support for JSON documents in recent times.&lt;/p&gt;
&lt;p&gt;Document databases like RethinkDB also support relational-like joins in its query language.&lt;/p&gt;
&lt;p&gt;These models are becoming more similar over time.&lt;/p&gt;
&lt;h2 id=&quot;query-languages-for-data&quot;&gt;Query Languages For Data &lt;a class=&quot;direct-link&quot; href=&quot;#query-languages-for-data&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There is a distinction here between imperative query languages and declarative.&lt;/p&gt;
&lt;p&gt;An imperative language tells the computer to perform certain operations in a certain order. A declarative language specifies the pattern of the data wanted, and how the data should be transformed, but not &lt;em&gt;how&lt;/em&gt; to achieve that goal.&lt;/p&gt;
&lt;p&gt;An advantage of this declarative approach to query languages is that it hides the implementation details of the database engine, which makes it possible for the database system to introduce performance improvements without requiring any changes to query.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;HTML and CSS are also declarative languages.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;mapreduce-querying&quot;&gt;MapReduce Querying &lt;a class=&quot;direct-link&quot; href=&quot;#mapreduce-querying&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;MapReduce is a paradigm for querying large amounts of data in bulk across many machines. Basically, map jobs are run on all the machines in parallel, and then the results are reduced.&lt;/p&gt;
&lt;p&gt;It&#39;s neither a declarative query language nor a fully imperative query API, but somewhere in between. The logic of the query is expressed with code but it&#39;s repeatedly called by the processing framework.&lt;/p&gt;
&lt;p&gt;Both the &lt;em&gt;map&lt;/em&gt; and the &lt;em&gt;reduce&lt;/em&gt; functions must be pure functions, they only use data passed to them as input, and they cannot perform additional database queries, and they must not have any side effects.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;MongoDB has implemented aggregation pipelines, which are similar to MapReduce jobs.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;graph-like-data-models&quot;&gt;Graph-Like Data Models &lt;a class=&quot;direct-link&quot; href=&quot;#graph-like-data-models&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If many-to-many relationships are common, the Graph model is probably best suited for it.&lt;/p&gt;
&lt;p&gt;Graphs are not limited to homogenous data. Facebook, for example, maintains a single graph with many different types of vertices and edges. Their vertices represent: people, location, events, checkins, comments made by users. Their edges indicate which people are friends with each other, which checkin happened in which location, who commented on what, who attended which event etc.&lt;/p&gt;
&lt;p&gt;There are a couple of different but related ways for representing graphs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Property graph model: Neo4j, Titan and InfiniteGraph&lt;/li&gt;
&lt;li&gt;Triple-store model: Datomic, AllegroGraph&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some declarative languages for querying graphs are: Cypher, SPARQL and Datalog.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Chapter 1 - Reliable, Scalable and Maintainable Applications</title>
		<link href="https://timilearning.com/posts/ddia/part-one/chapter-1/"/>
		<updated>2019-12-07T16:18:56-00:00</updated>
		<id>https://timilearning.com/posts/ddia/part-one/chapter-1/</id>
		<content type="html">&lt;p&gt;These are my notes from the first chapter of Martin Kleppmann&#39;s Designing Data Intensive Applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#reliability&quot;&gt;Reliability&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#hardware-faults&quot;&gt;Hardware Faults&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#software-errors&quot;&gt;Software Errors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#human-errors&quot;&gt;Human Errors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#scalability&quot;&gt;Scalability&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#describing-load&quot;&gt;Describing Load&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#maintainability&quot;&gt;Maintainability&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Three important concerns in most software systems are reliability, scalability, and maintainability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reliability:&lt;/strong&gt; The system should work correctly (performing the correct function at the desired level of performance) even in the face of adversity.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability:&lt;/strong&gt; As the system grows(in data , traffic volume, or complexity), there should be reasonable ways of dealing with that growth.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintainability:&lt;/strong&gt; People should be able to work on the system productively in the future.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;reliability&quot;&gt;Reliability &lt;a class=&quot;direct-link&quot; href=&quot;#reliability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Typical expectations for software to be termed as reliable are that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The application performs as expected.&lt;/li&gt;
&lt;li&gt;It can tolerate user mistakes or unexpected usage of the product.&lt;/li&gt;
&lt;li&gt;Performance is good enough for the required use case, under the expected load and data volume.&lt;/li&gt;
&lt;li&gt;The system prevents any unauthorized access and abuse.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Basically, a reliable system is fault-tolerant or resilient. Fault is different from failure. A fault is one component of the system deviating from its spec, whereas a failure is when the system as a whole stops providing the required service to a user.&lt;/p&gt;
&lt;p&gt;It&#39;s impossible to reduce the probability of faults to zero, thus, it&#39;s useful to design fault-tolerance mechanisms that prevent faults from causing failures.&lt;/p&gt;
&lt;h4 id=&quot;hardware-faults&quot;&gt;Hardware Faults &lt;a class=&quot;direct-link&quot; href=&quot;#hardware-faults&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Traditionally, the standard approach for dealing with hardware faults is to add redundancy to the individual hardware components so that if one fails, it can be replaced: &lt;a href=&quot;https://en.wikipedia.org/wiki/Standard_RAID_levels&quot;&gt;RAID configuration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As data volumes and applications&#39; computing demands have increased, there&#39;s been a shift towards using software fault-tolerance techniques in preference or in addition to hardware redundancy. This offers a number of advantages. For example, a single server system will require planned downtime if the machines needs to be rebooted (e.g. to apply operating system security patches). But for a system that can tolerate machine failure, it can be patched one node at a time (without downtime of the entire system - a rolling upgrade).&lt;/p&gt;
&lt;h4 id=&quot;software-errors&quot;&gt;Software Errors &lt;a class=&quot;direct-link&quot; href=&quot;#software-errors&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Software failures are more correlated than hardware failures. Meaning that a fault in one node is more likely to cause many more system failures than uncorrelated hardware failures.&lt;/p&gt;
&lt;p&gt;There is no quick solution to the problem of faults in software, but a few things can help:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Thorough testing&lt;/li&gt;
&lt;li&gt;Process isolation&lt;/li&gt;
&lt;li&gt;Measuring, monitoring, and analyzing system behavior in production.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;human-errors&quot;&gt;Human Errors &lt;a class=&quot;direct-link&quot; href=&quot;#human-errors&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Humans are known to be unreliable. How do we make systems reliable, in spite of unreliable humans? Through a combination of several approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Designing systems in a way that minimize opportunities for error through well-designed abstractions, APIs, and admin interfaces.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Decoupling the places where people make the most mistakes from the places where they can cause failures. E.g. by providing a fully-featured non-production sandbox environment where people can explore and experiment safely, using real data, without affecting real users.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Testing thoroughly at all levels: from unit tests to integration tests to manual tests to automated tests.
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Allow quick and easy recovery from human errors, to minimize the impact of failure. E.g. By making it easy to roll back configuration changes, roll out new code gradually (so bugs do not affect all users).
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Set up detailed and clear monitoring, such as performance metrics and error rates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have a responsibility to our users, and hence reliability is very important.&lt;/p&gt;
&lt;h3 id=&quot;scalability&quot;&gt;Scalability &lt;a class=&quot;direct-link&quot; href=&quot;#scalability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Scalability describes the ability to cope with increased load. It is meaningless to say &amp;quot;X is scalable&amp;quot; or &amp;quot;Y doesn&#39;t scale&amp;quot;. Discussing scalability is about answering the question of &amp;quot;If the system grows in a particular way, what are our options for coping with the growth?&amp;quot; and &amp;quot;How can we add computing resources to handle the additional load?&amp;quot;&lt;/p&gt;
&lt;h4 id=&quot;describing-load&quot;&gt;Describing Load &lt;a class=&quot;direct-link&quot; href=&quot;#describing-load&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Load can be described by the &lt;em&gt;load parameters.&lt;/em&gt; The choice of parameters depends on the system architecture. It may be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requests per second to a web server&lt;/li&gt;
&lt;li&gt;Ratio of reads to writes in a database&lt;/li&gt;
&lt;li&gt;Number of simultaneously active users in a chat room&lt;/li&gt;
&lt;li&gt;Hit rate on a cache.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Twitter Case-study&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Twitter has two main operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Posting a tweet: 4.6k requests/sec on average, 12k requests/sec at peak.&lt;/li&gt;
&lt;li&gt;Home timeline: A user can view tweets posted by the people they follow (300k requests/sec)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(&lt;em&gt;Based on data published in 2012:&lt;/em&gt; &lt;a href=&quot;https://www.infoq.com/presentations/Twitter-Timeline-Scalability/&quot; title=&quot;https://www.infoq.com/presentations/Twitter-Timeline-Scalability/&quot;&gt;https://www.infoq.com/presentations/Twitter-Timeline-Scalability/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Twitter&#39;s scaling challenge is primarily due to &lt;em&gt;fan-out&lt;/em&gt;. In electronics, fan-out is the number of input gates that are attached to another gate&#39;s output. For twitter, each user follows many people, and each user is followed by many people. What is an optimal way of loading all the tweets of people followed by a user? Two operations could happen:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Posting a tweet inserts the new tweet into a global collection of tweets. This collection could be a relational database which could then have billions of rows - &lt;em&gt;not ideal.&lt;/em&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Maintain a cache for each user&#39;s home timeline - like a mailbox of tweets for each recipient user. When a user posts a tweet, look up all the people who follow the user, and insert the new tweet into each of their home timeline caches. Reading the home timeline is cheap.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Twitter used the first approach initially but they now use approach 2. The downside to approach 2 is that posting a tweet requires a lot of extra work.&lt;/p&gt;
&lt;p&gt;For twitter, the distribution of followers per user (may be weighted by how often those users tweet) is a key load parameter for discussing scalability, since it determines the fan-out load.&lt;/p&gt;
&lt;p&gt;Note that Twitter is now implementing a hybrid of both approaches. For most users, tweets continue to be fanned out to home timelines at the time when they are posted. However, for a small number of users with millions of followers (celebrities), they are exempted from the fan out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Describing Performance&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once the load on the system has been described, you can investigate what happens when the load increases in the following two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you increase a load parameter and keep system resources (CPU, memory, network, bandwidth, etc.) unchanged, how is the performance of the system affected?&lt;/li&gt;
&lt;li&gt;When you increase a load parameter, how much do you need to increase the resources if you want to keep performance unchanged?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both questions require numbers.&lt;/p&gt;
&lt;p&gt;For Batch processing systems like Hadoop, we care about &lt;em&gt;throughput -&lt;/em&gt; the number of records we can process per second, or total time it takes to run a job on a dataset of a certain size. For online systems, we care about &lt;em&gt;response time -&lt;/em&gt; the time between a client sending a request and receiving a response.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Aside&lt;/em&gt; &lt;strong&gt;-&lt;/strong&gt; &lt;em&gt;Latency vs response time:&lt;/em&gt; These two words are often used as synonyms, but they are not the same. Response time is what the client sees: besides the actual time to process a request(&lt;em&gt;service time),&lt;/em&gt; it includes network delays and queuing delays. Latency is the duration that a request is waiting to be handled - during which it is &lt;em&gt;latent,&lt;/em&gt; awaiting service.&lt;/p&gt;
&lt;p&gt;Response time can vary a lot, therefore it&#39;s important to think of response time not as a single value, but as a distribution of values. Even in a scenario where you&#39;d think all requests should take the same time, you get variation: random additional latency could be introduced by a context switch on the background process, loss of a network packet and TCP retransmission, a garbage collection pause, etc.&lt;/p&gt;
&lt;p&gt;People often report the average response time of a service, but it is not a very good metric if you want to know your &amp;quot;typical&amp;quot; response time, because it doesn&#39;t tell you how many users actually experienced that delay. A better approach is to use &lt;em&gt;percentiles.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Percentiles&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you take your list of response times and sort from fastest to slowest, then the &lt;em&gt;median&lt;/em&gt; is the halfway point: e.g. if median response time is 200ms, it means half the requests return in less than 200ms and half take longer than that. Thus, median is a good metric if you want to know how long users typically wait. Median is known as the &lt;em&gt;50th percentile,&lt;/em&gt; and sometimes abbreviated as &lt;em&gt;p50.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Occasionally slow requests are known as outliers. In order to figure out how bad they are, you look at higher percentiles: &lt;em&gt;95th, 99th and 99.9th&lt;/em&gt; percentiles are common. These are the response time thresholds at which 95%, 99%, or 99.9% of requests are faster than that particular threshold. For example, if the 95th percentile response time is 1.5 seconds, it means 95 out of 100 requests take less than 1.5 seconds, and 5 out of 100 requests take 1.5 seconds or more.&lt;/p&gt;
&lt;p&gt;High percentiles of response times (aka. Tail latencies) are important because they directly impact a user&#39;s experience.&lt;/p&gt;
&lt;p&gt;Percentiles are often used in SLOs (Service Level Objectives) and SLAs (Service Level Agreements). They set the expectations of a user, and a refund may be demanded if the expectation is not met.&lt;/p&gt;
&lt;p&gt;A large part of the response time at high percentiles can be accounted for by queuing delays, which refers to how a number of slow requests on the server-side can hold up the processing of subsequent requests. This effect is called &lt;em&gt;Head-of-Line blocking.&lt;/em&gt; Those requests may be fast to process on the server, but the client will see a slow overall response time due to the time waiting for the prior request to complete. &lt;em&gt;This is why it is important to measure response times on the client side.&lt;/em&gt; Basically, requests could be fast individually but one slow request could slow down all the other requests.&lt;/p&gt;
&lt;p&gt;It takes just one slow call to make the entire end-user request slow, an effect known as tail latency amplification.&lt;/p&gt;
&lt;p&gt;If you want to monitor response times for a service on a dashboard, you need to monitor it on an ongoing basis. A good idea is to keep a rolling window of response times of requests in the last 10 minutes. So there could be a graph of the median and various percentiles over that window.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Averaging percentiles is useless, the right way of aggregating response time data is by adding histograms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Approaches for Coping with Load&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An architecture that is appropriate for one level of load is unlikely to cope with 10 times that load. Different terms that come up for dealing with this include vertical scaling, which is moving to a more powerful machine, and  scaling out/horizontal scaling, which involves distributing the load across multiple smaller machines. Distributing load across multiple machines is also known as &lt;em&gt;shared-nothing&lt;/em&gt; architecture.&lt;/p&gt;
&lt;p&gt;There&#39;s less of a dichotomy between both approaches, and more of a pragmatic mixture of both approaches.&lt;/p&gt;
&lt;p&gt;There&#39;s no generic one-size fits all approach for the architecture of large scale data systems, it&#39;s usually highly specific to the application.&lt;/p&gt;
&lt;p&gt;An architecture that scales well for a particular application is built around assumptions of which operations will be common and which will be rare: the load parameters.&lt;/p&gt;
&lt;p&gt;Note that though they are specific to a particular application, scalable architectures are nevertheless built from general-purpose building blocks, arranged in familiar patterns.&lt;/p&gt;
&lt;h3 id=&quot;maintainability&quot;&gt;Maintainability &lt;a class=&quot;direct-link&quot; href=&quot;#maintainability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Design software in a way that it will minimize pain during maintenance, thereby avoiding the creation of legacy software by ourselves. Three design principles for software systems are:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Operability:&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Make it easy for operations teams to keep the system running smoothly.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Simplicity&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Make it easy for new engineers to understand the system, by removing as much complexity as possible from the system.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Evolvability&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Make it easy for engineers to make changes to the system in the future, adapting it for unanticipated use cases as requirements change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Operability: Making Life Easy For Operations&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Operations teams are vital to keeping a software system running smoothly. A system is said to have good operability if it makes routine tasks easy so that it allows the operations teams to focus their efforts on high-value activities. Data systems can do the following to make routine tasks easy e.g.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Providing visibility into the runtime behavior and internals of the system, with good monitoring.&lt;/li&gt;
&lt;li&gt;Providing good support for automation and integration with standard tools.&lt;/li&gt;
&lt;li&gt;Providing good documentation and easy-to-understand operational model (&amp;quot;If I do X, Y will happen&amp;quot;).&lt;/li&gt;
&lt;li&gt;Self-healing where appropriate, but also giving administrators manual control over the system state when needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Simplicity: Managing Complexity&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Reducing complexity improves software maintainability, which is why simplicity should be a key goal for the systems we build.&lt;/p&gt;
&lt;p&gt;This does not necessarily refer to reducing the functionality of a system, it can also mean reducing &lt;em&gt;accidental&lt;/em&gt; complexity. Complexity is accidental if it is not inherent in the problem that the software solves (as seen by the users) but arises only from the implementation.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Abstraction&lt;/em&gt; is one of the best tools that we have for dealing with accidental complexity. A good abstraction can hide a great deal of implementation detail behind a clean, simple-to-understand façade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Evolvability: Making Change Easy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;System requirements change constantly and we must ensure that we&#39;re able to deal with those changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An application has to meet functional and non-functional requirements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Functional -&lt;/em&gt;&lt;/strong&gt; What it should do, such as allowing data to be stored, retrieved, searched, and processed in various ways.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Nonfunctional -&lt;/em&gt;&lt;/strong&gt; General properties like security, reliability, compliance, scalability, compatibility, and maintainability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Reliability&lt;/strong&gt; means making systems work correctly, even when faults occur.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt; means having strategies for keeping performance good, even when load increases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt; is in essence about making life better for the engineering and operations teams who need to work with the system.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Learning Diary: Designing Data Intensive Applications by Martin Kleppmann</title>
		<link href="https://timilearning.com/posts/ddia/notes/"/>
		<updated>2019-12-07T15:44:17-00:00</updated>
		<id>https://timilearning.com/posts/ddia/notes/</id>
		<content type="html">&lt;h3 id=&quot;background&quot;&gt;Background &lt;a class=&quot;direct-link&quot; href=&quot;#background&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I tend to read a technical book twice before I can convince myself that I&#39;ve actually read the book. The first time is typically during my commute to work, and the second time is when I&#39;m home and try to take notes from the book.&lt;/p&gt;
&lt;p&gt;This post is to share the notes I&#39;ve taken while reading Martin Kleppmann&#39;s book: &lt;a href=&quot;http://dataintensive.net/&quot;&gt;Designing Data-Intensive Applications.&lt;/a&gt; This was inspired by &lt;a href=&quot;https://jasdev.me/notes/&quot;&gt;Jasdev&lt;/a&gt;&#39;s attempt to learn in public and I hope that you can learn a thing or two from my notes.&lt;/p&gt;
&lt;p&gt;A few disclaimers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is not meant to be a substitute for the book, and you will rob yourself of a lot of useful knowledge if you use these notes as a replacement for the book.&lt;/li&gt;
&lt;li&gt;The first seven chapter notes I&#39;ll share were written without a public audience in mind. I had no intention of sharing these notes at the beginning. As such, you might find some mistakes. I&#39;ve tried to get rid of those, but it&#39;s possible I missed some. If you find any that you want to correct, please reach out via email too.&lt;/li&gt;
&lt;li&gt;Lastly, these posts will differ from the more expository deep-dive posts like &lt;a href=&quot;https://timilearning.com/posts/data-storage-on-disk/part-one/&quot;&gt;this one&lt;/a&gt; that I&#39;ll also post on this blog. If you manage to gain from my learning diary, that&#39;s great! If you come across a topic that you want to learn more about, you can leverage the numerous resources online about that topic, or reach out to me to write about it.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Part One - Foundations of Data Systems&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-one/chapter-1&quot;&gt;Chapter 1 - Reliable, Scalable and Maintainable Applications.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-one/chapter-2&quot;&gt;Chapter 2 - Data Models and Query Languages.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-one/chapter-3&quot;&gt;Chapter 3 - Storage and Retrieval.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-one/chapter-4&quot;&gt;Chapter 4 - Encoding and Evolution.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Part Two - Distributed Data&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-5&quot;&gt;Chapter 5 - Replication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-6&quot;&gt;Chapter 6 - Partitioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-7&quot;&gt;Chapter 7 - Transactions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-8&quot;&gt;Chapter 8 - The Trouble with Distributed Systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-1&quot;&gt;Chapter 9 - Consistency and Consensus (Part One)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timilearning.com/posts/ddia/part-two/chapter-9-2&quot;&gt;Chapter 9 - Consistency and Consensus (Part Two)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Data Storage on Your Computer&#39;s Disk - Part 1</title>
		<link href="https://timilearning.com/posts/data-storage-on-disk/part-one/"/>
		<updated>2019-10-22T21:36:52-00:00</updated>
		<id>https://timilearning.com/posts/data-storage-on-disk/part-one/</id>
		<content type="html">&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#a-bit-about-data-storage-on-disk&quot;&gt;A bit about data storage on disk&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#records&quot;&gt;Records&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#pages&quot;&gt;Pages&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#writeback&quot;&gt;Writeback&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#write-ahead-logs&quot;&gt;Write-ahead Logs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#transactions&quot;&gt;Transactions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion%2Fnext-steps&quot;&gt;Conclusion/Next Steps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#open-question-i-still-have&quot;&gt;Open Question I Still Have&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#further-reading&quot;&gt;Further Reading&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#general-overview-of-distributed-systems&quot;&gt;General Overview of Distributed Systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#records-%26-pages&quot;&gt;Records &amp;amp; Pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#write-ahead-logs-1&quot;&gt;Write-ahead Logs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;background&quot;&gt;Background &lt;a class=&quot;direct-link&quot; href=&quot;#background&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;You may skip this story and go straight to the main post.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve been reading Martin Kleppmann&#39;s great book on &lt;a href=&quot;http://dataintensive.net/&quot;&gt;&amp;quot;Designing Data-Intensive Applications&amp;quot;&lt;/a&gt; for some time now.&lt;/p&gt;
&lt;p&gt;In the third chapter of the book which deals with the storage and retrieval of data, Martin explains a lot about what database indexes are, how they work, the different types of indexing structures we have, and so on. I found this interesting to read, but I kept wondering &lt;em&gt;how&lt;/em&gt; or &lt;em&gt;if&lt;/em&gt; those database indexes differed from the underlying data being stored. He did a good job of explaining this, but I found it difficult to visualize how these indexing structures are actually laid out on disk with data.&lt;/p&gt;
&lt;p&gt;This led me on a journey of naive Google searches like: &amp;quot;&lt;em&gt;is the lsm tree an index or a storage engine&amp;quot;&lt;/em&gt; and &amp;quot;&lt;em&gt;is the b tree simply an index&lt;/em&gt;&amp;quot;. In the end, I think I have a decent understanding of how database indexes are related to the data they store, and how those get represented on disk. I aim to detail what I have learned about how data is stored in these series of posts.&lt;/p&gt;
&lt;p&gt;This post is an introduction and will focus on how data is represented on your computer&#39;s disk without indexes.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Starting off with a few disclaimers:&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A lot of the concepts I&#39;ll discuss here are based on my knowledge of how SQLServer works. Don&#39;t be discouraged if you haven&#39;t used SQLServer though, I haven&#39;t either! I just found that the resources for SQLServer were more accessible to me than any other set of resources. My guess (and hope) is that though some of the implementation details may differ from other Relational Database Management Systems (think MySQL, PostgreSQL, etc), the underlying concepts are similar.&lt;/li&gt;
&lt;li&gt;I&#39;m no expert in any of these topics so please reach out to via the feedback form if you spot any errors in my understanding that you would like to correct.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;a-bit-about-data-storage-on-disk&quot;&gt;A bit about data storage on disk &lt;a class=&quot;direct-link&quot; href=&quot;#a-bit-about-data-storage-on-disk&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ll be using some words repeatedly in this section so I thought it&#39;ll be a good idea to list them here as a reference. I&#39;ll explain how they fit together in more detail later on, so stay with me!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disk I/O Operations:&lt;/strong&gt; These are read and write operations that involve accessing your computer&#39;s physical disk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Records:&lt;/strong&gt; A row in your database table maps to a &lt;em&gt;record&lt;/em&gt; on your computer&#39;s disk. Records can be of various sizes depending on the data contained in a row. There are different types of records, such as &lt;em&gt;data records&lt;/em&gt; - say for storing an individual book in a database of books, &lt;em&gt;index records,&lt;/em&gt; and records for other metadata about the database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pages:&lt;/strong&gt; A &lt;em&gt;page&lt;/em&gt; consists of many records. Pages have a fixed size which can be 4KB, 8KB, 16KB, etc. Fixed here means that a 4KB page cannot be expanded to fit more data when full. SQLServer pages are typically 8KB in size. Two of the most popular page types are &lt;em&gt;Data pages&lt;/em&gt; and &lt;em&gt;Index pages.&lt;/em&gt; As you can imagine, data pages store data records and index pages store index records. We&#39;ll learn more about these page types in this series.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;records&quot;&gt;Records &lt;a class=&quot;direct-link&quot; href=&quot;#records&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When you add a new row to a database table using SQL, that row is converted internally to a &lt;em&gt;record&lt;/em&gt;. Think of a record as an array of bytes, where each byte or group of bytes stores information about the record. The information stored in a group of bytes could be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The type of the record: data, index, or other metadata.&lt;/li&gt;
&lt;li&gt;Whether the inserted row has columns with null values or columns have fixed or variable length data types.&lt;/li&gt;
&lt;li&gt;Where each column&#39;s data starts and ends in the array.&lt;/li&gt;
&lt;li&gt;The actual data encoded in bytes, divided into separate sections depending on whether the data type used for storing the data has a fixed length or a variable length.&lt;/li&gt;
&lt;/ul&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/record-2.png&quot; alt=&quot;Inexact representation of a database record&quot;&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
Figure 1 - Inexact representation of a database record.
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Notes:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A more precise representation of what a record looks like can be found &lt;a href=&quot;https://www.red-gate.com/simple-talk/sql/database-administration/sql-server-storage-internals-101/&quot;&gt;here&lt;/a&gt;. The purpose of the diagram above is to help visualize the structure on a high level.&lt;/li&gt;
&lt;li&gt;The &amp;quot;Information about columns...&amp;quot; here typically includes: &lt;ol type=&quot;a&quot; style=&quot;margin-top:0px&quot;&gt; &lt;li&gt; The number of columns that match the condition i.e. fixed length, null or variable length.&lt;/li&gt; &lt;li&gt;The positions of those columns; where those columns start and end. This makes it easier to query the data for a particular column.&lt;/li&gt; &lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;pages&quot;&gt;Pages &lt;a class=&quot;direct-link&quot; href=&quot;#pages&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When this record is created, it is added to a &lt;em&gt;page.&lt;/em&gt; Databases tend to work with pages. A page is the smallest unit of data that a database will load into a memory cache. Let&#39;s back up a little and talk about why pages are important.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A database table could have billions of records or more. Storing these records side by side in a file could be a nightmare to manage. There will be some added complexity around storing and retrieving a record. Records are organized into pages to make them easier to work with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Imagine that a database needs to retrieve millions of records from disk and load them into memory. It would be difficult to determine how much space to allocate in memory for this operation. With pages, this becomes easier to manage since they are of a fixed length.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Think of pages in this case like pages in a notebook. A notebook page is made up of several lines of text. This organization makes it easier to find a line on a page once you have the page number. It also means that if you tear out a page from a notebook, you can read all the lines on that page without having to refer to the notebook multiple times. Pages in the context of disk storage minimize the number of disk I/O operations that will be needed to retrieve a record or a set of records.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two key things to note are these:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When you add a new record (row) or update an existing record in your database table, that change is reflected on a data page which exists in an in-memory cache.&lt;/li&gt;
&lt;li&gt;When you want to read a record from your database table, the system first checks if the page exists in the cache before hitting the disk otherwise.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The reason for this is simple: it is faster and less resource-intensive for the CPU to access data in the main memory i.e. in-memory cache than it is to go the disk to fetch data. However, this is not without its drawbacks. Unlike data stored in memory, data stored on your hard disk is durable. When you shut down your laptop, you still have the data on your hard disk. That&#39;s unfortunately not the case for data in the main memory.&lt;/p&gt;
&lt;p&gt;You may then wonder: &lt;em&gt;If a record that I insert or update is stored on a page located in the main memory, and data is lost if the computer is switched off, how are my changes prevented from being lost if something bad happens?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Good question! I&#39;ll address this soon, but not before I talk about some principles that guide how pages are loaded from the disk into the in-memory cache. These include:&lt;/p&gt;
&lt;p&gt;a) &lt;strong&gt;Temporal Locality:&lt;/strong&gt; Temporal locality means that pages are loaded in-memory based on the likelihood that a recently accessed page will be accessed again soon. It means that if you make a query that happens to fetch some data pages from disk, those data pages will be stored in memory for as long as possible to prevent having to go to the disk to fetch them again.&lt;/p&gt;
&lt;p&gt;b) &lt;strong&gt;Spatial Locality:&lt;/strong&gt; This works based on the prediction that if a page is loaded in memory, the pages that are stored physically close to that page on disk will likely be accessed soon. As a result, some pages are &#39;pre-fetched&#39; ahead of when they are actually used. It means that if you run a query that fetches a page which has records with IDs that range from 1-30, the page which has records from 31-60 will also likely be loaded alongside it to prevent a subsequent trip to the disk.&lt;/p&gt;
&lt;p&gt;These principles help to minimize the number of disk I/O operations needed.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;img src=&quot;https://timilearning.com/uploads/Page-actual-size.png&quot; alt=&quot;Structure of a database page&quot;&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
Figure 2 - Structure of a Database Page
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Notes:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Page Header contains information about the page like: how many records it contains, how much space it has left, what table a page belongs to etc.&lt;/li&gt;
&lt;li&gt;The Record Offset array helps to manage the location of the records on a page. Each &#39;slot&#39; in the array points to the beginning of a record, and helps to locate where the record is physically stored on disk.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;writeback&quot;&gt;Writeback &lt;a class=&quot;direct-link&quot; href=&quot;#writeback&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Recall that saved records are first added to a page located in an in-memory cache. Writeback is the process by which pages located in-memory are written back to disk. When a page has two copies: one in memory and the other on disk, and the more recent page (the one in-memory) has been modified, the page is referred to as being &lt;em&gt;dirty.&lt;/em&gt; The content in-memory differs from what is on disk, and the content on disk needs to be updated since it is the durable data store.&lt;/p&gt;
&lt;p&gt;Now, I do not know all the details about when or how this writeback process is triggered. My understanding is that it happens periodically, but I believe there&#39;s more to that.&lt;/p&gt;
&lt;p&gt;I&#39;m aware that I haven&#39;t answered the question I teased earlier about how databases prevent data from being lost, if data is initially saved in a temporary location. This section might also provoke a similar question from you:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;What if the computer is switched off before writeback happens?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&#39;ve teased you enough, so I&#39;ll address that in the next section.&lt;/p&gt;
&lt;h4 id=&quot;write-ahead-logs&quot;&gt;Write-ahead Logs &lt;a class=&quot;direct-link&quot; href=&quot;#write-ahead-logs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Write-ahead logs are used in a number of database systems ranging from relational databases like PostgreSQL to in-memory datastores like Redis. A log is &lt;em&gt;an append-only&lt;/em&gt; file. It means that when you add a record to a log, it&#39;s placed at the bottom of the log i.e. adding records is sequential. This has an advantage of faster writes than say if the records in the file are sorted, as you only need to keep track of the latest record in the log when you want to insert something. There&#39;s the drawback that it&#39;s less efficient to find a specific record in the log if records are not sorted, as you may end up checking each line in a log of a billion lines! Logs are not typically used this way though, and there&#39;s typically an additional structure involved for this purpose (&lt;em&gt;hint:&lt;/em&gt; It&#39;s called an index).&lt;/p&gt;
&lt;p&gt;I&#39;m not giving you the full story about logs and other contexts in which they are used, but for the purpose of this post, the key thing to note is that they are &lt;em&gt;append-only&lt;/em&gt; files, and writes are sequential.&lt;/p&gt;
&lt;p&gt;Now, recall that I said that database writes (which involve creating, updating, or deleting a record) are first written to a page that exists in memory, making them likely to get lost in the event of a system crash. Well, that&#39;s not quite the full story. In many database systems, writes are also logged to a special file known as a &lt;em&gt;write-ahead log.&lt;/em&gt; This log describes what changes happened on what pages. The log file is persisted to the disk (aka. permanent storage) when the database writes are completed. This way, if your computer crashes before the in-memory pages have been written back to the disk, the lost changes can be restored from the log file. Any change that has been recorded on the log file but is not reflecting in the database can be restored.&lt;/p&gt;
&lt;p&gt;To explain why using a write-ahead log is beneficial over writing the pages directly to the disk, I&#39;ll introduce one final concept known as &lt;em&gt;Transactions.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;transactions&quot;&gt;Transactions &lt;a class=&quot;direct-link&quot; href=&quot;#transactions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Think of a transaction as a group of one or more database operations which act independently of other groups. If &lt;em&gt;at least one&lt;/em&gt; operation in a group fails, the whole group is declared as a failure and then the transaction &lt;em&gt;aborts.&lt;/em&gt; If &lt;em&gt;all&lt;/em&gt; the operations in a group complete successfully, then the transaction &lt;em&gt;commits.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To make this more practical, imagine you have a &#39;Cars&#39; table in your database, you could be performing the following operations in one transaction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inserting 5 new cars into the table&lt;/li&gt;
&lt;li&gt;Updating the prices of 3 cars&lt;/li&gt;
&lt;li&gt;Deleting cars manufactured before 2010&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If at least one of these operations fails, say one of the five insertions violates a uniqueness constraint, the whole transaction fails - including the updates and deletions!&lt;/p&gt;
&lt;p&gt;Without this concept of transactions, if there&#39;s a failure in the middle of a number of database changes, it&#39;s difficult to keep track of which changes have happened, and it could leave you with incorrect data if you make some changes twice.&lt;/p&gt;
&lt;p&gt;Going back to why writing to a write-ahead log is beneficial over writing data pages directly to disk, the advantage that a write-ahead log provides is that it is written to disk only once per transaction, which is &lt;em&gt;when the transaction is committed.&lt;/em&gt; Contrast this with a situation where data pages are written directly to disk immediately; if a transaction involves changing multiple data pages, each data file will need to be written to disk before a write is successful, which will slow down the performance of the database. A Write-ahead log greatly reduces the number of disk writes needed for database operations.&lt;/p&gt;
&lt;h2 id=&quot;conclusion%2Fnext-steps&quot;&gt;Conclusion/Next Steps &lt;a class=&quot;direct-link&quot; href=&quot;#conclusion%2Fnext-steps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve only scratched the surface of this and there are still questions I haven&#39;t answered like: &lt;em&gt;What determines what page a record is stored on? What determines how a page is laid out on disk? How does the database know what pages to search for a record? And more...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ll answer those in the next few posts about database indexing structures: B-Trees and LSM Trees in particular.&lt;/p&gt;
&lt;h2 id=&quot;open-question-i-still-have&quot;&gt;Open Question I Still Have &lt;a class=&quot;direct-link&quot; href=&quot;#open-question-i-still-have&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;What rules govern when writeback is triggered? Does the application developer just set an interval for that to happen?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;direct-link&quot; href=&quot;#further-reading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ll divide this section by different topics if you&#39;re interested in going into more detail.&lt;/p&gt;
&lt;h4 id=&quot;general-overview-of-distributed-systems&quot;&gt;General Overview of Distributed Systems &lt;a class=&quot;direct-link&quot; href=&quot;#general-overview-of-distributed-systems&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://dataintensive.net/&quot;&gt;Designing Data-Intensive Applications&lt;/a&gt; by Martin Kleppmann- I&#39;m about 80% done with the book and I&#39;ve learned so much from it already. I&#39;ll highly recommend it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;records-%26-pages&quot;&gt;Records &amp;amp; Pages &lt;a class=&quot;direct-link&quot; href=&quot;#records-%26-pages&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.red-gate.com/simple-talk/sql/database-administration/sql-server-storage-internals-101/&quot;&gt;SQL Server Storage Internals&lt;/a&gt; by Mark S Rasmussen- I really enjoyed this post as it helped me make sense of how information is stored on records and pages. It was also useful for understanding indexes better.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@adamzerner/spatial-and-temporal-locality-for-dummies-b080f2799dd&quot;&gt;Spatial and Temporal Locality for Dummies&lt;/a&gt; by Adam Zerner.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lwn.net/Articles/682582/&quot;&gt;Toward less-annoying background writeback&lt;/a&gt; by Jonathan Corbet.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/databasss/on-disk-io-part-1-flavours-of-io-8e1ace1de017&quot;&gt;On Disk IO, Part 1: Flavors of IO&lt;/a&gt; by Alex Petrov - I found the bit on the writeback process useful.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blogs.msdn.microsoft.com/askjay/2011/01/07/what-is-a-slot-array/&quot;&gt;What is a slot array?&lt;/a&gt; by Jamesask.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;write-ahead-logs-2&quot;&gt;Write-ahead Logs &lt;a class=&quot;direct-link&quot; href=&quot;#write-ahead-logs-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/9.1/wal-intro.html&quot;&gt;Write-ahead Logging&lt;/a&gt; from the PostgreSQL documentation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redislabs.com/ebook/part-2-core-concepts/chapter-4-keeping-data-safe-and-ensuring-performance/4-1-persistence-options/4-1-2-append-only-file-persistence/&quot;&gt;Append-only File Persistence&lt;/a&gt; from RedisLabs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wildwolf.name/how-to-make-system-logs-append-only/&quot;&gt;How to Make System Logs Append-Only&lt;/a&gt; from WildWildWolf.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Last updated on 19-06-2020.&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
</feed>
