<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="https://decomposition.al/CMPS290S-2018-09/feed.xml" rel="self" type="application/atom+xml" /><link href="https://decomposition.al/CMPS290S-2018-09/" rel="alternate" type="text/html" /><updated>2022-07-16T04:57:59+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/feed.xml</id><title type="html">CMPS290S, Fall 2018</title><subtitle>Languages and Abstractions for Distributed Programming</subtitle><entry><title type="html">An overview of Erlang</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/12/14/an-overview-of-erlang.html" rel="alternate" type="text/html" title="An overview of Erlang" /><published>2018-12-14T00:00:00+00:00</published><updated>2018-12-14T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/12/14/an-overview-of-erlang</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/12/14/an-overview-of-erlang.html">&lt;p&gt;by Natasha Mittal ⋅ edited by Abhishek Singh and Lindsey Kuper&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;In 1981, the Ericsson &lt;a href=&quot;http://www.cs-lab.org/&quot;&gt;Computer Science Laboratory (CSLab)&lt;/a&gt; had been experimenting with ways to program telephony features in Prolog, a declarative language. Telecom applications in general are distributed systems with a large number of concurrent actions taking place. The downside to Prolog was that such declarative languages did not have error-handling facilities and also lacked the means for concurrency control across multiple systems. Thus began a series of collaborations which led to the development of Erlang.&lt;/p&gt;

&lt;p&gt;Erlang is described in &lt;a href=&quot;https://joearms.github.io/&quot;&gt;Joe Armstrong&lt;/a&gt;’s &lt;a href=&quot;http://erlang.org/download/armstrong_thesis_2003.pdf&quot;&gt;2003 PhD thesis, “Making reliable distributed systems in the presence of software errors”&lt;/a&gt;.  In it, he describes how Erlang supports building fault-tolerant systems. Since its inception in 1986, Erlang has grown popular for building reliable telecom applications. It has been used in Web Prioritizer and &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=338532&quot;&gt;Mail Robustifier&lt;/a&gt;, two products developed by &lt;a href=&quot;https://www.walerud.com/blog/bluetail-spinning-out-of-ericsson-and-selling-for-152m-in-18-months&quot;&gt;Bluetail&lt;/a&gt;, a company founded by Joe Armstrong. &lt;a href=&quot;http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.33.5674&amp;amp;rep=rep1&amp;amp;type=pdf&quot;&gt;Ericsson’s AXD301&lt;/a&gt;, a scalable ATM switching system developed using Erlang middleware, was one of the company’s most successful new products from 1998 to the mid-2000s.&lt;/p&gt;

&lt;p&gt;Armstrong describes Erlang as &lt;a href=&quot;http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.34.5602&amp;amp;rep=rep1&amp;amp;type=pdf&quot;&gt;“a concurrent programming language designed for programming large-scale distributed soft real-time control applications”&lt;/a&gt;. It is used in conjunction with a collection of libraries and tools called &lt;a href=&quot;http://erlang.org/doc/system_architecture_intro/sys_arch_intro.html&quot;&gt;OTP (Open Telecom Platform)&lt;/a&gt;, which uses “supervision trees” to provide descriptions of error recovery actions to take for a given error. Erlang has been described as a &lt;a href=&quot;https://www.toptal.com/elixir/process-oriented-programming-elixir-and-otp&quot;&gt;“process-oriented”&lt;/a&gt; language; individual processes do not share memory and communicate via asynchronous message passing, hence maintaining strong isolation between concurrent processes. Since processes are isolated from each other, errors occuring in one process cannot propagate to other processes, so Erlang’s programming model is able to use fail-fast processes.  Erlang also offers support for dynamic code replacement, which aids in code updating and maintenance without stopping the system. This is essential since telecom applications are long-lived, or, more often than not, aren’t shut down ever.&lt;/p&gt;

&lt;h2 id=&quot;concurrency-oriented-programming&quot;&gt;Concurrency-Oriented Programming&lt;/h2&gt;

&lt;p&gt;In his &lt;a href=&quot;http://erlang.org/download/armstrong_thesis_2003.pdf&quot;&gt;thesis&lt;/a&gt;, Armstrong coined the term &lt;em&gt;COPL&lt;/em&gt;, which stands for “concurrency-oriented programming language”, and  argued that Erlang falls into this category of languages. Armstrong wrote that the advantage of using COPLs is the way they can easily model real-world concurrent activities and map them onto concurrent processes in a one-to-one fashion, in contrast to non-COPLs, where one process or thread might control several independent activities.&lt;/p&gt;

&lt;p&gt;As described in section 2.4.2 in Armstrong’s &lt;a href=&quot;http://erlang.org/download/armstrong_thesis_2003.pdf&quot;&gt;thesis&lt;/a&gt;, there are six essential characteristics of a COPL:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It supports lightweight processes, i.e., the computation required to generate and destroy processes is minimal.&lt;/li&gt;
  &lt;li&gt;It supports isolation of processes.&lt;/li&gt;
  &lt;li&gt;Every process is identified uniquely by a Pid.&lt;/li&gt;
  &lt;li&gt;There is no shared state between processes.&lt;/li&gt;
  &lt;li&gt;Message passing does not guarantee delivery, and is pure (no dangling pointers or data references).&lt;/li&gt;
  &lt;li&gt;Processes can detect the occurrence of, and the reason for, failures in other processes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A critical requirement in COPLs is &lt;em&gt;isolation&lt;/em&gt;. There must be strong isolation between the multiple processes running on a single machine. Unless programmed, no faults in any process should affect any of the other processes on the machine. To enable isolation, all processes have “share nothing” semantics and message passing between processes is asynchronous to prevent a sender of the message from getting indefinitely blocked in case software errors occur in the receiver. Data is also immutable within individual processes.&lt;/p&gt;

&lt;p&gt;Another requirement is unforgeability of process names so that it is impossible to guess their names. Each process can only know its own name and the names of the child processes it has created. The act of revealing names to other processes is called the “name distribution problem”, mentioned in section 2.4.4 in Armstrong’s &lt;a href=&quot;http://erlang.org/download/armstrong_thesis_2003.pdf&quot;&gt;thesis&lt;/a&gt;. Such revelations have to be limited to trusted processes only to maintain system security.&lt;/p&gt;

&lt;p&gt;Message passing has “send and pray” semantics in Erlang. Every message is assumed to be received in its entirety or not received at all. Messages can be sent to and received via &lt;em&gt;mailboxes&lt;/em&gt;, which every process has. To aid isolation, a message cannot contain pointers or references to data structures residing on other machines. Additionally, messages are received in the exact order they were sent. A key advantage of message passing is scalability: message-passing systems are relatively easy to replicate over multiple isolated machines, thereby enabling fault tolerance as well. Even though individual components may fail, the probability of all of them failing at the same time is low.&lt;/p&gt;

&lt;h2 id=&quot;fault-tolerance-in-erlang&quot;&gt;Fault tolerance in Erlang&lt;/h2&gt;

&lt;p&gt;According to section 5.1 of &lt;a href=&quot;http://erlang.org/download/armstrong_thesis_2003.pdf&quot;&gt;Armstrong’s thesis&lt;/a&gt;, the main strategy for implementing fault tolerance is to try to perform a task, and if unsuccessful, try to perform a simpler task. In this manner, a hierarchy of tasks is established. This strategy helps avoid unnecessary complexity that might result in the system becoming less reliable.&lt;/p&gt;

&lt;h3 id=&quot;supervision-hierarchies&quot;&gt;Supervision hierarchies&lt;/h3&gt;

&lt;p&gt;Armstrong refers to these hierarchical organizations of tasks as “supervision hierarchies”. In this level-based organization, the highest-level task is to run an application according to specific parameters, and if not possible, to run simpler lower-level tasks. A system failure occurs if the lowest-level task cannot be performed successfully. As we go down to simpler tasks, failure to perform the task becomes more unlikely. It is also interesting to note that on encountering more and more failures at different levels, the emphasis becomes less towards providing complete service and more towards protecting the system. Hence it becomes important to have some mechanism to log all failures and their particular reasons.&lt;/p&gt;

&lt;p&gt;The supervision hierarchy detects and attempts to stop errors from propagating upwards in the system. Every task is associated with a supervisor process which assigns it to a worker for achieving the goals necessary to complete the given task.&lt;/p&gt;

&lt;p&gt;A supervisor needs to have the information about how to start, stop or restart every worker under it. This data is stored in an SSRS (Start Stop and Restart Specification). In a linear hierarchy, the rule is: stop all child processes if a parent asks to stop the supervisor; and to restart a child in case it dies.&lt;/p&gt;

&lt;h2 id=&quot;programming-model&quot;&gt;Programming model&lt;/h2&gt;

&lt;h3 id=&quot;process-creation&quot;&gt;Process creation&lt;/h3&gt;

&lt;p&gt;Spawning is the way new processes are created in Erlang.  THe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spawn()&lt;/code&gt; function takes as arguments a module name and the name of a function within that module.  For example, the call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spawn(moduleA, response, [thank you])&lt;/code&gt; creates a new process which executes a function named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;response&lt;/code&gt; defined within module &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;moduleA&lt;/code&gt; with the argument &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;thank you&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The newly spawned process executes the function, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spawn()&lt;/code&gt; returns the identifier for the spawned process, i.e., the Pid. Pids can then be used for message passing between individual processes.&lt;/p&gt;

&lt;h3 id=&quot;message-passing&quot;&gt;Message Passing&lt;/h3&gt;

&lt;p&gt;All processes in Erlang use the same message-passing interface. Message passing is the only form of data exchange between two processes, and there is no data sharing. A message can be a list, a tuple, integers, and so on.  Message passing takes place causally.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receive&lt;/code&gt; language construct directs a process to wait for a message to come from another process.&lt;/p&gt;

&lt;p&gt;The example below is taken from &lt;a href=&quot;http://erlang.org/doc/getting_started/conc_prog.html#message-passing&quot;&gt;the “Message Passing” subsection of the Erlang documnentation&lt;/a&gt;, which explains Erlang’s constructs for sending and receiving messages.  In this example, two processes are created that send messages to each other a number of times.&lt;/p&gt;

&lt;div class=&quot;language-erlang highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg_passing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pong&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Pong_PID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;			
  &lt;span class=&quot;nv&quot;&gt;Pong_PID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;					&lt;span class=&quot;c&quot;&gt;%% syntax to send message, string ‘finished is being sent to process with Pid=Pong_PID
&lt;/span&gt;  &lt;span class=&quot;nn&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ping finished&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]);&lt;/span&gt;		

&lt;span class=&quot;nf&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Pong_PID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;Pong_PID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()},&lt;/span&gt;		
  &lt;span class=&quot;nv&quot;&gt;Receive&lt;/span&gt;						&lt;span class=&quot;c&quot;&gt;%% receive block does a pattern matching
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;pong&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nn&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Ping received pong&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Pong_PID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;				&lt;span class=&quot;c&quot;&gt;%% recursive call to itself
&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;pong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;receive&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;finished&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nn&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pong finished&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Ping_PID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nn&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pong received ping&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]),&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;Ping_PID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;pong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;Pong_PID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg_passing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]),&lt;/span&gt;		&lt;span class=&quot;c&quot;&gt;%% a process is spawned which executes pong function
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg_passing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Pong_PID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;			&lt;span class=&quot;c&quot;&gt;%% another process is spawned which executes ping message
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;An example run of the code looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Eshell V5.9.3.1  (abort with ^G)
1&amp;gt; c(msg_passing).
{ok,msg_passing}
2&amp;gt; msg_passing:start().
Pong received ping
&amp;lt;0.39.0&amp;gt;
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
ping finished
Pong finished
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;process-linking&quot;&gt;Process Linking&lt;/h3&gt;

&lt;p&gt;Linking of Erlang processes is done via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;link()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spawn_link()&lt;/code&gt; functions. The example discussed below has been taken from &lt;a href=&quot;https://learnyousomeerlang.com/errors-and-processes&quot;&gt;the “Errors and Processes” chapter of &lt;em&gt;Learn You Some Erlang for Great Good!&lt;/em&gt;&lt;/a&gt;, which clearly explains how linking works in Erlang.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chain()&lt;/code&gt; function in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linking&lt;/code&gt; module below spawns &lt;em&gt;N&lt;/em&gt; processes, which are linked to each other.&lt;/p&gt;

&lt;div class=&quot;language-erlang highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;				&lt;span class=&quot;c&quot;&gt;%% a module named linking is created
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;				&lt;span class=&quot;c&quot;&gt;%% export makes chain function public
&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;					&lt;span class=&quot;c&quot;&gt;%% base case when N=0 and process dies after 2000 milliseconds
&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;receive&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;chain dies here&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;					&lt;span class=&quot;c&quot;&gt;%% recursive call to spawn N processes and linking them to each other
&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;Pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;	&lt;span class=&quot;c&quot;&gt;%% spawns a new process running chain(N-1) func
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;receive&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After three recursive calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linking:chain()&lt;/code&gt;, the process running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chain(0)&lt;/code&gt; dies and this error propagates to other processes running with N=1, 2, and 3 consecutively. Eventually, the error propagates to the top-level Erlang shell process, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;erl shell&lt;/code&gt;, which also dies, as seen in the stack trace below.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Execution:
Eshell V6.4.1.7  (abort with ^G)
1&amp;gt; link(spawn(linking, chain, [3])).		%% N = 3, so three processes are spawned
true
2&amp;gt; 2&amp;gt; 2&amp;gt; ** exception error: &quot;chain dies here”
Stack Trace::
[erl shell] == [N=3] == [N=2] == [N=1] == [N=0]
[erl shell] == [N=3] == [N=2] == [N=1] == *dead*
[erl shell] == [N=3] == [N=2] == *dead*
[erl shell] == [N=3] == *dead*
[erl shell] == *dead*
*dead, error message shown*
[erl shell] &amp;lt;-- restarted       %% here erl also dies eventually.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The error propagation that linking enables is used by the aforementioned OTP supervision trees.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The past couple of decades have seen an increase in Erlang’s popularity. Databases like &lt;a href=&quot;http://couchdb.apache.org/&quot;&gt;CouchDB&lt;/a&gt;, &lt;a href=&quot;http://scalaris.zib.de/&quot;&gt;Scalaris&lt;/a&gt;, and &lt;a href=&quot;https://aws.amazon.com/simpledb/&quot;&gt;Amazon SimpleDB&lt;/a&gt; have been implemented using Erlang. Erlang is also an increasingly popular choice for  general-purpose programming, as in the &lt;a href=&quot;http://nitrogenproject.com/&quot;&gt;Nitrogen framework&lt;/a&gt; for web development, or even &lt;a href=&quot;http://www.wings3d.com/&quot;&gt;Wings 3D&lt;/a&gt;, designed for graphics modeling. In conclusion, the following quote from Armstrong in &lt;a href=&quot;https://www.youtube.com/watch?v=u41GEwIq2mE&amp;amp;t=3m59s&quot;&gt;a 2013 interview&lt;/a&gt; seems apt: “If &lt;a href=&quot;https://en.wikipedia.org/wiki/Java_(programming_language)&quot;&gt;Java&lt;/a&gt; is ‘&lt;a href=&quot;https://en.wikipedia.org/wiki/Write_once,_run_anywhere&quot;&gt;write once, run anywhere&lt;/a&gt;’, then Erlang is ‘write once, run forever’.”&lt;/p&gt;</content><author><name>Natasha Mittal</name></author><summary type="html">by Natasha Mittal ⋅ edited by Abhishek Singh and Lindsey Kuper</summary></entry><entry><title type="html">Conflict resolution in collaborative text editing with operational transformation (Part 2 of 2)</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/12/13/conflict-resolution-in-collaborative-text-editing-with-operational-transformation-part-2-of-2.html" rel="alternate" type="text/html" title="Conflict resolution in collaborative text editing with operational transformation (Part 2 of 2)" /><published>2018-12-13T00:00:00+00:00</published><updated>2018-12-13T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/12/13/conflict-resolution-in-collaborative-text-editing-with-operational-transformation-part-2-of-2</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/12/13/conflict-resolution-in-collaborative-text-editing-with-operational-transformation-part-2-of-2.html">&lt;p&gt;by Abhishek Singh ⋅ edited by Devashish Purandare and Lindsey Kuper&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;In this post we continue the discussion on collaborative text editing started in &lt;a href=&quot;https://decomposition.al/CMPS290S-2018-09/2018/11/20/conflict-resolution-in-collaborative-text-editing-with-operational-transformation-part-1-of-2.html&quot;&gt;my previous post&lt;/a&gt;.  The goal of part 1 of the post was to provide an overview of the dOPT operational transformation algorithm from Ellis and Gibbs’ &lt;a href=&quot;http://doi.acm.org/10.1145/67544.66963&quot;&gt;“Concurrency control in groupware systems” paper&lt;/a&gt; and how it addresses the problem of conflict resolution in collaborative text editing. We looked at the problem through a toy example under a set of assumptions that limited the scope of the problem. In this post we remove some of those assumptions and discuss the details of the dOPT algorithm as discussed in the paper.&lt;/p&gt;

&lt;p&gt;Here’s the list of the assumptions I made in my previous post:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;Operation messages from either site are received exactly once.&lt;/li&gt;
    &lt;li&gt;There are exactly two editors in the system: one at Alice’s end and the other at Bob’s end.&lt;/li&gt;
    &lt;li&gt;My implementation does not use clocks to timestamp operations, so the &lt;em&gt;happens before&lt;/em&gt; relationship is established based on message delivery. It is assumed that LOCAL and REMOTE operations happen concurrently.&lt;/li&gt;
    &lt;li&gt;Operations are processed in the order in which they are seen and executed at each particular site. In our implementation, the executed operations are stored in a list &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor.Ops&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Unlike the implementation in the paper, we do not assign priorities to an operation. Every operation has equal priority.&lt;/li&gt;
    &lt;li&gt;An operation is sent to others immediately after it was executed at one particular site. There is no out-of-order delivery of messages.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;For reference, here’s the example collaborative editing session between Alice and Bob that we looked at &lt;a href=&quot;http://composition.al/CMPS290S-2018-09/2018/11/20/conflict-resolution-in-collaborative-text-editing-with-operational-transformation-part-1-of-2.html&quot;&gt;last time&lt;/a&gt;:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/test_operations.png&quot; height=&quot;600&quot; width=&quot;450&quot; /&gt;
  &lt;figcaption&gt;Figure 1. Operations received by Alice and Bob are transformed before being applied to local data.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Let us consider the consequences of removing each of the above assumptions. Primarily, there is a problem of ascertaining &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=359563&quot;&gt;causality&lt;/a&gt; of messages in the design. Consider the second and third operations exchanged between Alice and Bob in Figure 1. There is no way for Alice to know if the operation &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;x&quot;, 2)&lt;/code&gt; sent by Bob happened before or after Bob received the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT(&quot;y&quot;, 0)&lt;/code&gt; operation from Alice (even though we can see the figure and know that the operations were concurrent).  Figure 2 shows Alice’s view of the world: Alice cannot know if there is a causal relationship between the operations, because we did not address causality in part 1. The ordering of the messages was enforced only by the order in which the messages were delivered to each user.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/o_site.png&quot; height=&quot;600&quot; width=&quot;450&quot; /&gt;
  &lt;figcaption&gt;Figure 2. Alice does not know how Bob executed the operations at his end and therefore cannot know how received operations should be applied at her end.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The possibility of receiving out-of-order messages is another issue that the design must address. The algorithm in part 1 executes messages on each replica in the order in which they are received. This aggravates the problem of data inconsistency among the replicas if messages were delayed in transit and were not received in the same order in which they were generated. Suppose the operation  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;x&quot;, 2)&lt;/code&gt; from Bob in Figure 1 was delayed and received by Alice after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE (3)&lt;/code&gt; operation from Bob.  This would lead to the operations being executed in different orders on Alice’s and Bob’s ends leading to data inconsistency between the replicas.  Since in-order message delivery is subsumbed by causal delivery, we can also solve this problem by addressing causality.&lt;/p&gt;

&lt;p&gt;Finally, another assumption we made has to do with the number of users participating in the system.  It is relatively easy to understand how operation messages are exchanged when only two users exist in the system. With more users, the number of messages needed to share changes with other users grows.  If there are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; users in the system, every state change at one user must be communicated with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n-1&lt;/code&gt; users. With each user making changes to their document replica, the version of dOPT developed for the two-user collaborative text editor developed in part 1 cannot be used, as there is no way of knowing the exact state of a replica when an operation was executed.&lt;/p&gt;

&lt;h2 id=&quot;precedence-convergence-and-quiescence&quot;&gt;Precedence, convergence, and quiescence&lt;/h2&gt;

&lt;p&gt;The crux of the data consistency problem discussed until now is that a site does not know the historical trace of an operation when it receives the operation request.  The approach proposed for dOPT in &lt;a href=&quot;http://doi.acm.org/10.1145/67544.66963&quot;&gt;Ellis and Gibbs’s paper&lt;/a&gt; involves the use of a &lt;a href=&quot;https://en.wikipedia.org/wiki/Vector_clock&quot;&gt;vector clock&lt;/a&gt; to order the operations generated on different sites (each site or user is associated with a single document replica). The paper defines a &lt;em&gt;Convergence Property&lt;/em&gt; and a &lt;em&gt;Precedence Property&lt;/em&gt; and a notion of &lt;em&gt;quiescence&lt;/em&gt; to describe the correctness of the algorithm. The authors define the properties as follows:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The &lt;em&gt;Precedence Property&lt;/em&gt; states that if one operation, &lt;em&gt;o&lt;/em&gt;, precedes another, &lt;em&gt;p&lt;/em&gt;, then at each site the execution of &lt;em&gt;o&lt;/em&gt; happens before the execution of &lt;em&gt;p&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;A groupware session is &lt;em&gt;quiescent&lt;/em&gt; iff all generated operations have been executed at all sites, that is, there are no requests in transit or waiting to be executed by a site process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;The &lt;em&gt;Convergence Property&lt;/em&gt; states that site objects are identical at all sites at quiescence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Precedence Property makes use of the &lt;em&gt;precedes&lt;/em&gt; relation, which is reminiscent of Lamport’s &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=359563&quot;&gt;“happens-before” relation&lt;/a&gt;.  If an operation &lt;em&gt;o&lt;/em&gt; &lt;em&gt;precedes&lt;/em&gt; &lt;em&gt;p&lt;/em&gt;, then the Precedence Property ensures that &lt;em&gt;o&lt;/em&gt; is executed before &lt;em&gt;p&lt;/em&gt; everywhere.&lt;/p&gt;

&lt;p&gt;The Convergence Property combined with the notion of quiescence is nearly identical to the Strong Convergence property defined by &lt;a href=&quot;https://hal.inria.fr/inria-00609399v1/document&quot;&gt;Shapiro et al.&lt;/a&gt;. In the Ellis and Gibbs paper, however, quiescence is enforced periodically. The detection of quiescence is done via a distributed consensus algorithm, which is not discussed in the paper.&lt;/p&gt;

&lt;h2 id=&quot;the-distributed-operational-transformation-dopt-algorithm&quot;&gt;The Distributed Operational Transformation (dOPT) algorithm&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;http://doi.acm.org/10.1145/67544.66963&quot;&gt;dOPT algorithm&lt;/a&gt; in essence is an attempt to order operations for execution by maintaining their causal links to other operations and, based on that order, decide whether transformation of the operation indices are really needed when executed by individual sites. The dOPT algorithm described in the paper assumes a constant number of sites. For every change that a site makes to its replica, a request is generated and sent to other sites. To achieve the two properties the design uses a &lt;em&gt;request queue&lt;/em&gt; &lt;em&gt;Q&lt;sub&gt;i&lt;/sub&gt;&lt;/em&gt; and a &lt;em&gt;request log&lt;/em&gt; &lt;em&gt;L&lt;sub&gt;i&lt;/sub&gt;&lt;/em&gt;, where the subscript &lt;em&gt;i&lt;/em&gt; is the site identifier. Throughout the rest of the discussion, site &lt;em&gt;i&lt;/em&gt; will be the location where the request is being processed and site &lt;em&gt;j&lt;/em&gt; will be the site which sent the request.&lt;/p&gt;

&lt;p&gt;The request queue contains operation requests either sent by remote sites (&lt;em&gt;j&lt;/em&gt;) or from the user of the current site (&lt;em&gt;i&lt;/em&gt;). These are requests that are waiting to be processed, and the queue acts as a buffer where all incoming requests are stored for further processing. The request log, on the other hand, is a log of requests that have been executed by the site. The log is a list of requests ordered by the order in which the requests were executed.&lt;/p&gt;

&lt;p&gt;Each request has the form &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;i, s, o, p&amp;gt;&lt;/code&gt;, where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; represents the site identifier and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; represents the &lt;em&gt;state vector&lt;/em&gt; of the site &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;. The state vector, as discussed by Ellis and Gibbs, is essentially a vector clock that specify when an operation was executed on site &lt;em&gt;j&lt;/em&gt; and its relation to the operations in the request queue in site &lt;em&gt;i&lt;/em&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;o&lt;/code&gt; represents the operation to be performed (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insert&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete&lt;/code&gt;). Finally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; specifies the priority of the operation.  As discussed in my previous post, operations must commute with each other.&lt;/p&gt;

&lt;p&gt;The dOPT algorithm builds a transformation matrix for the operations that are supported: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insert&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete&lt;/code&gt;. A 2x2 transformation matrix is created which reflects the cases seen during operation execution: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insert-insert&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insert-delete&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete-insert&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete-delete&lt;/code&gt;. The priority of an operation is used to compute if an operation should be transformed and what the values of the recomputed indexes should be.&lt;/p&gt;

&lt;p&gt;The algorithm uses state vectors to order events causally. Given two state vectors &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; and &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt;, Ellis and Gibbs define the following relations:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; = &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt; if each component of &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; is equal to the corresponding component of &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; &amp;lt; &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt; if each component of &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; is less than or equal to the corresponding component of &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt; and at least one component of  &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; is less than the corresponding component in  &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; &amp;gt; &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt; if at least one component of  &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; is greater than the corresponding component in  &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt;.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, if &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ 1 2 3 3 ]&lt;/code&gt; and &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ 1 2 3 4 ]&lt;/code&gt;, we have &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; &amp;lt; &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt;.  However, if &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ 4 3 2 1 ]&lt;/code&gt; and &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ 1 2 3 4 ]&lt;/code&gt;, we have &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;i&lt;/em&gt;&lt;/sub&gt; &amp;gt; &lt;em&gt;s&lt;/em&gt;&lt;sub&gt;&lt;em&gt;j&lt;/em&gt;&lt;/sub&gt;.&lt;/p&gt;

&lt;p&gt;During initialization the following operations are done:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set Q&lt;sub&gt;i&lt;/sub&gt; to empty&lt;/li&gt;
  &lt;li&gt;Set L&lt;sub&gt;i&lt;/sub&gt; to empty&lt;/li&gt;
  &lt;li&gt;Set s&lt;sub&gt;i&lt;/sub&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[0 0 ... 0]&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The algorithm defines three possible execution states:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Operation request generation,&lt;/li&gt;
  &lt;li&gt;Operation request reception, and&lt;/li&gt;
  &lt;li&gt;Operation execution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;During operation request generation the site &lt;em&gt;i&lt;/em&gt; generates an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insert&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete&lt;/code&gt; operation. The operation is not executed immediately; the local data is not modified during operation request generation. Once the request is generated, it is appended to the site’s request queue Q&lt;sub&gt;i&lt;/sub&gt; and broadcast to all other sites.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Generate operation &amp;lt;i ,s&lt;sub&gt;i&lt;/sub&gt; , o, p&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Q&lt;sub&gt;i&lt;/sub&gt;  :=  Q&lt;sub&gt;i&lt;/sub&gt;  +  &amp;lt;i ,s&lt;sub&gt;i&lt;/sub&gt; , o, p&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A request generated on a site &lt;em&gt;j&lt;/em&gt; is eventually received by site &lt;em&gt;i&lt;/em&gt; which then moves to the “operation request reception” state. In this state, the received operations are appended to the site’s request queue.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Receive operation request from remote site j: &amp;lt;j ,s&lt;sub&gt;j&lt;/sub&gt; , o&lt;sub&gt;j&lt;/sub&gt;, p&lt;sub&gt;j&lt;/sub&gt; &amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Q&lt;sub&gt;i&lt;/sub&gt;  :=  Q&lt;sub&gt;i&lt;/sub&gt;  +  &amp;lt;j ,s&lt;sub&gt;j&lt;/sub&gt; , o&lt;sub&gt;j&lt;/sub&gt;, p&lt;sub&gt;j&lt;/sub&gt; &amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;During operation execution, requests from the request queue are processed. The order of execution of requests in the request queue is determined by the total order of events in the request queue as determined by the comparison of the state vectors. Briefly, in this step the operation from the request queue is chosen based on the executed operations in the request log. We locate the operation older than the current state vector at site &lt;em&gt;i&lt;/em&gt;. Transformation is performed based on the operation logs in the request log. Comparison of the state vectors follows the conditions stated previously:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;If s&lt;sub&gt;j&lt;/sub&gt; &amp;gt; s&lt;sub&gt;j&lt;/sub&gt;, this means that the site &lt;em&gt;j&lt;/em&gt; has executed operations which site &lt;em&gt;i&lt;/em&gt; has not seen yet. So this operation will have to stay in the queue till all operations between &lt;em&gt;i&lt;/em&gt; and &lt;em&gt;j&lt;/em&gt; have been executed.&lt;/li&gt;
  &lt;li&gt;If s&lt;sub&gt;j&lt;/sub&gt; = s&lt;sub&gt;j&lt;/sub&gt;, the two state vectors are identical and operation o&lt;sub&gt;j&lt;/sub&gt; can be executed without transformation.&lt;/li&gt;
  &lt;li&gt;If s&lt;sub&gt;j&lt;/sub&gt; &amp;lt; s&lt;sub&gt;j&lt;/sub&gt;, site &lt;em&gt;i&lt;/em&gt; has executed operations not seen by site &lt;em&gt;j&lt;/em&gt;. The operation can be applied immediately, but requires operations to be transformed because other changes not visible to site &lt;em&gt;j&lt;/em&gt; have already been executed by site &lt;em&gt;i&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The principles behind transformations were discussed in my previous post. The main idea is that the transformations must commute. This allows the operations to be executed in any order. The idea of commutative operations is vital to any operational transformation technique. In fact, the idea of commutative (and associative) operations comes up very frequently in discussions of synchronization-free convergence algorithms in distributed systems. A fairly recent example is in &lt;a href=&quot;http://doi.acm.org/10.1145/2933057.2933090&quot;&gt;a paper by Attiya et al.&lt;/a&gt; when discussing convergence of replica state in their formalization of the &lt;a href=&quot;https://www.sciencedirect.com/science/article/pii/S0743731510002716&quot;&gt;RGA protocol&lt;/a&gt; for collaborative text editing. Another example is in &lt;a href=&quot;https://hal.inria.fr/inria-00609399v1/document&quot;&gt;conflict-free replicated data types&lt;/a&gt;, where operation commutativity is key to state convergence.&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;The idea of operational transformation originally proposed by Ellis and Gibbs has morphed into a &lt;a href=&quot;https://en.wikipedia.org/wiki/Operational_transformation&quot;&gt;compendium of technologies&lt;/a&gt; used to build collaborative systems.  A prominent example is the &lt;a href=&quot;https://dl.acm.org/citation.cfm?doid=215585.215706&quot;&gt;Jupiter collaboration system&lt;/a&gt;.  Instead of a peer-to-peer system as we have been discussing until now, Jupiter used a centralised architecture where a server maintains a single copy and all operation requests are handled via the server. This system became the basis of the Google Wave and Google Docs projects, as mentioned in my previous post.&lt;/p&gt;

&lt;p&gt;Over the course of these two blog posts, I have aimed to understand the key ideas behind the early specification of operational transformation and convey them with some clarity.  Although collaborative text editing has been a topic of research since at least the 1980s, writing these posts allowed me to study the problem in some detail and has kindled my interest in building systems where multiple agents can work together towards a common goal. Thinking about building useful abstractions for collaborative computing agents is something that will keep me busy for some time.&lt;/p&gt;</content><author><name>Abhishek Singh</name></author><summary type="html">by Abhishek Singh ⋅ edited by Devashish Purandare and Lindsey Kuper</summary></entry><entry><title type="html">State is Progressive, or: hey, what happens if we make literally _everything_ append-only?</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/12/13/state-is-progressive-or-hey-what-happens-if-we-make-literally-everything-append-only.html" rel="alternate" type="text/html" title="State is Progressive, or: hey, what happens if we make literally _everything_ append-only?" /><published>2018-12-13T00:00:00+00:00</published><updated>2018-12-13T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/12/13/state-is-progressive-or-hey-what-happens-if-we-make-literally-everything-append-only</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/12/13/state-is-progressive-or-hey-what-happens-if-we-make-literally-everything-append-only.html">&lt;p&gt;by Sohum Banerjea ⋅ edited by Natasha Mittal and Lindsey Kuper&lt;/p&gt;

&lt;p&gt;State is weird.&lt;/p&gt;

&lt;p&gt;State is weird because we want the freedom to mutate and mangle it however we like. That’s the default conceptualisation we have of state, since we all learnt to program – state is a bag of bits, or structs, or objects, that we can mangle however we like.&lt;/p&gt;

&lt;p&gt;But we keep finding situations in which this view of state loses us verifiability, or reproducibility, or, god forbid, &lt;em&gt;performance&lt;/em&gt;. Whether it’s multicore processors or distributed systems, storage systems or language models, we keep finding ourselves in cases where the bag-of-bits model lets us do &lt;em&gt;less than we could&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the effort to do more, there’s a common thread: designs that don’t mutate state, only &lt;a href=&quot;https://sites.google.com/site/progressive294/&quot;&gt;add to it&lt;/a&gt;; that conceptualise knowledge as something that only monotonically grows, and never decreases.&lt;/p&gt;

&lt;p&gt;In this post, we’ll look at a few examples of these “progressive systems”. For our purposes, it’s easiest to discuss them in terms of the axis along which they restrict state to be monotonic. Treating the data itself as always monotonic, independent of time, gets you &lt;em&gt;lattice models&lt;/em&gt;, whereas treating time as the property that is always monotonic gives you &lt;em&gt;stream models&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;lattice-models&quot;&gt;Lattice Models&lt;/h2&gt;

&lt;p&gt;Placing this monotonicity guarantee on your data means ensuring that “bigger” data is always closer to the final state, for some definition of “bigger”. Mathematically, enforcing this structure on data cashes out to structuring it as a &lt;em&gt;join-semilattice&lt;/em&gt;, which we obviously have to shorten to “lattice”.  A join-semilattice is a set with an associated partial order, and where every pair of
elements have a least upper bound (“join”) that respects that partial order. The
archetypical example here is &lt;a href=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Hasse_diagram_of_powerset_of_3.svg/429px-Hasse_diagram_of_powerset_of_3.svg.png&quot;&gt;sets under the subset partial order&lt;/a&gt;.  Every pair of sets has a least upper bound — the union of those two sets — that respects
subset inclusion as a partial order. Thus, sets, under the partial order of subset
inclusion and the join operation of union, form a join-semilattice.&lt;/p&gt;

&lt;p&gt;Okay. So we can think of the states our data can take on as members of a join-semilattice. What does this &lt;em&gt;actually&lt;/em&gt; buy us?&lt;/p&gt;

&lt;p&gt;It means that if we have conflicting copies of our data (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{2,3}&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{3,5}&lt;/code&gt;, let’s say) at any point,
we know what to do to resolve the conflict: take the least upper bound (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{2,3,5}&lt;/code&gt;). Since
we know our data can never get “smaller”, this is a correct operation — the state of our
data is at &lt;em&gt;least&lt;/em&gt; what it is at each replica.  This is the fundamental idea that links the following three distinct approaches to state.&lt;/p&gt;

&lt;h3 id=&quot;crdts&quot;&gt;CRDTs&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://hal.inria.fr/inria-00609399/document&quot;&gt;CRDTs&lt;/a&gt;, or &lt;em&gt;conflict-free replicated data types&lt;/em&gt;, try to present the concept of join-semilattices to the programmer
as transparently as possible. They’re data structures designed for replication, and a lot of their complexity comes from providing expressive, efficient data structures while maintaining the property that the data is always monotonically growing. Research in the field tries hard
to make the resulting data structures efficiently support the operations we want them to.&lt;/p&gt;

&lt;p&gt;There ends up being a lot of subtlety in how more complex operations can be supported when we try to represent a data structure as a lattice — even the canonical CRDT, the set, gets complicated once we dare to do anything as gauche as &lt;em&gt;deleting&lt;/em&gt; elements. (In short:
deleting elements from a set is a non-growing operation, and so if we’re not
careful, when we delete an element from one replica, the rest of our replicas will just assume
it’s an addition we haven’t seen yet, &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=1294281&quot;&gt;and add it right back again&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;A major issue with using CRDTs in practice is how they need to be &lt;em&gt;garbage collected&lt;/em&gt; — as you
may imagine, having your state grow without bound is not a desirable property
for a data structure. This is an instance of non-monotonicity in our programs: as much as we’d like
our programs to be monotonic, we end up needing these synchronisation points for &lt;a href=&quot;/CMPS290S-2018-09/2018/11/12/implementing-a-garbage-collected-graph-crdt-part-1-of-2.html&quot;&gt;real-world&lt;/a&gt; &lt;a href=&quot;/CMPS290S-2018-09/2018/12/08/implementing-a-garbage-collected-graph-crdt-part-2-of-2.html&quot;&gt;practical usage&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;lvars&quot;&gt;LVars&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://users.soe.ucsc.edu/~lkuper/papers/lvars-fhpc13.pdf&quot;&gt;LVars&lt;/a&gt; are another data structure that associates states with lattice elements. Any write to an LVar updates it to the least upper bound of the value being written and the state the LVar already had. Where they differ from CRDTs is in the additional restrictions they impose on how this data can be interacted with: LVars are not directly readable. The read operation on an LVar is a “threshold read” — an operation that blocks until one of a set of specified points in the lattice has been reached or surpassed, and thus can never return any intermediate state.
To preserve determinism, this threshold read doesn’t even necessarily return the current state of the LVar when it unblocks.  Rather, it deterministically returns a state that the current state is guaranteed to be &lt;em&gt;at or above&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In exchange, LVars recover &lt;em&gt;determinism&lt;/em&gt;. That is, all interleavings of a program that coordinates data with LVars will produce the same result. This design decision is a more natural set of tradeoffs to want in their domain: LVars were designed for shared memory, whereas CRDTs were designed for replicated data.&lt;/p&gt;

&lt;p&gt;There &lt;em&gt;is&lt;/em&gt; a way to read the exact state of the LVar, but only after it’s been
&lt;a href=&quot;https://users.soe.ucsc.edu/~lkuper/papers/lvish-popl14.pdf&quot;&gt;“frozen”&lt;/a&gt;. This creates the opportunity for races between attempts to write to the LVar and
attempts to freeze it, and thus this weakens the determinism guarantee to
“quasi-determinism” — all interleavings of the program are guaranteed to produce the same
result as long as &lt;em&gt;that interleaving doesn’t error&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;LVars don’t aim to adddress non-monotonic use cases; instead, the philosophy is that they can just give up control over the data once a non-monotonic operation is necessary. The assumption is that a non-monotonic operation will &lt;em&gt;not&lt;/em&gt; be necessary until after a (often lengthy) phase of monotonic computation, and so LVars allow parallelisation of the monotonic phase.&lt;/p&gt;

&lt;h3 id=&quot;bloom-and-blooml&quot;&gt;Bloom and Bloom^L&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://db.cs.berkeley.edu/papers/cidr11-bloom.pdf&quot;&gt;Bloom&lt;/a&gt; and its successor &lt;a href=&quot;http://www.neilconway.org/docs/socc2012_bloom_lattices.pdf&quot;&gt;Bloom^L&lt;/a&gt; build monotonicity right into a programming language – or, to be more precise, they &lt;em&gt;remove&lt;/em&gt; operations until the only operations left are monotonic.&lt;/p&gt;

&lt;p&gt;The idea is that
non-monotonicity is what leads to the need for
synchronisation in distributed programs. If your program is not monotone, then it will need to have all of its
inputs to produce the correct result. Some approaches to distributed programming weaken the
“correctness” of the result in response, but if you could program with purely monotonic
operations &lt;em&gt;most&lt;/em&gt; of the time, you could narrow down the places you need to
synchronise to those places where you’re using non-monotonic operations.
This is the &lt;a href=&quot;http://db.cs.berkeley.edu/papers/cidr11-bloom.pdf&quot;&gt;CALM principle&lt;/a&gt; — “Consistency As Logical Monotonicity”.&lt;/p&gt;

&lt;p&gt;Bloom^L has you write monotone functions on data structures, so it can
ensure that the entire program’s use of state is monotone and thus doesn’t need
synchronisation to ensure eventual consistency.
One particularly useful class of monotone functions is &lt;em&gt;homomorphic&lt;/em&gt; functions — functions that map from one lattice to another, ensuring the map
preserves the structure of the lattice while doing so.
For instance, under the standard lattice for sets, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set.is_empty()&lt;/code&gt; operation is
homomorphic if and only if the resulting boolean is false-biasing. That is, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false &amp;gt; true&lt;/code&gt;
in its lattice, and thus &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifnot&lt;/code&gt; is the only monotonic operation available on it, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The constraints of Bloom^L allow it to guarantee monotonicity without static
checking in most cases.&lt;/p&gt;

&lt;p&gt;(Psst. Don’t tell anyone, but Bloom^L &lt;em&gt;also&lt;/em&gt; belongs in the next
section. See you there!)&lt;/p&gt;

&lt;h2 id=&quot;data-as-streams&quot;&gt;Data as Streams&lt;/h2&gt;

&lt;p&gt;Another way to have grow-only state is to just totally order it in time. Despite
&lt;a href=&quot;https://decomposition.al/CMPS290S-2018-09/2018/11/17/time-is-partial-or-why-do-distributed-consistency-models-and-weak-memory-models-look-so-similar-anyway.html&quot;&gt;all the problems&lt;/a&gt; in trying to totally order time, there are &lt;em&gt;some&lt;/em&gt; cases in which
we can structure the entirety of how we process state such that we can exploit this total
ordering.&lt;/p&gt;

&lt;p&gt;In practice, these designs look like stream processing languages. Streams are inherently &lt;em&gt;ordered&lt;/em&gt;
data, which means we always know what’s newer and what’s older, and stream processing is
inherently an operation in time, such that any stream processor can only ever emit at time
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; data that is dependent on input data up to time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt;. This is, of course, monotonicity
again, just pushed a layer outward, into metadata — timestamps — associated with our data cells.&lt;/p&gt;

&lt;p&gt;In this framework, one key requirement keeps showing up: for the system or a stream to
somehow guarantee that no further messages with timestamps ≤ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; will appear. Most
commonly referred to as “punctuation”, this shows us the key point at which we
fundamentally need to guarantee the (local) monotonicity of time for our operations on
these streams to continue being monotonic.&lt;/p&gt;

&lt;h3 id=&quot;cql&quot;&gt;CQL&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://link.springer.com/chapter/10.1007/978-3-540-24607-7_1#page-1&quot;&gt;CQL&lt;/a&gt;, the “Continuous Query Language”, takes this framing and applies the well-known declarative language of
relational logic (i.e., SQL) to streams.&lt;/p&gt;

&lt;p&gt;The core of CQL is in its stream-to-relation and relation-to-stream operators, in order to
let the standard SQL operators do work on the intermediate relations. It deliberately does
not provide stream-to-stream operators, requiring all operations to be done on the
relational layer. To do this, they augment their concept of relations to natively include time; all relations in CQL are &lt;em&gt;time-varying&lt;/em&gt; (parameterised by timestamp).&lt;/p&gt;

&lt;p&gt;Both of these classes of operators are simple to specify:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;stream-to-relation operators capture a given time window over the stream and present the
  data within that window as a relation with the same schema&lt;/li&gt;
  &lt;li&gt;relation-to-stream operators emit elements on the stream at time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; when
    &lt;ul&gt;
      &lt;li&gt;an element is added to the relation at time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Istream&lt;/code&gt;)&lt;/li&gt;
      &lt;li&gt;an element is deleted from the relation at time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dstream&lt;/code&gt;)&lt;/li&gt;
      &lt;li&gt;or, an element just is in the relation at time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rstream&lt;/code&gt;)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The language attempts to add a default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Istream&lt;/code&gt; in cases where a conservative static
check of the underlying relation can guarantee that the relational operation being
described is monotonic.&lt;/p&gt;

&lt;p&gt;Punctuation isn’t considered a language-level concern for CQL — the language assumes you
have access to all data up to timestamp &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; when computing something at timestamp &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt;, and
it’s left to the runtime to validate this requirement. However, they do describe that
their runtime does need to have ways of generating these “heartbeats”, and various
policies for doing so.&lt;/p&gt;

&lt;h3 id=&quot;blooml&quot;&gt;Bloom^L&lt;/h3&gt;

&lt;p&gt;Bloom^L also uses streams as a core data type. More precisely, it parameterises its data
cells by time, thus providing a similar set of properties to stream based languages. It
does this in order to model the nondeterminism of asynchronous communication (a message
from one node to another is modelled as data inserted into a shared object at a
nondeterministic future timestamp).&lt;/p&gt;

&lt;p&gt;Bloom^L is capable of reasoning about non-monotone parts of your program, and this
integration of time into its modelling is a major reason why it can do so.&lt;/p&gt;

&lt;p&gt;When you allow Bloom^L to reason about punctuation, you get &lt;a href=&quot;http://www.neilconway.org/docs/vldb2014_edelweiss.pdf&quot;&gt;Edelweiss&lt;/a&gt;, a generic
solution to the garbage collection problem CRDTs have to face. Edelweiss has a far more
general concept of punctuation than either other system — it’s a guarantee that no
more messages will arrive &lt;em&gt;matching any arbitrary predicate&lt;/em&gt;. Edelweiss can then track
these predicates across channels compositionally (if the set of peers at a particular
epoch is fixed, and all of those peers have responded to a given message, it’s &lt;em&gt;guaranteed&lt;/em&gt;
that one will not receive any further responses to that message).&lt;/p&gt;

&lt;p&gt;Pervasive monotonicity, combined with the strong guarantees Edelweiss enables via its
punctuation system, allow the user to reason directly about what state they no longer need
to keep around and can thus garbage-collect.&lt;/p&gt;

&lt;h3 id=&quot;timely-dataflow&quot;&gt;Timely Dataflow&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://www.frankmcsherry.org/timely-dataflow/&quot;&gt;Timely dataflow&lt;/a&gt; is an approach to stream processing that has handling of iterative loops as an explicit design goal.
Loops are constrained specifically to ensure they manage the
timestamps of messages flowing through the system in a precise, predictable, monotonic
way.&lt;/p&gt;

&lt;p&gt;Since the system has more control over the timestamps than a general stream processing
system, it can reason about how messages &lt;em&gt;could result in&lt;/em&gt; later messages at given
timestamps — essentially providing a flexible, productive method for &lt;em&gt;generating&lt;/em&gt;
punctuation across the whole system in the presence of loops.
This allows for complex dataflows across the resulting stream processing nodes, including
messages sent to nodes at specific future timestamps, and nodes that wait for punctuation
before computing some non-incremental property of the data.&lt;/p&gt;

&lt;p&gt;Again, we find monotonicity, as modified to address the problem, to provide strong
properties we can exploit — even when computing non-monotonic properties.&lt;/p&gt;

&lt;h2 id=&quot;what-does-all-this-mean&quot;&gt;What Does All This Mean?&lt;/h2&gt;

&lt;p&gt;State is progressive.&lt;/p&gt;

&lt;p&gt;…Okay, that may be overselling it. While all of these models have important benefits when applied to the use cases they were designed for, none of them attempt to be a &lt;em&gt;general-purpose&lt;/em&gt; model of state. We’re not yet trying to overthrow the von Neumann machine as the fundamental architecture on which computation is built.&lt;/p&gt;

&lt;p&gt;That said, these designs do point to a &lt;a href=&quot;https://sites.google.com/site/progressive294/&quot;&gt;“turning point”&lt;/a&gt; in how we think about state. If you can stomach the limitations of designing your state to be progressive, you get strong, relevant properties in return.&lt;/p&gt;

&lt;p&gt;State may be progressive. Maybe it’s time to seriously consider those designs.&lt;/p&gt;</content><author><name>Sohum Banerjea</name></author><summary type="html">by Sohum Banerjea ⋅ edited by Natasha Mittal and Lindsey Kuper</summary></entry><entry><title type="html">Diversifying Consistency in Ceph</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/12/12/diversifying-consistency-in-ceph.html" rel="alternate" type="text/html" title="Diversifying Consistency in Ceph" /><published>2018-12-12T00:00:00+00:00</published><updated>2018-12-12T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/12/12/diversifying-consistency-in-ceph</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/12/12/diversifying-consistency-in-ceph.html">&lt;p&gt;by Aldrin Montana · edited by Austen Barker and Lindsey Kuper&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://decomposition.al/CMPS290S-2018-09/2018/11/21/mixing-consistency-in-a-programmable-storage-system.html&quot;&gt;In my previous post&lt;/a&gt;, we provided background and motivation for
mixing consistency in a programmable storage system. In this post, we describe approaches to
supporting a range of consistency options in the &lt;a href=&quot;https://www.ssrc.ucsc.edu/Papers/weil-osdi06.pdf&quot;&gt;Ceph&lt;/a&gt; storage system to enable
mixed-consistency, programmable storage on top of Ceph.&lt;/p&gt;

&lt;p&gt;We will compare approaches to implementing a variety of consistency models by first looking at the
implementation of &lt;a href=&quot;http://www.cs.nthu.edu.tw/~ychung/conference/ICPADS-2016.pdf&quot;&gt;PROAR&lt;/a&gt;, a recently proposed replication strategy for Ceph that
trades strong consistency for lower write latency. We will also consider other approaches to
implementing eventual consistency in Ceph.  Finally, we’ll consider  implementing [“bolt-on” causal
consistency][bolt-on-paper] on top of Ceph, which requires that the underlying datastore support
eventual consistency.&lt;/p&gt;

&lt;h1 id=&quot;replication-and-data-placement-in-ceph&quot;&gt;Replication and Data Placement in Ceph&lt;/h1&gt;

&lt;p&gt;To understand how Ceph might support weaker consistency models such as eventual consistency or causal
consistency, we describe how the storage subsystem in Ceph, &lt;a href=&quot;https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf&quot;&gt;RADOS&lt;/a&gt;,
manages persistent storage and replication. We also mention the role of Ceph’s &lt;a href=&quot;https://ceph.com/wp-content/uploads/2016/08/weil-crush-sc06.pdf&quot;&gt;CRUSH&lt;/a&gt;
algorithm for determining data placement.&lt;/p&gt;

&lt;p&gt;Storage in RADOS consists of four layers of abstraction. At the highest level are files or
abstractions for applications to write and read data. The second layer maps files to data objects.
Then, the third layer distributes (stripes) data objects across the storage cluster into &lt;em&gt;placement
groups&lt;/em&gt; (PGs) using a hash function. Each PG has a replication factor, which represents how many
replicas a data object assigned to the PG will have. A PG is a logical entity, not a server, that
can be likened to a horizontal shard in data management systems. At the lowest level of
abstraction, &lt;em&gt;object storage devices&lt;/em&gt; (OSDs) handle data persistence and replication.&lt;/p&gt;

&lt;p&gt;The diagram
below depicts an overview of the data placement in Ceph. OSDs are assigned to, or organized into,
PGs. OSDs of the same PG cooperate to replicate data objects and exchange state, and do not
communicate with OSDs associated with other PGs. The diagram below is based on descriptions in the
RADOS and Ceph papers; for a slightly higher level of abstraction, see Figure 3 in the &lt;a href=&quot;https://www.ssrc.ucsc.edu/Papers/weil-osdi06.pdf&quot;&gt;Ceph paper&lt;/a&gt;. The
mapping of files to objects to PGs to OSDs is succinctly described in
Section 5 of the Ceph paper. The organization of OSDs into a PG is calculated using
the CRUSH (“controlled replication under scalable hashing”) algorithm, detailed in the &lt;a href=&quot;https://ceph.com/wp-content/uploads/2016/08/weil-crush-sc06.pdf&quot;&gt;CRUSH paper&lt;/a&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/diversifying-consistency-in-ceph/ceph_pg_overview.png&quot; /&gt;
  &lt;figcaption&gt;Overview of object assignment to placement groups and object storage devices in Ceph&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;To facilitate communication and failure recovery, monitor services maintain an up-to-date
representation of cluster state called the &lt;em&gt;cluster map&lt;/em&gt;, which includes all PGs, OSDs, and other
monitors. During the first interaction clients and other services have with the storage cluster, a
request is sent to a monitor for the current version of the cluster map. On subsequent
communications, OSDs lazily provide incremental updates of the cluster map. The &lt;a href=&quot;https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf&quot;&gt;RADOS
paper&lt;/a&gt; describes how Ceph clients, OSDs, and monitors interact.&lt;/p&gt;

&lt;h1 id=&quot;eventual-consistency-in-ceph&quot;&gt;Eventual Consistency in Ceph&lt;/h1&gt;

&lt;p&gt;Zhang et al. describe their approach to eventual consistency in Ceph in their &lt;a href=&quot;http://www.cs.nthu.edu.tw/~ychung/conference/ICPADS-2016.pdf&quot;&gt;PROAR paper&lt;/a&gt;. Their
replication strategy differs from standard Ceph in how it determines which OSD (i.e., which replica) receives read and write
operations from clients, called primary role hash ring (PROAR). The PROAR (“primary role hash ring”) algorithm cycles through
the ordered list of OSDs calculated with CRUSH, using the ID of the data object being written. For
instance, if the PG replication factor is 3, and we have the object 4, then 3 % 4 = 1, thus the
write operation is sent to OSD 1. This is depicted in the diagram below, adapted from Figure 3 in
the PROAR paper. When the write operation is received and applied, OSD 1 immediately acknowledges
the write, without waiting for acknowledgements from other replicas. Presumably, OSDs are still
expected to communicate and lazily replicate data among themselves. The name “hash ring” suggests a
similarity to the consistent hashing ring described in the &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=1294281&quot;&gt;Dynamo paper&lt;/a&gt;. While PROAR
iterates over the OSDs circularly, CRUSH provides consistent hashing.&lt;/p&gt;

&lt;p&gt;By distributing writes across the OSDs of a PG and acknowledging the applied write from the same
OSD, PROAR improves write latency at the expense of consistency. PROAR claims to provide eventual
consistency, and the fact that operations on a given object are deterministically directed to the
same replica can help to ameliorate typical replication anomalies, such as stale reads.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/diversifying-consistency-in-ceph/proar-hash-ring.png&quot; /&gt;
  &lt;figcaption&gt;PROAR PG replication circle, based on Figure 3 in the PROAR paper&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;other-eventually-consistent-replication-strategies&quot;&gt;Other eventually consistent replication strategies&lt;/h2&gt;

&lt;p&gt;The PROAR paper proposes one approach to supporting eventual consistency in Ceph. We could also
consider other ways to modify the various replication strategies currently supported by RADOS. The
&lt;a href=&quot;https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf&quot;&gt;RADOS paper&lt;/a&gt; describes three replication strategies: primary-copy, chain, and splay. Figure 2 from
the RADOS paper is included below as a reference point.
We’ll look at each of these replication strategies and consider how we might tweak them to lower write latency.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/diversifying-consistency-in-ceph/rados-figure2-replstrategies.png&quot; /&gt;
  &lt;figcaption&gt;Figure 2 from the RADOS paper: replication strategies implemented by RADOS&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The default replication strategy for Ceph OSDs is &lt;em&gt;primary-copy&lt;/em&gt;, in which clients are expected to send all
write and read operations to the primary OSD for the PG. An acknowledgment of an applied write, or
&lt;em&gt;ack&lt;/em&gt;, is sent to the client from the primary OSD. A client only receives an ack when it is known
that all replicas have received the write. This ensures strong consistency, therefore maximizing
data safety, or the ability to recover data after failures. The different replication strategies
have different policies for which OSD sends an ack to the client and to which OSD a client
should send read operations, but in all three replication strategies, write operations are always sent to the
primary OSD.&lt;/p&gt;

&lt;p&gt;In each replication strategy that RADOS uses, write latency includes time for each replica to
acknowledge a write operation. As a consequence of where read operations are directed and when the
primary OSD acks to the client, read operations are always strongly consistent. In the below
discussion, we consider alternative approaches that could lower write latency, including changing
when a client receives an ack and which OSD to send read and write operations to.  We want to continue to ensure that read operations will only observe the effects of writes that have already been applied at &lt;em&gt;some&lt;/em&gt; OSD; we won’t allow reading of a write that has not yet been applied.  However, reads may observe the effects of writes that have been applied at only one replica, or a subset of replicas, instead of &lt;em&gt;all&lt;/em&gt; replicas.&lt;/p&gt;

&lt;p&gt;For primary-copy replication, let’s compare the behavior of read operations between the primary OSD and
secondary OSDs if we change when the primary OSD acks relative to a received
read operation. Currently, the primary replica acks after a write has been applied at all replicas, thus reads
return a strongly consistent value.
Alternatively, we could ack once the write has been applied just at the primary.
Other replicas could send an ack to the client asynchronously after applying the write.
In this case, reads are strongly consistent as long as the primary is
active, and weak reads occur if the primary goes down and other replicas have not received
and applied all writes. This level of consistency is the same as PROAR, the difference being that
all writes and reads are still funneled through the primary OSD which could have increased ack latency during times of high load.&lt;/p&gt;

&lt;p&gt;In this weakening of the primary-copy replication strategy, an epoch for tracking which replicas
have received which updates would be necessary, because we can no longer rely on the primary
logically being the last to apply an update. Note that when the primary OSD fails in the standard
primary-copy replication strategy, a newly elected primary OSD may not have received all writes
from the former primary OSD.  The same recovery process can be used for the weakened and
standard primary-copy replication strategies, although recovery time for the weakened replication
strategy would likely be longer. PROAR approaches failure recovery by maintaining a PG log at each
OSD in the PG.&lt;/p&gt;

&lt;p&gt;For chain replication, we consider how we can change which replica receives read operations and
which replica acks to the client. Reads sent to the primary will always be strongly consistent, but
will return values for writes that may not have been propagated to other replicas. Typically, reads
sent to the last replica in the chain (the &lt;em&gt;tail&lt;/em&gt;) are strongly consistent because acks are sent from
the tail. If the primary replica were to ack instead of the tail, then reads sent to the tail can
be, at most, &lt;em&gt;n&lt;/em&gt;-1 versions old, assuming &lt;em&gt;n&lt;/em&gt; updates in a row, where each OSD in the chain from the primary to the tail has a version of the data object that is at least as old as that of its predecessor.
In general, if the replica that acks writes (say, OSD&lt;sub&gt;ack&lt;/sub&gt;) is &lt;em&gt;at least as far&lt;/em&gt; down the chain than the replica that receives reads (OSD&lt;sub&gt;read&lt;/sub&gt;), then reads are strongly consistent.  Furthermore, the number of replicas in the chain between OSD&lt;sub&gt;read&lt;/sub&gt; and OSD&lt;sub&gt;ack&lt;/sub&gt; is the “replication factor”, or the number of replicas on which each write will be applied.&lt;/p&gt;

&lt;p&gt;Finally, we consider splay replication. The main difference between primary-copy and splay is which
OSD acks to the client that a write has been applied. This allows better load balancing by
splitting read and write workloads across the primary and tail replicas. This difference is
considered in our above weakening of primary-copy replication. So, we observe that because the
primary replica applies writes before propagation to other replicas, then to reduce latency the
tail should ack concurrently while applying writes. It seems that changes to primary-copy and splay
replication converge to similar protocols for which replica acks to the client, and which replica
receives read operations. This is due to wanting to prevent dirty reads at the primary replica.&lt;/p&gt;

&lt;h1 id=&quot;bolt-on-causal-consistency&quot;&gt;Bolt-on Causal Consistency&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;http://www.bailis.org/papers/bolton-sigmod2013.pdf&quot;&gt;Bolt-on causal consistency&lt;/a&gt;, as proposed by Bailis et al., is a mechanism for
upgrading the consistency guarantee provided by a datastore from eventual to causal consistency.
This approach is useful for datastores such as Cassandra, which &lt;a href=&quot;https://decomposition.al/CMPS290S-2018-09/2018/11/29/consistency-in-cassandra.html&quot;&gt;support weak and strong
consistency via quorum consistency&lt;/a&gt;, but cannot natively support causal
consistency.&lt;/p&gt;

&lt;p&gt;Bolt-on causal consistency assumes that the underlying datastore is eventually consistent and
implements a &lt;em&gt;shim layer&lt;/em&gt; over it. This would seem to suggest that if we can support eventual
consistency in Ceph, whether with the PROAR approach or with one of the other replication
approaches I’ve sketched out, then we can also have causal consistency “for free” by means of the
bolt-on approach. Another assumption is that the bolt-on shim layer has access to the merge
function of the datastore. For instance, Bailis et al. note that the last-writer-wins merge function
requires knowledge of which writes are concurrent, or do not have sequential
dependence. This merge function is not implemented in Ceph, since it currently only
supports strong consistency. Though it may be possible to support last-writer-wins semantics by adding a
version to each object, it would result in an undesirable data overhead for applications with many
objects. Another major, but straightforward, assumption is that the shim layer will be deployed with
clients as well.&lt;/p&gt;

&lt;p&gt;Finally, bolt-on assumes that reads will
obey the happens-before relation, that the datastore provides single-value register semantics instead of
multiple versions, and that there are no callbacks from the datastore. These assumptions all hold in Ceph: the happens-before relation is obeyed because clients deterministically send write operations to the same replica for a given
object, and the weakened versions of each replication strategy do not allow dirty reads; Ceph updates objects in place, and so provides single-value register semantics; and callbacks are not present in the POSIX interface to Ceph’s subsystem.&lt;/p&gt;

&lt;h1 id=&quot;final-thoughts-and-next-steps&quot;&gt;Final Thoughts and Next Steps&lt;/h1&gt;

&lt;p&gt;It seems to me that implementing weaker consistency in Ceph can be consolidated in OSDs because of the
decoupling between cluster state (maintained by the cluster map) and write replication. This means
that Ceph’s monitors and use of CRUSH can provide the state of the cluster in a strongly consistent
manner, without affecting the consistency level of read and write operations. The proposed
weakenings above require changes to when a write is acknowledged, which replica acknowledges an
applied write, or which replicas should receive read operations. These changes appear possible in
specific places in the protocol, but they may affect many other aspects such as the correctness,
complexity, or runtime of disaster recovery. Understanding the depths of these details is beyond
the scope of this post.&lt;/p&gt;

&lt;p&gt;Once the use of a variety of consistency models is possible in Ceph, providing tools to make
development easier in a mixed-consistency environment would be nice.
Language-level abstractions such as the ones I discussed in &lt;a href=&quot;/CMPS290S-2018-09/2018/11/21/mixing-consistency-in-a-programmable-storage-system.html&quot;&gt;my previous post&lt;/a&gt; could play an important role here.&lt;/p&gt;

&lt;!-- link URLs --&gt;</content><author><name>Aldrin Montana</name></author><summary type="html">by Aldrin Montana · edited by Austen Barker and Lindsey Kuper</summary></entry><entry><title type="html">Simplifying Agreement: Language Support for Consensus</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/12/10/simplifying-agreement-language-support-for-consensus.html" rel="alternate" type="text/html" title="Simplifying Agreement: Language Support for Consensus" /><published>2018-12-10T00:00:00+00:00</published><updated>2018-12-10T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/12/10/simplifying-agreement-language-support-for-consensus</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/12/10/simplifying-agreement-language-support-for-consensus.html">&lt;p&gt;by Devashish Purandare · edited by Aldrin Montana and Lindsey Kuper&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;/CMPS290S-2018-09/2018/11/19/manufacturing-consensus-an-overview-of-distributed-consensus-implementations.html&quot;&gt;my last post&lt;/a&gt;, we explored consensus mechanisms and why it is so hard to get
them right. Implementing consensus and other distributed protocols often requires implementing a lot of supporting functionality, resulting in long and complex code. This makes it
hard to verify whether the implemented protocol is true to its formal definition.
The &lt;a href=&quot;https://github.com/denizalti/paxosmmc&quot;&gt;“Paxos Made Moderately Complex” implementation&lt;/a&gt;
may be only 451 lines of Python code, but implementations used in state-of-the-art
systems are &lt;a href=&quot;https://github.com/etcd-io/etcd/tree/master/raft&quot;&gt;much more complex&lt;/a&gt;. Popular Paxos and Raft implementations run into the thousands of lines of code. As the authors of &lt;a href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/paxos_made_live.pdf&quot;&gt;“Paxos Made Live”&lt;/a&gt; point out:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;While Paxos can be described with a page of pseudo-code, our complete implementation contains several thousand lines of C++ code. The blow-up is not due simply to the fact that we used C++ instead of pseudo notation, nor because our code style may have been verbose. Converting the algorithm into a practical, production-ready system involved implementing many features and optimizations – some published in the literature and some not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When using a general-purpose programming language to implement Paxos, then, we seem to have two options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We can use a higher-level language like Python; such an implementation may be concise and relatively easy to understand, but not suitable for scalability and high performance.&lt;/li&gt;
  &lt;li&gt;Alternatively, we can use a lower-level language, such as C++, and write a scalable, high-performance implementation that is verbose and hard to understand.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is there any way to have both high performance &lt;em&gt;and&lt;/em&gt; ease of programmability?  Domain-specific languages (DSLs) &lt;a href=&quot;http://ppl.stanford.edu/papers/pact11-brown.pdf&quot;&gt;promise a way to have both programmability and performance by trading off generality&lt;/a&gt; – so let’s see how well that works!&lt;/p&gt;

&lt;p&gt;In this blog post I plan to explore a special class of domain-specific languages
(DSLs) that have been designed for implementing
consensus protocols.&lt;/p&gt;

&lt;h2 id=&quot;domain-specific-languages-for-distributed-systems&quot;&gt;Domain-Specific Languages for Distributed Systems&lt;/h2&gt;

&lt;p&gt;Programming distributed systems requires complex reasoning about the timing and order of messages sent and received, synchronization conditions, and other distributed-systems-specific concepts.  In general-purpose programming languages, it is hard to implement distributed algorithms while maintaining simplicity.&lt;/p&gt;

&lt;p&gt;Various DSLs intended for implementing distributed algorithms – especially consensus algorithms – have been proposed, such as &lt;a href=&quot;https://sites.google.com/site/distalgo/home&quot;&gt;DistAlgo&lt;/a&gt;, &lt;a href=&quot;http://bloom-lang.net/&quot;&gt;Bloom&lt;/a&gt;, &lt;a href=&quot;http://dl.acm.org/citation.cfm?id=1095818&quot;&gt;Overlog&lt;/a&gt;, and &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=2837650&quot;&gt;PSync&lt;/a&gt;. In this post we will do a deep dive into one of these: DistAlgo.&lt;/p&gt;

&lt;h2 id=&quot;distalgo&quot;&gt;DistAlgo&lt;/h2&gt;

&lt;p&gt;You might remember the infamous abstract from &lt;a href=&quot;https://lamport.azurewebsites.net/pubs/paxos-simple.pdf&quot;&gt;“Paxos Made Simple”&lt;/a&gt;: “The Paxos algorithm, when presented in plain English, is very simple.”  Yet, as we’ve seen,
implementing Paxos in practice is not so simple.  Some of the difficulty in trying to convert English pseudo-code into running code comes from the fact that the programming language we are using is not especially suited for expressing consensus protocols.&lt;/p&gt;

&lt;h3 id=&quot;a-new-hope&quot;&gt;A New Hope&lt;/h3&gt;

&lt;p&gt;What if
we could express distributed protocols in working code as easily as they are stated in pseudo-code?  This is where &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=2384645&quot;&gt;DistAlgo&lt;/a&gt;
comes in! DistAlgo is a programming language that emphasizes the ability to clearly describe distributed algorithms. Prototyped in Python,
DistAlgo was built specifically to address the lack of support in general-purpose programming
languages for expressing constructs required for distributed algorithms.&lt;/p&gt;

&lt;p&gt;The DistAlgo team has implemented a &lt;a href=&quot;https://github.com/DistAlgo/distalgo/tree/master/da/examples&quot;&gt;variety of distributed algorithms&lt;/a&gt;
to showcase their language’s ability to express these algorithms in running code,
which can then be optimized, tested, and even formally verified.&lt;/p&gt;

&lt;h3 id=&quot;paxos-in-distalgo&quot;&gt;Paxos in DistAlgo&lt;/h3&gt;

&lt;p&gt;In &lt;a href=&quot;https://arxiv.org/pdf/1704.00082.pdf&quot;&gt;“Moderately Complex Paxos Made Simple,”&lt;/a&gt; the creators of DistAlgo discuss implementing a version of Paxos in DistAlgo.  The paper is an excellent read, not only for the implementation of Paxos that
they walk the reader through, but also the insight they offer on what makes
Paxos so hard to understand and implement.  For instance, Lamport’s prose description of Paxos in the “Paxos Made Simple” paper involves the notion of a “promise”, and the authors of “Moderately Complex Paxos Made Simple” devote some effort to making this notion precise, commenting:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Indeed, this is the hardest part for understanding Paxos,
because understanding an earlier phase requires understanding a later phase,
which requires understanding the earlier phases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The cyclic nature of the definition of the Paxos protocol is the cause of a lot
of the confusion surrounding it, and that’s where &lt;a href=&quot;https://raft.github.io/&quot;&gt;Raft&lt;/a&gt; diverges from Paxos in its effort to
simplify consensus. (Although, as mentioned &lt;a href=&quot;/CMPS290S-2018-09/2018/11/19/manufacturing-consensus-an-overview-of-distributed-consensus-implementations.html&quot;&gt;in my previous post&lt;/a&gt;, it is an &lt;a href=&quot;https://twitter.com/copyconstruct/status/1061818753925578753&quot;&gt;open question&lt;/a&gt;
whether Raft succeeds in doing that.)&lt;/p&gt;

&lt;h3 id=&quot;distalgos-superpowers&quot;&gt;DistAlgo’s Superpowers!&lt;/h3&gt;

&lt;p&gt;DistAlgo supports the notion of a process, which presents interfaces for initialization 
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup&lt;/code&gt;), code execution (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt;), and handling of received messages (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receive&lt;/code&gt;).
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt; makes the process allow handling of unhandled messages, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt;
allows timeouts. Queries on message histories are divided into comprehensions, aggregations, and quantifications over sets.&lt;/p&gt;

&lt;p&gt;This allows an easy implementation of simple Paxos, using just 72 lines of code.
The DistAlgo implementation can be compiled to Python or run directly.  The authors also argue that the high-level nature of the DistAlgo code allows easy translation to
&lt;a href=&quot;https://lamport.azurewebsites.net/tla/tla.html&quot;&gt;TLA+&lt;/a&gt; for subsequent verification.&lt;/p&gt;

&lt;h3 id=&quot;moderately-complex-paxos-made-simple&quot;&gt;Moderately Complex Paxos Made Simple&lt;/h3&gt;

&lt;p&gt;The authors implement the version of Paxos we saw in &lt;a href=&quot;http://dl.acm.org/citation.cfm?id=2673577&quot;&gt;Paxos Made Moderately
Complex&lt;/a&gt;, the version with slots and scouts and commanders! Unlike the original paper, though, they encapsulate the functionality of commanders and scouts back into the
leader to allow simpler code. The split in the original paper seemed unnecessary
from a correctness point of view; this assigns their actions to the leader.&lt;/p&gt;

&lt;p&gt;The implementation ends up being about 100 lines of code in DistAlgo, and 300 in
Python. While this is smaller than the 450-line Python implementation of the original,
it also reduces or repackages some functionality.&lt;/p&gt;

&lt;p&gt;In DistAlgo, it is trivial to
implement an optimization which keeps just the maximum numbered ballot for each
slot. The paper shows how this can be achieved by changing just one line of the code:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;accepted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;to&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;accepted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This will pick the maximum ballot for each slot instead of keeping every ballot.&lt;/p&gt;

&lt;p&gt;They also implement leader leases with timeout, similar optimization to the Google paper
to prevent unnecessary contention because of multiple readers.&lt;/p&gt;

&lt;p&gt;The authors also manually translate the DistAlgo code into &lt;a href=&quot;http://lamport.azurewebsites.net/tla/tla.html&quot;&gt;TLA+&lt;/a&gt; and mechanically verify the TLA+ specification using &lt;a href=&quot;http://tla.msr-inria.inria.fr/tlaps/content/Home.html&quot;&gt;TLAPS&lt;/a&gt;, discovering and correcting a safety violation in the original “Paxos Made Moderately Complex” pseudo-code which may cause &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preempt&lt;/code&gt; messages to never be delivered to the leaders.&lt;/p&gt;

&lt;p&gt;Although initially enticed by the idea of implementing Paxos in DistAlgo, I am not sure DistAlgo achieves what it
sets out to do. It doesn’t save a lot on the size of the code, and while the code
complexity is reduced, the original code for “Paxos Made Moderately Complex” was not very complex to begin with. What is somewhat disappointing is that both
resort to Python in the end, in a prototype which is not practical or useful to
the systems community and is limited by Python’s limited support for multi-core
processing. For now, it offers us a relatively simple-to-write implementation of
Multi-Paxos that is presumably easy to manually convert to a TLA+ specification, but at least for the current stage of the prototype, it fails to
deliver on practical applicability.&lt;/p&gt;

&lt;h3 id=&quot;raft-in-distalgo&quot;&gt;Raft in DistAlgo&lt;/h3&gt;

&lt;p&gt;Although the DistAlgo developers have an &lt;a href=&quot;https://github.com/DistAlgo/distalgo/blob/master/da/examples/raft/orig.da&quot;&gt;implementation of Raft&lt;/a&gt; in the “examples” directory of their &lt;a href=&quot;https://github.com/DistAlgo/distalgo/&quot;&gt;GitHub repo&lt;/a&gt;, Raft isn’t described in any of their papers.
This makes it unclear if the committed example is indeed complete or correct.  Nevertheless I dived into the specification code, so we can check for ourselves.
Testing the implementation was harder than expected. The pip module fails
installation, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt; stops with an error. Whatever is working for the
authors doesn’t seem to work for me. I checked with a fresh uncorrupted Python
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;virtualenv&lt;/code&gt; just to make sure, but it fails with similar issues. Thankfully,
the authors also provide binaries with the distribution, which seem to work.&lt;/p&gt;

&lt;p&gt;Executing the Raft code spits out a wall of text that is the log messages produced by an example run of the protocol:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; ./dar ../da/examples/raft/orig.da
../da/examples/raft/orig.da compiled with 0 errors and 0 warnings.
[369] da.api&amp;lt;MainProcess&amp;gt;:INFO: &amp;lt;Node_:b2c01&amp;gt; initialized at 127.0.0.1:(UdpTransport=45231, TcpTransport=41250).
[369] da.api&amp;lt;MainProcess&amp;gt;:INFO: Starting program &amp;lt;module 'orig' from '../da/examples/raft/orig.da'&amp;gt;...
[370] da.api&amp;lt;MainProcess&amp;gt;:INFO: Running iteration 1 ...
[370] da.api&amp;lt;MainProcess&amp;gt;:INFO: Waiting for remaining child processes to terminate...(Press &quot;Ctrl-C&quot; to force kill)
[2542] orig.Server&amp;lt;Server:17c06&amp;gt;:OUTPUT: Heartbeat timeout, transitioning to Candidate state.
[2549] orig.Server&amp;lt;Server:17c06&amp;gt;:OUTPUT: Transitioning to Leader.
[3898] orig.Server&amp;lt;Server:17c06&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c09&amp;gt;:0  at index 1 applied to state machine.
[3899] orig.Client&amp;lt;Client:17c09&amp;gt;:OUTPUT: Request 1 complete.
[3899] orig.Server&amp;lt;Server:17c06&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c08&amp;gt;:0  at index 2 applied to state machine.
[3900] orig.Client&amp;lt;Client:17c08&amp;gt;:OUTPUT: Request 1 complete.
[3901] orig.Server&amp;lt;Server:17c05&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c09&amp;gt;:0  at index 1 applied to state machine.
[3901] orig.Server&amp;lt;Server:17c03&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c09&amp;gt;:0  at index 1 applied to state machine.
[3901] orig.Server&amp;lt;Server:17c06&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c07&amp;gt;:0  at index 3 applied to state machine.
[3902] orig.Client&amp;lt;Client:17c07&amp;gt;:OUTPUT: Request 1 complete.
.
.
.
.
[6923] orig.Client&amp;lt;Client:17c07&amp;gt;:OUTPUT: Request 3 complete.
[6926] orig.Server&amp;lt;Server:17c04&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c07&amp;gt;:2  at index 9 applied to state machine.
[6927] orig.Server&amp;lt;Server:17c02&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c07&amp;gt;:2  at index 9 applied to state machine.
[6926] orig.Node_&amp;lt;Node_:b2c01&amp;gt;:OUTPUT: All clients done.
[6929] orig.Server&amp;lt;Server:17c05&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c07&amp;gt;:2  at index 9 applied to state machine.
[6929] orig.Server&amp;lt;Server:17c03&amp;gt;:OUTPUT: LogEntry:1:&amp;lt;Client:17c07&amp;gt;:2  at index 9 applied to state machine.
[6929] da.api&amp;lt;MainProcess&amp;gt;:INFO: Main process terminated.


&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Curious as to what it was doing, I profiled the execution in the instruments
provided by Xcode.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/raft.png&quot; alt=&quot;Instrumenting a DistAlgo run of Raft&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;The process spawns three threads (processes in an instance of Raft) and tries the
leader election protocol between them. In the execution, we can see the distribution
of work between threads, with more weight associated with more work, the particular
leader of that round. The Raft process updates state machines with the committed
entries.&lt;/p&gt;

&lt;p&gt;Sadly, the comments do not tell us anything about the code and the execution. At
best it seems to be a proof of concept that Raft could be implemented in DistAlgo. One interesting thing is that the implementation
ends up being ~240 lines of code, with the expanded Python being around 360 lines. This
is in contrast to the much shorter ~100-line implementations of the Paxos protocols.
It is not completely clear why the Raft implementation is so much longer, but based on my interpretation of the code, the
leader election code and changes to allow support for “idle mode” increase the length of the code. The code also &lt;a href=&quot;https://github.com/DistAlgo/distalgo/blob/master/da/examples/raft/orig.da#L96&quot;&gt;combines several checks into single conditions&lt;/a&gt;, resulting in dense code like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentRole&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Leader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commitIndex&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt;
                        &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matchIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matchIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;peers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;term&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentTerm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Updating commitIndex from %d to %d&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commitIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;commitIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Idle timeout is half of normal term timeout:
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;termTimeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Idle timeout triggered.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;has_idled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing Thoughts&lt;/h2&gt;

&lt;p&gt;When I started writing this post, the area of DSLs for distributed systems seemed
very promising. However, the state of the art turns out to be a bit
demoralizing. The DSLs mentioned in this post include fascinating ideas that
could change how we program distributed systems, but all of them are buggy and incomplete prototypes.  None of the languages mentioned at the start of this post have advanced beyond
the proof-of-concept stage.&lt;/p&gt;

&lt;p&gt;If you want to use consensus in
your system today, it would be better to go with &lt;a href=&quot;https://github.com/etcd-io/etcd&quot;&gt;battle-tested implementations&lt;/a&gt; in general-purpose languages, as verbose and complex as they might be.  We need more researchers to look into the space of performance- and scalability-oriented DSLs for expressing distributed algorithms, and I hope the projects
discussed in this post mature beyond the prototype stage and become strong contenders.&lt;/p&gt;</content><author><name>Devashish Purandare</name></author><summary type="html">by Devashish Purandare · edited by Aldrin Montana and Lindsey Kuper</summary></entry><entry><title type="html">Implementing a Garbage-Collected Graph CRDT (Part 2 of 2)</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/12/08/implementing-a-garbage-collected-graph-crdt-part-2-of-2.html" rel="alternate" type="text/html" title="Implementing a Garbage-Collected Graph CRDT (Part 2 of 2)" /><published>2018-12-08T00:00:00+00:00</published><updated>2018-12-08T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/12/08/implementing-a-garbage-collected-graph-crdt-part-2-of-2</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/12/08/implementing-a-garbage-collected-graph-crdt-part-2-of-2.html">&lt;script type=&quot;text/javascript&quot; src=&quot;http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML,https://decomposition.al/javascripts/MathJaxLocal.js&quot;&gt;
&lt;/script&gt;

&lt;p&gt;by Austen Barker · edited by Sohum Banerjea and Lindsey Kuper&lt;/p&gt;

&lt;h2 id=&quot;background--recap&quot;&gt;Background &amp;amp; Recap&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&quot;/CMPS290S-2018-09/2018/11/12/implementing-a-garbage-collected-graph-crdt-part-1-of-2.html&quot;&gt;my previous blog post&lt;/a&gt;, we discussed &lt;a href=&quot;https://hal.inria.fr/inria-00609399v1/document&quot;&gt;Conflict-Free Replicated Types&lt;/a&gt; (CRDTs), a class of specialized data structures designed to be replicated across a distributed system. We implemented a few CRDTs as described in &lt;a href=&quot;https://hal.inria.fr/inria-00555588/document&quot;&gt;Shapiro et al.’s &lt;em&gt;A Comprehensive Study of Convergent and Commutative Replicated Data Types&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CRDT implementations often run into the issue of distributed garbage collection, a perennial problem in distributed systems.  My previous post discussed multiple challenges involved with implementing distributed garbage collection: high metadata storage costs, fault intolerance, and the need for synchronization, which runs counter to the asynchronous nature of a CRDT.&lt;/p&gt;

&lt;p&gt;To tackle these problems,  of research avenues, including Paxos Commit and Two-Phase Commit, &lt;a href=&quot;https://arxiv.org/pdf/1603.01529.pdf&quot;&gt;delta state CRDTs&lt;/a&gt;, and various methods for reducing metadata overhead in Shapiro et al.’s garbage collection scheme. We explore these different garbage collection mechanisms in the context of our two-phase set (2P-Set) CRDT implementation.&lt;/p&gt;

&lt;h2 id=&quot;delta-state-crdts&quot;&gt;Delta State CRDTs&lt;/h2&gt;

&lt;p&gt;Almeida, Shoker, and Baquero proposed &lt;a href=&quot;https://arxiv.org/pdf/1603.01529.pdf&quot;&gt;delta state CRDTs&lt;/a&gt;, or delta-CRDTs for short, which help to avoid the issue of sending the entire state of a data structure over a network. The potentially large message sizes involved with classical state-based CRDTs result in them only being practical for small objects such as counters. A solution to this problem is to transmit a &lt;em&gt;delta&lt;/em&gt; that encompasses only changes made to a replica. Not only do delta-CRDTs address the problem of large message sizes, they also give us an efficient way to enable asynchronous garbage collection.&lt;/p&gt;

&lt;p&gt;In delta-CRDTs, deltas use the same join operations as traditional state-based CRDTs to apply updates to a local state, with the additional ability to join multiple deltas into a group. Temporally ordered deltas can also be joined into groups called delta-intervals which allow the programmer to transmit and join deltas in batches and assist in establishing a causal ordering of events.  Almeida, Shoker, and Baquero present two anti-entropy algorithms that respectively ensure eventual consistency and per-object causal consistency of delta-CRDTs.&lt;/p&gt;

&lt;p&gt;In the causal-consistency-ensuring anti-entropy algorithm, each node maintains two metadata maps (for keeping track of the sequence of deltas $D$ and the sequence of acknowledgments $A$), and a counter that is incremented each time a delta is joined with the local state. When a node sends a delta-interval to another, the receiving node replies with an acknowledgment after merging the interval into its local state. In practice, this delta-interval is every delta merged into the sending node’s local state after the last acknowledgment from the receiving node. A delta that has been acknowledged by all of a node’s neighbors is then garbage-collected and removed from the map $D$.&lt;/p&gt;

&lt;p&gt;In my previous blog post, we saw that for traditional CRDTs, the space complexity for storing the metadata necessary for $N$ nodes is $O(N^2)$ ($N$ vector clocks, each of size $N$, keeping track of the latest received vector clock from each replica).  This metadata is needed to show causal relationships between updates, so that we can know when an update is “stable” or received by all replicas.&lt;/p&gt;

&lt;p&gt;For delta-CRDTs, Almeida, Shoker, and Baquero show that for delta-CRDTs, the metadata storage cost at each node with $\lvert A \rvert$ neighbors and $\lvert D \rvert$ stored deltas is $O(\lvert A \rvert + \lvert D \rvert)$. That is, instead of the state scaling quadratically with more replicas, it grows linearly with how many neighbors each node is keeping track of and how many deltas have been sent.&lt;/p&gt;

&lt;p&gt;Even though the delta-CRDT metadata is only described as a way to enable garbage collection of deltas cached on each node, it can serve an additional purpose. The causal-consistency-enforcing anti-entropy algorithm does pretty much the same thing as the $O(N^2)$ scheme from the previous paper, but to garbage-collect deltas. So, why not garbage-collect deltas and tombstones together? When a delta that contains an operation that created a tombstone is garbage-collected, we can assume that the tombstone is also garbage-collected. We do need to associate tombstones with their deltas, but that can be done with a simple pointer.&lt;/p&gt;

&lt;p&gt;To apply this mechanism to &lt;a href=&quot;https://github.com/atbarker/CRDTexperiments/tree/master/Twopset&quot;&gt;our 2P-Set implementation&lt;/a&gt; from my previous post, we need only add the acknowledgment map, the delta map, and a counter. We can also re-use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct Twopset&lt;/code&gt; to represent deltas.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntMap&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Twopset&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;addGset&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gset&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;remGset&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gset&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ACK&lt;/span&gt;             &lt;span class=&quot;n&quot;&gt;IntMap&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;//map of acknowledgments from all neighbors&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Deltas&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;IntMap&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;//map of deltas currently &quot;in flight&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DeltaCounter&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;//incremented to the latest delta merged into the local state&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Interval&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;//if this is an interval then this is the earliest counter value encompassed by the interval, -1 if not an interval&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Newtwopset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Twopset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Twopset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
                &lt;span class=&quot;n&quot;&gt;addGset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewGset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;remGset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewGset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;DeltaCounter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;//overridden if the Twopset is a delta/interval&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Interval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In practice, it is difficult to perform add/remove operations on the local state and then forward them to other replicas. Instead, we can treat all local operations as deltas that, though immediately merged into the local state, are persisted in the local delta map until they have been acknowledged by all neighbors. This creates a log similar to those in database systems.&lt;/p&gt;

&lt;p&gt;Since the causally consistent delta-CRDT metadata allows us to garbage-collect tombstones, we can compare its space usage to the $O(N^2)$ overhead of Shapiro et al.’s approach. If we treat every node as neighboring every other node, the space complexity is $O(N+D)$. Therefore, if we keep the number of deltas in check and run local garbage collection regularly, the metadata overhead is considerably decreased. The $O(N)$ overhead may still prove costly at scale.  Therefore, instead of keeping track of each replica, we only keep track of a replica’s immediate neighbors, which effectively caps the maximum metadata size on each node.&lt;/p&gt;

&lt;p&gt;There are still a few drawbacks of delta-CRDTs. Firstly, they do not entirely eliminate the metadata overhead problem. Secondly, there still are cases where it is necessary to transmit the entire state, including when we have to send the empty map of deltas, and during crash recovery.&lt;/p&gt;

&lt;h3 id=&quot;synchronized-garbage-collection&quot;&gt;Synchronized Garbage Collection&lt;/h3&gt;

&lt;p&gt;One way of providing distributed garbage collection for CRDTs is with strong synchronization using a distributed commitment protocol such as &lt;a href=&quot;https://lamport.azurewebsites.net/video/consensus-on-transaction-commit.pdf&quot;&gt;Paxos Commit&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Two-phase_commit_protocol&quot;&gt;two-phase commit&lt;/a&gt;.  In this approach, the system will periodically run a garbage collection operation.  The frequency of this operation should be tuned to the rate at which tombstones are created, or the operation can be triggered when the system approaches a maximum number of tombstones.&lt;/p&gt;

&lt;p&gt;Under two-phase commit, each replica will vote on whether each tombstone is still necessary. If all the replicas agree that the tombstone is no longer needed, it is garbage-collected when the replicas are notified of the vote’s outcome. The replicas need not vote on each tombstone sequentially, and can submit a list of their tombstones to a coordinator, instead, receiving in return a list of tombstones to delete. The trade-off between this mechanism and the delta-CRDT approach is that while very little extra metadata is needed in order to perform garbage collection, it requires a lot of coordination between nodes,  which runs counter to the asynchronous nature of CRDTs. In addition, the need for a coordinator creates a single point of failure and undermines the inherent fault tolerance of CRDTs.&lt;/p&gt;

&lt;p&gt;Many implementations of two-phase commit will require additional entities (“transaction managers”) with which the replicas must register in order to perform garbage collection. These additional entities further complicate the programmer’s job, as they must not only account for the CRDT implementation (a non-trivial task, as we saw in my previous post), but also separate synchronization mechanisms.&lt;/p&gt;

&lt;p&gt;In the end, whether or not to use synchronization for garbage collection depends on which resources are scarce in the production environment. If local storage space is scarce enough, it may be worth the additional network overhead and compromised availability to explicitly synchronize.&lt;/p&gt;

&lt;h3 id=&quot;space-saving-optimizations&quot;&gt;Space-Saving Optimizations&lt;/h3&gt;

&lt;p&gt;If a CRDT is not expected to have a long lifespan, instead of doing garbage collection it is likely sufficient to simply perform some common space-saving optimizations, in order to minimize the effect of state explosion.&lt;/p&gt;

&lt;p&gt;As discussed in my previous post, a tombstone can actually be rather small.  In the case of a 2P-Set or an ARPO, the tombstone set can be represented as a bitmap with each bit corresponding to an element in the set being deleted. Using this trick, the storage space needed for tombstones become negligible. This does not eliminate the problem of garbage collection in its entirety, though, as the set can still contain deleted elements after all replicas have marked them as deleted. Therefore this sort of optimization is best applied in conjunction with another garbage collection scheme.&lt;/p&gt;

&lt;p&gt;As a programmer, a metadata overhead of $O(N^2)$ gives me pause, but as we saw with delta-CRDTs, it is possible to use less space and still perform distributed garbage collection.  There are also other ways of achieving the same goal of $O(N)$ metadata overhead.&lt;/p&gt;

&lt;p&gt;Recall that in Shapiro et al.’s approach, the key to garbage-collecting tombstones is to track causal relationships. In a &lt;a href=&quot;http://www.bailis.org/blog/causality-is-expensive-and-what-to-do-about-it/&quot;&gt;blog post&lt;/a&gt;, Peter Bailis discusses &lt;a href=&quot;https://www.sciencedirect.com/science/article/pii/002001909190055M&quot;&gt;work by Bernadette Charron-Bost&lt;/a&gt; that showed that $O(N)$ is the lowest timestamp overhead one can achieve while still providing the information necessary to show causal relationships.  Bailis suggests various ways to reduce the space costs of vector clocks. The last method he describes is restricting the number of replicas participating, either by putting a total upper bound on the number of participants, or by only requiring a replica to store information about its immediate “neighbors”. This may be one of the optimizations omitted from  the delta-CRDT paper that the authors nevertheless describe as being “important in practice”.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;CRDTs often require maintaining tombstones in order to properly handle deleted elements. Without some kind of garbage collection mechanism, these tombstones cause a CRDT to require unbounded space.&lt;/p&gt;

&lt;p&gt;Distributed garbage collection mechanisms are difficult to implement and are costly, whether that cost is in metadata overhead or synchronization. In the former case, we’ve discussed ways in which a programmer can significantly reduce the metadata cost, either through a few simple optimizations or through the use of delta-CRDTs. If a programmer wishes to use a state-based CRDT, it may be prudent for them to instead use a delta-CRDT as it provides a relatively easy solution to implementing garbage collection, among other benefits. In the latter case — synchronisation — there may be situations in which the additional overhead of performing a synchronized operation is considered acceptable.&lt;/p&gt;

&lt;p&gt;There are a multitude of environmental factors that can influence the programmer’s choice of CRDT garbage collection mechanism. For instance, if it’s desirable to be able to adjust the frequency of the garbage collection operation based on load, it would be easier to avoid synchronization and choose a mechanism that runs individually at each replica.&lt;/p&gt;

&lt;p&gt;The conclusion may be, of course, the same conclusion we drew in the last post: the easiest and best way to tackle this problem may just be to rely on CRDTs that do not require tombstones, or that keep the lifespan of a CRDT short.&lt;/p&gt;</content><author><name>Austen Barker</name></author><summary type="html"></summary></entry><entry><title type="html">Consistency in Cassandra</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/11/29/consistency-in-cassandra.html" rel="alternate" type="text/html" title="Consistency in Cassandra" /><published>2018-11-29T00:00:00+00:00</published><updated>2018-11-29T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/11/29/consistency-in-cassandra</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/11/29/consistency-in-cassandra.html">&lt;p&gt;by Natasha Mittal ⋅ edited by Devashish Purandare and Lindsey Kuper&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Today, popular NoSQL databases like &lt;a href=&quot;http://cassandra.apache.org/&quot;&gt;Cassandra&lt;/a&gt;, &lt;a href=&quot;https://www.mongodb.com/&quot;&gt;MongoDB&lt;/a&gt; or &lt;a href=&quot;https://hbase.apache.org/&quot;&gt;HBase&lt;/a&gt; claim to provide &lt;em&gt;eventual consistency&lt;/em&gt; and offer mechanisms to tune consistency.&lt;/p&gt;

&lt;p&gt;A consistency model is a contract between the distributed data store and its clients, in which if the clients agree to obey rules for ordering the read/write operations, the underlying data store will precisely specify the result of those operations. In the context of Cassandra, consistency &lt;a href=&quot;https://docs.datastax.com/en/archived/cassandra/2.0/cassandra/dml/dml_config_consistency_c.html&quot;&gt;“refers to how up-to-date and synchronized a row of Cassandra data is on all of its replicas”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Under strong consistency, all operations are seen in the same order by all the nodes in the cluster; that is, there must be a global total ordering of all read and write operations. This introduces high latency, as a lot of synchronization is required which hampers availability and scalability. On the other hand, eventual consistency merely guarantees that if no updates are made to a given data item, eventually all replicas will converge and return the last updated value of the data item.  It provides lower latency, as there is less synchronization overhead.&lt;/p&gt;

&lt;p&gt;Cassandra’s &lt;a href=&quot;http://cassandra.apache.org/doc/latest/architecture/dynamo.html#tunable-consistency&quot;&gt;&lt;em&gt;tunable consistency&lt;/em&gt;&lt;/a&gt; is intended to give clients the flexibility to adjust consistency levels to meet application requirements. Cassandra provides different read and write consistency levels, and users can fine-tune these levels by explicitly modifying Cassandra’s configuration file. The consistency level associated with an operation determines the number of replicas in the cluster that must respond with an acknowledgment for that operation to succeed.&lt;/p&gt;

&lt;p&gt;In this blog post, we will go over Cassandra’s consistency levels, &lt;a href=&quot;https://www.datastax.com/dev/blog/lightweight-transactions-in-cassandra-2-0&quot;&gt;Light Weight Transactions (LWTs)&lt;/a&gt; which provide serial consistency, some background on &lt;a href=&quot;https://amturing.acm.org/p558-lamport.pdf&quot;&gt;vector clocks&lt;/a&gt;, and the &lt;a href=&quot;https://aphyr.com/posts/294-call-me-maybe-cassandra&quot;&gt;2013 Jepsen analysis of Cassandra&lt;/a&gt; that revealed a number of consistency-related bugs.&lt;/p&gt;

&lt;h2 id=&quot;cassandras-model-of-consistency&quot;&gt;Cassandra’s Model of Consistency&lt;/h2&gt;

&lt;p&gt;Let’s establish a &lt;a href=&quot;http://cassandra.apache.org/doc/latest/architecture/dynamo.html#replication&quot;&gt;few definitions&lt;/a&gt; before getting started:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;RF (Replication Factor): the number of copies of each data item&lt;/li&gt;
  &lt;li&gt;R: the number of replicas that are contacted when a data object is accessed through a read operation (the &lt;em&gt;read set&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;W: the number of replicas that need to acknowledge the receipt of the update before the update completes (the &lt;em&gt;write set&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;QUORUM: sum_of_replication_factors/2 + 1, where sum_of_replication_factors = sum of all the replication factor settings for each data center&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If R + W &amp;gt; RF, the write set and the read set always overlap, resulting in what the Cassandra documentation describes as &lt;a href=&quot;http://cassandra.apache.org/doc/latest/architecture/dynamo.html#picking-consistency-levels&quot;&gt;“strong” consistency&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The right choices for RF, R and W in this model depend on the application for which the storage system is being used. In a write-intensive application, setting W=1 and R=RF can affect durability, as failures can result in conflicting writes. In read-intensive applications, setting W=RF and R=1 can affect the probability of the write succeeding.  To provide strong consistency and fault tolerance for a balanced mix of reads and writes, one should ensure that these two properties hold:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;R + W &amp;gt; RF&lt;/li&gt;
  &lt;li&gt;R = W = QUORUM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a system with configuration RF=3, W=2, and R=2 provides strong consistency.&lt;/p&gt;

&lt;p&gt;R + W &amp;lt;= RF is a weaker consistency model, where there is a possibility that the read and write set will not overlap and the system is vulnerable to reading from nodes that have not yet received updates that have already completed on other nodse.&lt;/p&gt;

&lt;h3 id=&quot;read-requests-in-cassandra&quot;&gt;Read Requests in Cassandra&lt;/h3&gt;

&lt;p&gt;As &lt;a href=&quot;https://docs.datastax.com/en/cassandra/3.0/cassandra/dml/dmlClientRequestsRead.html&quot;&gt;the Cassandra documentation explains&lt;/a&gt;, Cassandra can send three types of read requests to a replica: direct read requests, &lt;em&gt;digest&lt;/em&gt; requests, and background read repair requests.  A &lt;a href=&quot;https://wiki.apache.org/cassandra/DigestQueries&quot;&gt;digest request&lt;/a&gt; returns only a hash of the data being read instead of the actual data.  The purpose of a digest request is to allow quick comparisons of the contents of replicas: if the hashes disagree, then the actual data will disagree as well.&lt;/p&gt;

&lt;p&gt;When reading, the coordinator node sends a direct read request to one replica, and a digest request to a number of replicas determined by the read consistency level specified by the client. If all replicas are not in sync, the coordinator chooses the data with the latest timestamp and sends the result back to the client. Meanwhile, a background read repair request is sent to out-of-date replicas to ensure that the requested data is made consistent on all replicas.&lt;/p&gt;

&lt;p&gt;The following table shows some of the &lt;a href=&quot;https://docs.datastax.com/en/cassandra/3.0/cassandra/dml/dmlConfigConsistency.html#dmlConfigConsistency__dml-config-read-consistency&quot;&gt;read consistency levels&lt;/a&gt; that Cassandra provides:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Consistency Level&lt;/th&gt;
      &lt;th&gt;Usage&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;ALL&lt;/td&gt;
      &lt;td&gt;highest consistency and lowest availability&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;QUORUM&lt;/td&gt;
      &lt;td&gt;strong consistency with some level of failure&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;LOCAL_QUORUM&lt;/td&gt;
      &lt;td&gt;strong consistency which avoids inter-datacenter communication latency&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ONE&lt;/td&gt;
      &lt;td&gt;lowest consistency and highest availability&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;write-requests-in-cassandra&quot;&gt;Write Requests in Cassandra&lt;/h3&gt;

&lt;p&gt;For writes to a row, the coordinator node sends a write request to all the replicas that comprise the write set for that particular row. As long as all the replicas are available, they will get the write request regardless of the &lt;a href=&quot;https://docs.datastax.com/en/cassandra/3.0/cassandra/dml/dmlClientRequestsWrite.html&quot;&gt;write consistency level&lt;/a&gt; specified by the client. For a write operation to succeed, the number of replicas required to respond with an acknowledgement is determined by the write consistency level. So, if W = QUORUM and RF = 3, then write request will be sent to all three replicas, but an acknowledgment is expected from any two.&lt;/p&gt;

&lt;p&gt;Here are some of the &lt;a href=&quot;https://docs.datastax.com/en/cassandra/3.0/cassandra/dml/dmlConfigConsistency.html#dmlConfigConsistency__dml-config-write-consistency&quot;&gt;write consistency levels&lt;/a&gt; Cassandra provides:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Consistency Level&lt;/th&gt;
      &lt;th&gt;Usage&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;ALL&lt;/td&gt;
      &lt;td&gt;highest consistency and lowest availability&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;EACH_QUORUM&lt;/td&gt;
      &lt;td&gt;strong consistency but write fails when a data center is down&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;QUORUM&lt;/td&gt;
      &lt;td&gt;strong consistency with some level of failure&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;LOCAL_QUORUM&lt;/td&gt;
      &lt;td&gt;strong consistency which avoids inter-datacenter communication latency&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ONE&lt;/td&gt;
      &lt;td&gt;low consistency and high availability&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ANY&lt;/td&gt;
      &lt;td&gt;lowest consistency and highest availability and guarantees that write will never fail&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;lightweight-transactions-lwt&quot;&gt;Lightweight Transactions (LWT)&lt;/h2&gt;

&lt;p&gt;Applications like banking or airline reservations require operations to be appear to be performed at a single, instantaneous, global time. This is &lt;a href=&quot;https://cs.brown.edu/~mph/HerlihyW90/p463-herlihy.pdf&quot;&gt;linearizable&lt;/a&gt; consistency, which is one of the strongest single-object consistency models. Cassandra provides linearizability via &lt;a href=&quot;https://www.datastax.com/dev/blog/lightweight-transactions-in-cassandra-2-0&quot;&gt;lightweight transactions (LWTs)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the SQL-like &lt;a href=&quot;http://cassandra.apache.org/doc/latest/cql/&quot;&gt;Cassandra Query Language&lt;/a&gt;, LWTs are used for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPDATE&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt; operations that are conditioned on an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IF&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IF NOT EXISTS&lt;/code&gt; condition, such as:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transaction_date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customer_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2016&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;356&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;125&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXISTS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;UPDATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;230&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payment_date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2016&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;02&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customer_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;356&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;125&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To synchronize replica nodes for LWTs, Cassandra uses an implementation of the &lt;a href=&quot;https://lamport.azurewebsites.net/pubs/paxos-simple.pdf&quot;&gt;Paxos consensus protocol&lt;/a&gt;. There are four phases in this implementation of Paxos: &lt;strong&gt;prepare/promise&lt;/strong&gt;, &lt;strong&gt;read/results&lt;/strong&gt;, &lt;strong&gt;propose/accept&lt;/strong&gt; and &lt;strong&gt;commit/ack&lt;/strong&gt;. Thus, Cassandra makes four network round trips between the coordinator node and other replicas in the cluster to ensure linearizable execution, so performance is affected.  In fact, the Cassandra documentation points out that &lt;a href=&quot;http://cassandra.apache.org/doc/latest/cql/dml.html#update&quot;&gt;“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IF&lt;/code&gt; conditions will incur a non-negligible performance cost (internally, Paxos will be used) so this should be used sparingly.”&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prepare/promise is the most time-consuming phase of the Paxos algorithm. The leader node proposes a ballot number and sends it to all the replicas in the cluster. The replicas accept the proposal if the ballot number is the highest it has seen so far and sends back a promise message which includes the most recent proposal it has already received in advance.&lt;/p&gt;

&lt;p&gt;If a majority of the nodes promise to accept the ballot number, the leader can then move on to the next phase of the protocol. But if a majority of the nodes sent an earlier proposal with their promise message, the leader must use that value.&lt;/p&gt;

&lt;p&gt;If a leader node interrupts a previous leader node, then it must finish the previous leader’s proposal first and then proceed with its own proposal, thereby assuring the desired linearizable behavior. After the commit phase, the value written by LWT is visible to non-LWTs.&lt;/p&gt;

&lt;p&gt;The following is a (slightly anonymized) example of a Paxos trace in Cassandra (taken from one of my own Cassandra logs from a system I worked on):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Parsing insert into users (username, password, email ) values ( ‘mick’, ’mick’, ’mick@gmail.com' ) if
not exists; [SharedPool-Worker-1] | 2013-05-12 10:32:12.112000 | 127.0.0.1 | 1125
Sending PAXOS_PREPARE message to /127.0.0.3 [MessagingService-Outgoing-/127.0.0.3] | 2016-08-22 12:38:44.141000
| 127.0.0.1 | 10414
Sending PAXOS_PREPARE message to /127.0.0.2 [MessagingService-Outgoing-/127.0.0.2] | 2013-05-12 12:38:44.144200
| 127.0.0.1 | 10908
Promising ballot fb282190-685c-11e6-71a2-e0d2d098d5d6 [SharedPool-Worker-1] | 2013-05-12 12:38:44.149000 |
127.0.0.3 | 4325
Promising ballot fb282190-685c-11e6-71a2-e0d2d098d5d6 [SharedPool-Worker-1] | 2013-05-12 12:38:52.147000 |
127.0.0.3 | 4325
Promising ballot fb282190-685c-11e6-71a2-e0d2d098d5d6 [SharedPool-Worker-3] | 2013-05-12 12:38:52.166000 |
127.0.0.1 | 35282
Accepting proposal Commit(fb282190-685c-11e6-71a2-e0d2d098d5d6, [lwts.users] key=mick columns=[[] | [email
password]]\n Row: EMPTY | email=mick@gmail.com, password=mick) [SharedPool-Worker-2] |
2013-05-12 12:38:52.199000 | 127.0.0.1 | 67804
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are two consistency levels associated with &lt;a href=&quot;https://www.datastax.com/dev/blog/lightweight-transactions-in-cassandra-2-0&quot;&gt;LWTs&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;SERIAL&lt;/strong&gt;: where the Paxos consensus protocol will involve all the nodes across multiple data centers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;LOCAL_SERIAL&lt;/strong&gt;: where the Paxos consensus protocol will run on the local datacenter.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;jepsen&quot;&gt;Jepsen&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/jepsen-io/jepsen&quot;&gt;Jepsen&lt;/a&gt; is an open-source Clojure library, written by Kyle Kingsbury, designed to test the partition tolerance of distributed systems by fuzzing them with random operations. The results of these tests are analyzed to expose failure modes and to verify if the system violates any of the consistency properties it claims to have.  The Jepsen project &lt;a href=&quot;https://aphyr.com/posts/294-call-me-maybe-cassandra&quot;&gt;did an analysis of Cassandra in 2013&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As &lt;a href=&quot;https://www.youtube.com/watch?v=OnG1FCr5WTI&amp;amp;t=335s&quot;&gt;Joel Knighton explains in his talk about how the DataStax team uses Jepsen&lt;/a&gt;, a Jepsen test has three key properties:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;It is generative&lt;/strong&gt;: relies on randomized testing to explore the state space of distributed systems&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;It’s “black box”&lt;/strong&gt;: observes the system at client boundaries (does not need any tracing framework or apply some code patch in the distributed system to run the test)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;It relies on invariants&lt;/strong&gt;: it checks invariants from the recorded history of operations rather than runtime&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Knighton’s talk also covers the Jepsen &lt;a href=&quot;https://www.youtube.com/watch?v=OnG1FCr5WTI&amp;amp;t=368s&quot;&gt;test data structure&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{:name                    ...| name of the results
 :os                      ...| prepares the operating system
 :db                      ...| configures/starts/stops the database being tested
 :client                  ...| client protocol to interact with database
 :generator               ...| instructs on how to interact
 :conductors{:nemesis  ...}  | interacts with the environment
 :checker              ...}  | looks at and assesses the test run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, Knighton’s talk discusses &lt;a href=&quot;https://www.youtube.com/watch?v=OnG1FCr5WTI&amp;amp;t=507s&quot;&gt;how a Jepsen test runs&lt;/a&gt;.  As he explains, in Jepsen an &lt;em&gt;orchestration node&lt;/em&gt; has one thread representing each client of the system being tested, and a thread for the &lt;em&gt;nemesis&lt;/em&gt;, which is the Jepsen process that injects failures into the system as it runs.  The orchestration node connects to several nodes on which the Cassandra cluster is running.  Jepsen generates a stream of read and write operations for client threads and crash/corrupt/partition operations for the nemesis thread.  The result is a &lt;em&gt;history&lt;/em&gt; that shows which operations happened during the test and when.  Operations in the history are expressed as &lt;em&gt;windows&lt;/em&gt; that show when they began and ended.  Finally, Jepsen runs a &lt;em&gt;checker&lt;/em&gt; that can determine whether the history is valid according to some metric of correctness.  The checker can also produce an artifact to help explain performance characteristics, such as the latency of operations.&lt;/p&gt;

&lt;h3 id=&quot;jepsen-analysis-of-cassandra&quot;&gt;Jepsen Analysis of Cassandra&lt;/h3&gt;

&lt;h4 id=&quot;background-vector-clocks&quot;&gt;Background: Vector Clocks&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Vector_clock&quot;&gt;Vector clocks&lt;/a&gt; are a technique for determining whether pairs of events are causally related in a distributed system. Logical timestamps are generated for each event in the system, and their potential causality (i.e., their &lt;a href=&quot;https://amturing.acm.org/p558-lamport.pdf&quot;&gt;happens-before&lt;/a&gt; relationship) is determined by comparing those logical timestamps.&lt;/p&gt;

&lt;p&gt; The timestamp for an event is a vector of numbers, with each number corresponding to a process. Each process knows its position in the vector.  Each process assigns a timestamp to each event.&lt;/p&gt;

&lt;p&gt;For a message send event, the entire vector associated with that event is sent along with the message payload. When the message is received by a process, the receiving process does the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Increments the counter for the process’s position in the vector.&lt;/li&gt;
  &lt;li&gt;Performs an element-by-element comparison of the received vector with the process’s timestamp vector, and sets the process’s timestamp vector to the higher of the values:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;for (i=0; i &amp;lt; num_elements; i++) 
    if (received[i] &amp;gt; system[i])
        system[i] = received[i];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To determine if two events are concurrent, their vector timestamps are compared element-by-element.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;if each element of V1 timestamp &amp;lt;= each element of V2 timestamp, then V1 causally precedes V2, or&lt;/li&gt;
  &lt;li&gt;if each element of V2 timestamp &amp;lt;= each element of V1 timestamp, then V2 causally precedes V1, or&lt;/li&gt;
  &lt;li&gt;if neither of these conditions applies and some elements in V1 timestamp  is greater than while others are less than the corresponding elements in V2 timestamp, then the events are concurrent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following animation shows an example of vector clocks in a system with three interacting processes:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/vector_clock_final.gif&quot; /&gt;
&lt;/figure&gt;

&lt;h4 id=&quot;conflicting-writes-in-cassandra&quot;&gt;Conflicting writes in Cassandra&lt;/h4&gt;

&lt;p&gt;Cassandra uses a last-write-wins (LWW) policy to resolve write conflicts and does &lt;em&gt;not&lt;/em&gt; implement vector clocks &lt;a href=&quot;https://aphyr.com/posts/294-jepsen-cassandra&quot;&gt;“for performance reasons”&lt;/a&gt;. In this case, 
if a client A writes x=1, and another client B writes x=2, it is possible that the final value of x can be 1 or 2 depending upon which write comes second (the one with the most recent timestamp).&lt;/p&gt;

&lt;p&gt;In order to avoid this problem, Cassandra uses the concept of immutable data. For every update operation for a particular column, a &amp;lt;value,timestamp&amp;gt; pair is added. For example, for a particular column called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt;, one might have a series of updates, as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;name
Mick, 2017-11-23 12:11:23
Micky, 2017-11-24 17:13:45
Micky Lawson, 2017-12-02 09:34:09
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When a client makes a read request, a client-specific merge function is applied to all the column values and the desired result is obtained.  In the case of equal timestamps (i.e., a tie), &lt;a href=&quot;https://aphyr.com/posts/294-jepsen-cassandra&quot;&gt;the lexicographically greater value is chosen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this to happen, two timestamps need to collide, and it would seem to be a rare possibility that two writes would get exactly the same microsecond-resolution timestamp.  However, &lt;a href=&quot;https://aphyr.com/posts/294-call-me-maybe-cassandra&quot;&gt;the Jepsen analysis of Cassandra tested this&lt;/a&gt; by repeatedly changing a column value and found that 1 row is corrupted per 250 transactions.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1000 total
399 acknowledged
397 survivors
4 acknowledged writes lost!         //writes lost means corrupt data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is happening because in Cassandra, the time resolution is in milliseconds, not microseconds. The probability of writes conflicting is much higher for millisecond resolution, resulting in much corrupted data.&lt;/p&gt;

&lt;h4 id=&quot;session-consistency&quot;&gt;Session consistency&lt;/h4&gt;

&lt;p&gt;Since Cassandra uses a last-write-wins policy, the writes are ordered by wall-clock timestamps. One might expect that the “Read Your Writes” and “Monotonic Reads” &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=645792.668302&quot;&gt;session guarantees&lt;/a&gt; would hold.  However, the Jepsen tests of Cassandra introduce clock drifts due to which system clocks are unsynchronized, and the session guarantees no longer hold.&lt;/p&gt;

&lt;p&gt;Another issue arises in the event of a &lt;em&gt;leap second&lt;/em&gt;, which is &lt;a href=&quot;https://en.wikipedia.org/wiki/Leap_second&quot;&gt;“a one-second adjustment that is occasionally applied to civil time Coordinated Universal Time (UTC) to keep it close to the mean solar time at Greenwich, in spite of the Earth’s rotation slowdown and irregularities”&lt;/a&gt;.  Linux kernel systems handle leap seconds by taking a one-second backward jump.  When that happens, &lt;a href=&quot;https://aphyr.com/posts/299-the-trouble-with-timestamps&quot;&gt;the following situation can arise in Cassandra, as Kyle Kingsbury explains&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Say a client writes w1 just prior to a leap second, then writes w2 just after the leap second. Session consistency demands that any subsequent read will see w2–but since w2 has a lower timestamp than w1, Cassandra immediately ignores w2 on any nodes where w1 is visible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since system clocks are not monotonic, timestamps alone cannot be used for global total ordering of operations across all the data centers.&lt;/p&gt;

&lt;p&gt;Having worked extensively with Cassandra as a backend developer, I can say that these issues are prominent, and occur frequently during the copious amounts of transaction processing. This has forced enterprises to introduce hacks at the application level, thereby increasing complexity and making the application code lengthy.&lt;/p&gt;

&lt;h4 id=&quot;bugs-that-jepsen-analysis-found-in-cassandra&quot;&gt;Bugs that Jepsen analysis found in Cassandra&lt;/h4&gt;

&lt;p&gt;The Jepsen analysis of Cassandra found numerous issues that challenged Cassandra’s claim to offer linearizability via LWTs.  Here I’ll highlight two specific bugs.&lt;/p&gt;

&lt;h5 id=&quot;writetimeoutexception-when-lwt-concurrency-level--quorum&quot;&gt;WriteTimeoutException when LWT concurrency level = QUORUM&lt;/h5&gt;

&lt;p&gt;As explained in Cassandra bug &lt;a href=&quot;https://issues.apache.org/jira/browse/CASSANDRA-9328&quot;&gt;9328&lt;/a&gt; during high contention, the coordinator node “loses track” of whether the value it submitted to Paxos has been applied or not. For instance, in a banking application, the following situation could occur:&lt;/p&gt;

&lt;p&gt;Thread 1: Reads account_balance=$0.&lt;br /&gt;
Thread 1: Updates account_balance=$0+$100=$100 successfully, but still receives WriteTimeoutException.&lt;br /&gt;
Thread 2: Reads account_balance=$100.&lt;br /&gt;
Thread 2: Updates account_balance=$100+500=$600 successfully with no WriteTimeoutException.&lt;br /&gt;
Thread 1: Tries again and reads account_balance=$600, which is greater than its previous update.&lt;/p&gt;

&lt;p&gt;In this case, thread 1 cannot clearly identify whether its update failed or succeeded. It might assume that it failed and try again and add another $100 to the balance.&lt;/p&gt;

&lt;h5 id=&quot;incorrect-implementation-of-paxos&quot;&gt;Incorrect implementation of Paxos&lt;/h5&gt;

&lt;p&gt;In Paxos, the leader node proposes the highest-number ballot that has been accepted by the nodes. In case no node responds back with a value, the leader then proposes its own value.&lt;/p&gt;

&lt;p&gt;But Cassandra’s implementation of Paxos had a bug in which a value already accepted by some nodes could be ignored. As discussed in Cassandra bug &lt;a href=&quot;https://issues.apache.org/jira/browse/CASSANDRA-6012&quot;&gt;6012&lt;/a&gt;, the result is a system that “can mistakenly accept two different values for the same round.”&lt;/p&gt;

&lt;p&gt;Since the first analysis of Cassandra by Jepsen in 2013, the DataStax team has adapted Jepsen and further extended it by incorporating new tests to break the new versions of Cassandra, and this has helped to identify critical bugs in the implementation.&lt;/p&gt;</content><author><name>Natasha Mittal</name></author><summary type="html">by Natasha Mittal ⋅ edited by Devashish Purandare and Lindsey Kuper</summary></entry><entry><title type="html">Mixing Consistency in a Programmable Storage System</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/11/21/mixing-consistency-in-a-programmable-storage-system.html" rel="alternate" type="text/html" title="Mixing Consistency in a Programmable Storage System" /><published>2018-11-21T00:00:00+00:00</published><updated>2018-11-21T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/11/21/mixing-consistency-in-a-programmable-storage-system</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/11/21/mixing-consistency-in-a-programmable-storage-system.html">&lt;p&gt;by Aldrin Montana · edited by Abhishek Singh and Lindsey Kuper&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Modern distributed applications call for ways to store and access data using a range of consistency guarantees. Consider a distributed shared log, like that described in the &lt;a href=&quot;https://www.usenix.org/system/files/osdi18-lockerman.pdf&quot;&gt;FuzzyLog&lt;/a&gt; paper. A log service is useful in many contexts for applications including data management systems and storage systems. The FuzzyLog paper describes the use of log services for funneling updates for data management systems and other services (e.g., metadata and coordination services, filesystem namespaces) within data centers. The authors motivate their work by discussing the benefit of distributing the log across several servers and relaxing constraints on data consistency.&lt;/p&gt;

&lt;p&gt;Reasoning about mixed consistency becomes especially challenging where data accessed at one level of consistency is used in the computation for data at a different level of
consistency. In FuzzyLog, for instance, updates to the log are marked with &lt;em&gt;colors&lt;/em&gt; that enable a variety of consistency choices.  FuzzyLog makes operations to a single color across regions causally consistent, while operations to a single color within a region are serializable.  Hence, FuzzyLog uses at least two levels of consistency in its implementation.&lt;/p&gt;

&lt;p&gt;The need to use a range of consistency levels in the same application motivates the need for &lt;em&gt;language-level&lt;/em&gt; support for mixed consistency.  Recent language-level abstractions such as &lt;a href=&quot;http://kcsrk.info/papers/quelea_pldi15.pdf&quot;&gt;QUELEA&lt;/a&gt;, &lt;a href=&quot;https://homes.cs.washington.edu/~luisceze/publications/ipa-socc16.pdf&quot;&gt;IPA&lt;/a&gt;, and &lt;a href=&quot;http://www.cs.cornell.edu/andru/papers/mixt/mixt.pdf&quot;&gt;MixT&lt;/a&gt; aim to make programming in a mixed-consistency world safer and easier. QUELEA allows the developer to declaratively specify the consistency guarantees provided by a datastore and the consistency requirements for application-level operations. IPA provides types representing consistency levels that can be placed on abstract data type (ADT) instances, and policies for determining the consistency of a data type at runtime. MixT is a domain-specific language (DSL) that uses information flow analysis to prevent weakly consistent data from influencing strongly consistent data.&lt;/p&gt;

&lt;p&gt;In this blog post, we are interested in exploring approaches for specifying a range of consistency guarantees in a &lt;em&gt;programmable storage&lt;/em&gt; system. After giving an informal overview of data consistency in distributed systems, I’ll discuss what programmable storage is and how a programmable storage system might support a mixture of consistency levels.&lt;/p&gt;

&lt;h2 id=&quot;data-consistency&quot;&gt;Data consistency&lt;/h2&gt;

&lt;p&gt;In a distributed system where data is copied between multiple servers, keeping the copies consistent with each other may not always be possible.
For instance, consider a small system where each &lt;em&gt;data object&lt;/em&gt; has two copies (Copy&lt;sub&gt;1&lt;/sub&gt; and Copy&lt;sub&gt;2&lt;/sub&gt;), distributed over two servers. What if you’re accessing Copy&lt;sub&gt;1&lt;/sub&gt; and it is somehow different from Copy&lt;sub&gt;2&lt;/sub&gt;?&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;consistency model&lt;/em&gt; defines the ways in which Copy&lt;sub&gt;1&lt;/sub&gt; and Copy&lt;sub&gt;2&lt;/sub&gt; may disagree. Informally, &lt;em&gt;strong consistency&lt;/em&gt; means that all clients agree on the order that operations on a data object appear.  Under &lt;em&gt;eventual consistency&lt;/em&gt;, on the other hand, copies of a data object may appear different at any point, but given enough time without updates, all copies will converge to the same state.  In a hybrid consistency model, such as &lt;a href=&quot;https://www.usenix.org/system/files/conference/osdi12/osdi12-final-162.pdf&quot;&gt;RedBlue consistency&lt;/a&gt;, individual operations on data objects may be strongly consistent (red) or eventually consistent (blue). Red operations must be ordered with respect to each other, while blue operations may be in any order (and must commute).&lt;/p&gt;

&lt;p&gt;In some contexts, such as in the &lt;a href=&quot;http://www.cs.cornell.edu/andru/papers/mixt/mixt.pdf&quot;&gt;MixT&lt;/a&gt; and &lt;a href=&quot;https://homes.cs.washington.edu/~luisceze/publications/ipa-socc16.pdf&quot;&gt;IPA&lt;/a&gt; programming models, consistency is considered a property of the &lt;em&gt;data&lt;/em&gt; being operated on, rather than a property of the operations themselves. Since ADTs are defined by the operations that can be invoke on them, though, these two points of view are necessarily difficult to disentangle.&lt;/p&gt;

&lt;h2 id=&quot;programmable-storage&quot;&gt;Programmable storage&lt;/h2&gt;

&lt;p&gt;Across all fields of computing, data storage is extremely important. In fact, even abstract 
models of computation require the concept of &lt;em&gt;tape&lt;/em&gt;, as an infinite, contiguous sequence of locations that can store symbols. It likely isn’t surprising that significant improvements in storage devices have huge impacts for many areas of computing. However, hardware improvements have not always come fast enough. As application requirements for storage have grown, storage systems have grown more complex. To accommodate web-scale and high-performance applications, storage systems have become distributed, and spanned many storage devices.&lt;/p&gt;

&lt;p&gt;The performance of a storage system has significant impact on the design and implementation of applications that communicate with it – consider HDFS and cloud services such as Amazon’s S3. In the case of HDFS, knowledge of how it partitions files or caches data can help the application programmer make choices to reduce latency or increase throughput. And so, as applications increase in both complexity and concurrency, there is an increasing need to extract better performance from the storage system.  In addition to growing application needs, there are also improvements in hardware that storage systems have yet to make use of. Due to reliability needs, storage systems must be well tested and extensively exercised.  On the other hand, due to increasing application requirements and the rate at which underlying hardware is improving, it is necessary to iterate quickly, or to periodically re-design various subsystems. Finally, there are a variety of storage devices to tune for, and that can significantly affect software system design and implementation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Programmable storage&lt;/em&gt; is an approach to developing storage interfaces, pioneered by storage systems researchers at UC Santa Cruz, that emphasizes programmability and reusability of storage subsystems to address these challenges.  Due to the inherently high reliability expectations of storage systems, the programmable storage approach discourages rewriting storage subsystems or components, because this only invites younger, error-prone code. The intuition is that reusing subsystems of a storage system means that the community supporting these subsystems is larger, and these subsystems are exercised and improved more frequently. The &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=3064208&quot;&gt;Malacology&lt;/a&gt; programmable storage system is an interface built on top of the &lt;a href=&quot;https://ceph.com/&quot;&gt;Ceph&lt;/a&gt; storage stack.  Malacology abstracts storage subsystems into building blocks that can be combined to more easily build a service on top of the storage system.&lt;/p&gt;

&lt;p&gt;Ceph is an open source, distributed, large-scale storage system that aims to be “&lt;a href=&quot;https://ceph.com/geen-categorie/ceph-storage-introduction/&quot;&gt;completely
distributed without a single point of failure, scalable to the exabyte level, and freely-available&lt;/a&gt;.” Ceph has been part of storage systems research at UC Santa
Cruz for over a decade, from &lt;a href=&quot;https://www.ssrc.ucsc.edu/Papers/weil-osdi06.pdf&quot;&gt;the original Ceph paper&lt;/a&gt; (2006), the &lt;a href=&quot;https://ceph.com/wp-content/uploads/2016/08/weil-crush-sc06.pdf&quot;&gt;CRUSH algorithm&lt;/a&gt; (2006) and the &lt;a href=&quot;https://ceph.com/wp-content/uploads/2016/08/weil-rados-pdsw07.pdf&quot;&gt;RADOS&lt;/a&gt; data store (2007), to &lt;a href=&quot;https://cloudfront.escholarship.org/dist/prd/content/qt72n6c5kq/qt72n6c5kq.pdf?t=pcfodf&quot;&gt;Noah Watkins’s recent dissertation&lt;/a&gt; on programmable storage built on top of Ceph.&lt;/p&gt;

&lt;p&gt;Although Ceph is a distributed, large-scale storage system, it was designed to fill the role of
reliable, durable storage. This expectation is common (and preferred) for many applications,
especially scientific applications, where the complexity of weaker consistency models is too
difficult to work with. This makes Ceph’s support for only strong consistency, via
&lt;a href=&quot;http://docs.ceph.com/docs/cuttlefish/architecture/#cluster-side-replication&quot;&gt;primary-copy&lt;/a&gt; replication, reasonable. However, the trade-off between strong consistency and availability or performance is very important for some &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=1294281&quot;&gt;Dynamo-like&lt;/a&gt; applications. For Ceph to support these types of applications, it would need to offer weaker consistency as an option.  Recent work on a weak consistency model for Ceph, &lt;a href=&quot;http://www.cs.nthu.edu.tw/~ychung/conference/ICPADS-2016.pdf&quot;&gt;PROAR&lt;/a&gt;, has been published by researchers at the Graduate School at Shenzhen, Tsinghua University.&lt;/p&gt;

&lt;p&gt;Further building up our motivating example, we would like to consider extensions to &lt;a href=&quot;https://nwat.xyz/blog/2014/10/26/zlog-a-distributed-shared-log-on-ceph/&quot;&gt;ZLog&lt;/a&gt;, an distributed shared log developed on top of Malacology. ZLog is an implementation of the &lt;a href=&quot;https://www.usenix.org/conference/nsdi12/technical-sessions/presentation/balakrishnan&quot;&gt;CORFU&lt;/a&gt; strongly-consistent shared log. If Ceph (and Malacology on top of it) supported multiple consistency levels, then ZLog could as well.  It would be interesting to compare FuzzyLog to a mixed-consistency version of ZLog built on Malacology. Using an approach in the spirit of QUELEA, MixT, or IPA for mixing consistencies would align well with the programmability aspect of programmable storage.&lt;/p&gt;

&lt;h2 id=&quot;mixing-consistency&quot;&gt;Mixing consistency&lt;/h2&gt;

&lt;p&gt;For developers working in distributed systems, it can be cumbersome to think about whether the correct consistency guarantees are being satisfied, especially when building on storage systems that support a mixture of consistency levels. In the past few years, there have been a variety of language-level abstractions that support reasoning about mixtures of consistency guarantees.&lt;/p&gt;

&lt;h3 id=&quot;inconsistent-performance-bound-approximate-ipa-programming&quot;&gt;Inconsistent, Performance-bound, Approximate (IPA) programming&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://homes.cs.washington.edu/~luisceze/publications/ipa-socc16.pdf&quot;&gt;IPA&lt;/a&gt; provides a &lt;em&gt;consistency type system&lt;/em&gt; that makes consistency models explicit in types.  Developers are able to verify that important data types are used at an appropriate consistency level. This type of support from the programming model is useful for developers to be both more efficient and more correct. IPA is motivated by taking a principled approach to the trade-off between consistency and performance.&lt;/p&gt;

&lt;p&gt;There is &lt;a href=&quot;https://github.com/bholt/ipa/tree/master/src/main/scala/ipa&quot;&gt;a prototype implementation of IPA&lt;/a&gt; in Scala on top of Cassandra. Scala’s
powerful type system allows for ergonomically deconstructing consistency types. This
approach allows the developer to directly interact with the consistency type of their data, using
features such as pattern matching. IPA allows consistency guarantees to be specified as a policy on an ADT in two ways:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;em&gt;Static consistency policies&lt;/em&gt; allow specification of a consistency model (e.g., strong, weak, causal) that can be enforced by the data store.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Dynamic consistency policies&lt;/em&gt; allow specification of performance or correctness bounds within which to achieve the strongest consistency possible, and which require additional runtime support to enforce.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Static consistency policies can be implemented relatively straightforwardly on top of the data store. For this particular implementation, consistency levels for data store operations are determined via Cassandra’s use of quorum reads and writes.&lt;/p&gt;

&lt;p&gt;Dynamic consistency policies are specifications of performance or behavior properties, within which the strongest consistency constraints should be satisfied. More concretely, IPA provides two dynamic consistency types: rushed types and interval types. The dynamic consistency types are out of scope for this blog post, but they are a very interesting aspect of the IPA paper.&lt;/p&gt;

&lt;h3 id=&quot;mixt-consistency-enforced-with-information-flow&quot;&gt;MixT: consistency enforced with information flow&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://www.cs.cornell.edu/andru/papers/mixt/mixt.pdf&quot;&gt;MixT&lt;/a&gt; is a domain-specific language that aims to keep consistent computations “untainted” by inconsistent computations. Because the use of inconsistent data can weaken computations that are expected to be strongly consistent, MixT takes an information flow approach to preventing unsafe, or unexpected, interactions between computations running at different consistency levels.  Furthermore, MixT offers support for &lt;em&gt;mixed-consistency transactions&lt;/em&gt; that execute in separate phases depending on the consistency level of the operations therein.&lt;/p&gt;

&lt;h3 id=&quot;quelea-declarative-programming-over-eventually-consistent-data-stores&quot;&gt;QUELEA: declarative programming over eventually consistent data stores&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://kcsrk.info/papers/quelea_pldi15.pdf&quot;&gt;QUELEA&lt;/a&gt; takes a declarative programming approach that allows developers to specify constraints on a data store and on the operations that interact with it. Programmers can annotate operations with &lt;em&gt;contracts&lt;/em&gt; that enforce application-level invariants, such as preventing a negative bank account balance. An SMT-based &lt;em&gt;contract classification&lt;/em&gt; system analyzes these contracts and automatically determines the minimum consistency level at which an operation can be run, simplifying reasoning about consistency from the developer’s perspective. Further, QUELEA supports transactional contracts even when the backend datastore does not.&lt;/p&gt;

&lt;h2 id=&quot;mixing-consistency-in-programmable-storage&quot;&gt;Mixing consistency in programmable storage&lt;/h2&gt;

&lt;p&gt;The typical IO path to Ceph’s storage cluster does not support consistency models other than strong consistency. To
support weaker consistency models in our programmable storage system, we would need to build support for a range of consistency models directly in Ceph.  That, in turn, would motivate the need for a language-level abstraction to help programmers deal with the resulting “consistency zoo”.&lt;/p&gt;

&lt;p&gt;To support an IPA-like system on top of Ceph with as little modification as possible, it would be useful to add a quorum interface to Ceph. Ceph’s RADOS datastore uses OSDs (object storage daemons) for data persistence. By communicating with these OSDs directly, rather than through a RADOS gateway (RGW), it may be possible to provide a quorum interface in Ceph. Understanding the details of OSD communication will be important for understanding whether Ceph can provide a similar interface to IPA as what Cassandra provides.&lt;/p&gt;

&lt;p&gt;MixT, being built on top of Postgres, describes an alternate possible mechanism for supporting
weak consistency in a programmable storage system. To enable causal consistency on top of Postgres, Milano et al. replicated data over several Postgres servers. Clients were then partitioned to separate servers and each server was configured to use snapshot isolation for transactions. Version numbers for each row allowed operation ordering across servers, and vector clocks used microsecond-resolution wall clock time. If we encode versions and vector clocks in data stored in Ceph OSDs, it may be possible to simply treat each OSD as a replica server as MixT does with Postgres, hence enabling causal consistency in Ceph.&lt;/p&gt;

&lt;p&gt;The implementation of QUELEA, like IPA, uses Cassandra as the backend data store.  It uses a &lt;a href=&quot;http://www.bailis.org/papers/bolton-sigmod2013.pdf&quot;&gt;“bolt-on”&lt;/a&gt;-style mechanism implemented in a shim layer on top of the data store to enable causal consistency on top of Cassandra. Because QUELEA requires programmers to specify contracts on operations that interact with the store and ensures that store operations happen at a consistency level that satisfies those contracts, it seems that implementing QUELEA on top of Ceph may benefit from the ability to communicate with Ceph OSDs individually, just like IPA.&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;

&lt;p&gt;In a follow-up blog post, I will try to explore mechanisms by which Ceph could support a range of consistency levels. The approaches to investigate will include quorum consistency, bolt-on causal consistency, and MixT’s approach to causal consistency. Once we understand how these mechanisms might fit into Ceph, we can investigate various approaches to mixing consistency.&lt;/p&gt;

&lt;!-- intro links --&gt;

&lt;!-- DPS links --&gt;

&lt;!-- programmable storage links --&gt;

&lt;!-- consistency-types links --&gt;</content><author><name>Aldrin Montana</name></author><summary type="html">by Aldrin Montana · edited by Abhishek Singh and Lindsey Kuper</summary></entry><entry><title type="html">Conflict resolution in collaborative text editing with operational transformation (Part 1 of 2)</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/11/20/conflict-resolution-in-collaborative-text-editing-with-operational-transformation-part-1-of-2.html" rel="alternate" type="text/html" title="Conflict resolution in collaborative text editing with operational transformation (Part 1 of 2)" /><published>2018-11-20T00:00:00+00:00</published><updated>2018-11-20T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/11/20/conflict-resolution-in-collaborative-text-editing-with-operational-transformation-part-1-of-2</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/11/20/conflict-resolution-in-collaborative-text-editing-with-operational-transformation-part-1-of-2.html">&lt;p&gt;by Abhishek Singh · edited by Austen Barker and Lindsey Kuper&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Anyone who has used a version control system or collaboratively edited a document knows all too well the problems that arise when versions of a document conflict.  Suppose Alice and Bob decide to collaborate on a document. Alice creates a new document, shares it with Bob, and starts editing. Meanwhile,  Bob edits his copy. If Alice and Bob’s respective copies of the document magically synchronized with each other with zero latency, collaborative text editing would be easy.  But if there is any latency between Alice and Bob — perhaps due to a &lt;a href=&quot;https://en.wikipedia.org/wiki/Network_partition&quot;&gt;network partition&lt;/a&gt; — then their copies of the document may diverge, leading to a conflict that must eventually be resolved.&lt;/p&gt;

&lt;p&gt;If Alice and Bob had been writing on paper, they would have to manually create a unified document incorporating both their changes.  &lt;a href=&quot;https://en.wikipedia.org/wiki/Collaborative_software&quot;&gt;Collaborative software&lt;/a&gt; — encompassing both real-time collaborative editing tools such as Google Docs, and version control systems such as Git, Mercurial, and Subversion —  aims to automate as much of this conflict resolution process as possible.&lt;/p&gt;

&lt;h2 id=&quot;operational-transformation&quot;&gt;Operational Transformation&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Operational_transformation&quot;&gt;Operational transformation&lt;/a&gt; is an algorithm first discussed in a 1989 paper called &lt;a href=&quot;http://doi.acm.org/10.1145/67544.66963&quot;&gt;“Concurrency control in groupware systems” by Ellis and Gibbs&lt;/a&gt; and intended to allow systems to collaboratively perform a common task. The technique allowed users keep track of operations performed on shared data as a means of keeping track of changes in the data. Additionally, it formalized certain properties functions must have to allow tasks to be performed collaboratively. A more thorough survey of operational transformation was published by &lt;a href=&quot;http://dx.doi.org/10.1145/289444.289469&quot;&gt;Sun and Ellis&lt;/a&gt; in 1998. The technology grew beyond Ellis and Gibb’s original definition and now describes a host of architectures, data models and algorithms for building collaborative software systems. For the rest of this post, we will restrict ourselves to operational transformation as described in the original paper, and its use in collaborative text editing.&lt;/p&gt;

&lt;p&gt;Operational transformation was popularized by Google in its &lt;a href=&quot;http://web.archive.org/web/20090923095705/http://www.waveprotocol.org/whitepapers/operational-transform&quot;&gt;Google Wave project&lt;/a&gt;.  Since Google Wave was discontinued, some of the original papers and definitions can be hard to find, but some documents are available via the &lt;a href=&quot;https://web.archive.org/web/20111126052203/http://wave-protocol.googlecode.com/hg/whitepapers/operational-transform/operational-transform.html&quot;&gt;Wayback Machine.&lt;/a&gt; Operational transformation has also made it into Google’s &lt;a href=&quot;https://developers.google.com/realtime/conflict-resolution&quot;&gt;other products&lt;/a&gt;,  such as &lt;a href=&quot;https://drive.googleblog.com/2010/09/whats-different-about-new-google-docs_22.html&quot;&gt;Google Drive and Google Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Google Wave project itself was based on the &lt;a href=&quot;https://dl.acm.org/citation.cfm?doid=215585.215706&quot;&gt;Jupiter collaboration system&lt;/a&gt;. Jupiter was aimed towards simplifying the algorithm created by Ellis and Gibbs by creating a centralized architecture, as opposed to the free-range collaborative system that Ellis and Gibbs drew out in their paper. In our discussion we deal with the decentralized idea as presented in &lt;a href=&quot;http://doi.acm.org/10.1145/67544.66963&quot;&gt;Ellis and Gibbs’ original 1989 paper&lt;/a&gt;. To be specific, we look at the Distributed Operational Transformation (dOPT) algorithm and its use in Ellis and Gibbs’ GROVE editor.&lt;/p&gt;

&lt;p&gt;To describe the problem in in its most basic form, let’s say we have a document which is being edited by two users, &lt;strong&gt;Alice&lt;/strong&gt; and &lt;strong&gt;Bob&lt;/strong&gt;. Alice creates a local copy of the document with the string &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abcd&lt;/code&gt; and shares it with Bob. She then starts editing the document, and these changes are shared with Bob as shown in the figure below.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/test_operations_inconsistent.png&quot; height=&quot;600&quot; width=&quot;450&quot; /&gt;
  &lt;figcaption&gt;Figure 1. Operations received by both Alice and Bob are applied to local data as they are received. This leads to data inconsistencies. (Data index starts from 0.)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In Figure 1, the red arrows denote operations performed by Alice on her local copy of the data and then their subsequent transmission to Bob for synchronizing their data. The purple arrows show operations executed by Bob on his copy of the data and then transmitted to Alice.  For example, Alice inserts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; at index 0 in the string with the operation &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;y&quot;, 0)&lt;/code&gt;, and Bob inserts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; at index 2 with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT(&quot;x&quot;, 2)&lt;/code&gt;, then deletes the character at index 1 with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE(1)&lt;/code&gt;.  Any changes that either Alice or Bob make to their copy of the document is sent over to the other as an operation message.&lt;/p&gt;

&lt;p&gt;The problem is that neither user applies the operation to their local data with any consideration of how the other user applied the operation at their end. Clearly, the data on which Alice executes an operation is not the same as the data on which Bob applied the same operation. The problem is that indices used by Alice do not always correspond to the indices used by Bob. This leads to data inconsistencies at both ends when operations are executed by either of them.&lt;/p&gt;

&lt;p&gt;A mechanism is clearly needed that will allow Alice to correctly apply the the operations that Bob did on his data, and vice-versa. The dOPT algorithm specifies the properties of a transformation function which could help us transform the operation and indices received from one user and apply it safely on the other user. Next, we will discuss a rudimentary implementation of such a transformation.&lt;/p&gt;

&lt;h2 id=&quot;an-example-of-collaborative-editing-using-distributed-operational-transformation-dopt&quot;&gt;An example of collaborative editing using distributed operational transformation (dOPT)&lt;/h2&gt;

&lt;p&gt;For this blog post, I wrote &lt;a href=&quot;https://bitbucket.org/alfredd/collabalgos&quot;&gt;a simple Go program and a set of test cases which showcase dOPT&lt;/a&gt;.  The implementation follows the algorithm roughly as stated in the &lt;a href=&quot;http://doi.acm.org/10.1145/67544.66963&quot;&gt;1989 paper by Ellis and Gibbs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To see what the code does, let’s begin by walking through the example shown in the figure below. Again, Alice and Bob have a shared document that both start editing. At the end of each edit, the edit operation performed is sent to the other. When a message is received, the indices are recomputed based on some criteria and the correct operation is executed.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/CMPS290S-2018-09/blog-assets/test_operations.png&quot; height=&quot;600&quot; width=&quot;450&quot; /&gt;
  &lt;figcaption&gt;Figure 2. Operations received by Alice and Bob are transformed before being applied to local data.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;As seen from Figure 2, the following operations are performed by Alice and Bob:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Initially, the data is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abcd&lt;/code&gt;, inserted by Alice and sent over to Bob.  Alice and Bob have the same data and are in a consistent state.&lt;/li&gt;
  &lt;li&gt;Alice inserts a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; at index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; on her copy of the data that now becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yabcd&lt;/code&gt;. This operation is then sent to Bob as well.&lt;/li&gt;
  &lt;li&gt;While Alice was performing the insert in step 2, Bob inserts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; at index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt; to his copy, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abcd&lt;/code&gt; as of this moment.  This happens concurrently with the operation in step 2. The insert modifies Bob’s local copy to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abxcd&lt;/code&gt;.  Bob then sends this operation to Alice.&lt;/li&gt;
  &lt;li&gt;Bob then receives Alice’s operation from step 2, correctly computes the index of the operation on his copy of the data, and executes the operation on his copy, which is modified to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yabxcd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Bob’s insert operation from step 3 is received by Alice at some point and Alice correctly computes the index for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; to be inserted on her local data. Her local data is now &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yabxcd&lt;/code&gt; — the same as Bob’s.&lt;/li&gt;
  &lt;li&gt;If this were the end of the editing process, Alice and Bob would both find that their local copies are now consistent. But they don’t stop. They continue editing their copies, and every edit and subsequent transmission to the other keeps their local data consistent with the others.&lt;/li&gt;
  &lt;li&gt;Bob deletes the character at index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; from his copy of the data, which was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yabxcd&lt;/code&gt;, modifying it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ybxcd&lt;/code&gt; and sending this information to Alice.&lt;/li&gt;
  &lt;li&gt;When Alice receives the delete operation from Bob, she correctly computes the indices and modifies her copy to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ybxcd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;While Alice is executing the operation in step 8, Bob deletes a character at index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt; of his local data, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ybxcd&lt;/code&gt;, changing it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ybxd&lt;/code&gt; and sending this operation over to Alice.&lt;/li&gt;
  &lt;li&gt;At some point after Alice executes step 8 but before receiving the message from Bob from step 9, Alice inserts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt; at index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; to her local copy, modifying it from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ybxcd&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yfbxcd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The operation from step 9 is received by Alice, who deletes the character from her index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;, with the data finally becoming &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yfbxd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Meanwhile, Bob receives Alice’s insert from step 10 and updates his copy to now read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yfbxd&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are some things that should be noted from both Figure 2 and its explanation above. First, steps 2 and 3 in the above discussion are concurrent. There is no way to know when exactly steps 2 or 3 were executed by either parties. We can however state unequivocally that step 2 &lt;em&gt;happened before&lt;/em&gt;  step 5 at Alice’s end (and for Bob, step 3 &lt;em&gt;happens before&lt;/em&gt; step 4). Thus, at their own ends, operations are executed sequentially, whereas some operations are concurrent when we look at the system as a whole.&lt;/p&gt;

&lt;p&gt;Based on Figure 2, we create a set of test cases that will be the basis of further discussion of the process. In my implementation, I make a few assumptions that differ from the dOPT algorithm described in Ellis and Gibbs’ paper:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Operation messages from either sites are received exactly once.&lt;/li&gt;
  &lt;li&gt;There are exactly two editors in the system: one at Alice’s end and the other at Bob’s end.&lt;/li&gt;
  &lt;li&gt;My implementation does not use clocks to timestamp operations, so the &lt;em&gt;happens before&lt;/em&gt; relationship is established based on message delivery. It is assumed that LOCAL and REMOTE operations happen concurrently.&lt;/li&gt;
  &lt;li&gt;Operations are processed in the order in which they are seen and executed at a particular site. In our implementation the executed operations are stored in a list &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor.Ops&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Unlike the implementation in the &lt;a href=&quot;http://doi.acm.org/10.1145/67544.66963&quot;&gt;paper&lt;/a&gt;, we do not assign priorities to an operation. Every operation has equal priority.&lt;/li&gt;
  &lt;li&gt;An operation is sent to others immediately after it was executed at one particular site. There is no out-of-order delivery of messages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the main issues in this implementation is that we overlook establishing causality between operations. There is an implicit &lt;em&gt;happens before&lt;/em&gt; relationship established by the order in which operations are stored in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor.Ops&lt;/code&gt; list. This can easily be broken by packets that arrive out of order, leading to data inconsistency. Suppose Bob performs two operations &lt;em&gt;Op1&lt;/em&gt; and &lt;em&gt;Op2&lt;/em&gt;, and &lt;em&gt;Op2&lt;/em&gt; reaches Alice before &lt;em&gt;Op1&lt;/em&gt;; this would cause data inconsistency in my implementation of dOPT, since the transformation depends on the previous operation to compute the new indices.&lt;/p&gt;

&lt;p&gt;The code below shows test cases based on the operations performed in Figure 2. The comments in the program identify the operations and the generator of the operations.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestOTEditor_Transformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OTEditor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;yabcd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ops&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;// OPERATION #1 : ALICE&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;abcd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOCAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;// OPERATION #2 : ALICE&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOCAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// OPERATION #3 : BOB&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Operation 3. BOB (remote) insert 'x' at index 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppendOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;REMOTE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;yabxcd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// OPERATION #4 : BOB&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Operation 4. BOB (remote) delete char at index 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppendOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DELETE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;REMOTE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ybxcd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// OPERATION #5 : ALICE&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Operation 5. ALICE (local) insert 'f' at index 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppendOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOCAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;yfbxcd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// OPERATION #6 : BOB&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Operation 6. BOB (remote) delete char at index 3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppendOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DELETE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;REMOTE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;yfbxd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ordering of the operations is implicit in the test cases. Operations are executed from the perspective of Alice in Figure 2, and are processed in the order in which they are seen by Alice.&lt;/p&gt;

&lt;p&gt;In the code, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor&lt;/code&gt; is represented by a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data&lt;/code&gt; string and a list of operations, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ops&lt;/code&gt;, that have taken place on it.  There are two supported operations: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt;.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REMOTE&lt;/code&gt; are constants distinguishing two operation locations; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; operations should be thought of as being performed on a local copy of the data, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REMOTE&lt;/code&gt; operations should be thought of as being performed by another user (Bob in this case) on their own copy of the data and sent over as part of the synchronization process. At the end of each operation execution, data at both Alice and Bob’s end is synchronized and is in a consistent state.&lt;/p&gt;

&lt;p&gt;Running the tests produces the following output, including log messages produced by calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppendOperation&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BOB&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'x'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Existing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yabcd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abcd&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;New&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Executing&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yabcd&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yabxcd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abcd&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BOB&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Existing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yabxcd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abcd&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;New&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;''&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Executing&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;''&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yabxcd&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ybxcd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abcd&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;''&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ALICE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'f'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Existing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ybxcd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abcd&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;New&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Executing&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ybxcd&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yfbxcd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abcd&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BOB&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Existing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yfbxcd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abcd&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;New&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;''&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Executing&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;''&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yfbxcd&lt;/span&gt;
&lt;span class=&quot;m&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;46&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yfbxd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abcd&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;''&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;''&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the &lt;a href=&quot;http://doi.acm.org/10.1145/67544.66963&quot;&gt;paper&lt;/a&gt;, a necessary (but not sufficient) condition to ensure that the transformation function works is commutativity of the operations. When dOPT is applied commutativity of operations must be ensured. Additionally, we need to understand if transformation is required. Transformation in the paper is a technique to resolve conflicts. If there are no conflicts, we do not apply any transformations.&lt;/p&gt;

&lt;p&gt;First, we need to understand when a transformation &lt;em&gt;is not&lt;/em&gt; required. In the above test case, we have three cases where transformation is not required.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;During the execution of operation #2 where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; (or Alice) inserts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt;. These are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; operations. Data consistency is ensured because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; editor is &lt;a href=&quot;https://en.wikipedia.org/wiki/Consistency_model#Read-your-writes_Consistency&quot;&gt;“&lt;em&gt;reading your writes&lt;/em&gt;”&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Operation #4 does not require any transformation because the last operation (#3) was also a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REMOTE&lt;/code&gt; edit. Since there were no other edits performed anywhere between operation #3 and #4, the data is consistent at both Alice and Bob’s end before the execution of operation 4 (check Figure 2).&lt;/li&gt;
  &lt;li&gt;Operation #5 does not required transformation because execution of #4 synchronized edits at both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REMOTE&lt;/code&gt; ends.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In all of the three cases, there are no conflicts where a transformation function is needed. The only case where transformation is required is when the last operation was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; and the current operation is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REMOTE&lt;/code&gt;. This is because the remote user (Bob) may not be aware of the last operation performed by the local user (Alice) when he made the edit. This rule must be taken with a pinch of salt as it is specific only to this implementation as there exist only two editors in the system.&lt;/p&gt;

&lt;p&gt;Next, we discuss what commutativity is and how it applies to our test cases. Given two operations &lt;em&gt;A&lt;/em&gt; and &lt;em&gt;B&lt;/em&gt;, the transformation &lt;strong&gt;T&lt;/strong&gt; generates the following transformations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;B’&lt;/em&gt; := &lt;strong&gt;T&lt;/strong&gt; (&lt;em&gt;B&lt;/em&gt;, &lt;em&gt;A&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;A’&lt;/em&gt; := &lt;strong&gt;T&lt;/strong&gt; (&lt;em&gt;A&lt;/em&gt;, &lt;em&gt;B&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The transformation &lt;strong&gt;T&lt;/strong&gt; is implemented such that &lt;strong&gt;&lt;em&gt;B&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; &lt;em&gt;A’&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; &lt;em&gt;A&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; &lt;em&gt;B’&lt;/em&gt;&lt;/strong&gt;. Here the symbol &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; in the expression is used to refer to order of operation execution. For example, for operations &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A x B&lt;/code&gt; means &lt;em&gt;apply operation A, then B&lt;/em&gt;.
The symbol &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LHS = RHS&lt;/code&gt; refers to the fact that data after application of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RHS&lt;/code&gt; operations equals &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LHS&lt;/code&gt;. 
This transformation works in the test case shown above. Let’s first consider operation #3:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;A&lt;/em&gt; := &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;y&quot;, 0 )&lt;/code&gt; ; operation #2.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;A’&lt;/em&gt; := &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;y&quot;, 0 )&lt;/code&gt; ;  The transformation is the same &lt;em&gt;A&lt;/em&gt;, as operation #1 was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;B&lt;/em&gt; := &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;x&quot;, 2 )&lt;/code&gt; ; operation #3.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;B’&lt;/em&gt; := &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;x&quot;, 3 )&lt;/code&gt; ; the operation after the transformation.&lt;/li&gt;
  &lt;li&gt;Data at both ‘remote’ and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; systems before operations &lt;em&gt;A&lt;/em&gt; and &lt;em&gt;B&lt;/em&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abcd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;B&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; &lt;em&gt;A’&lt;/em&gt;&lt;/strong&gt; yields data &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abxcd&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yabxcd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;A&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; &lt;em&gt;B’&lt;/em&gt;&lt;/strong&gt; yields data &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yabcd&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yabxcd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Both sequences of operations lead to the same state after test case #1, so commutativity holds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Updates by operations #4 and #5 are all conflict-free and do not require transformation. Operation #6 does. To prove commutativity for operation #5 and #6:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;A&lt;/em&gt; := &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;f&quot;, 1 )&lt;/code&gt; ; operation #5.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;A’&lt;/em&gt; := &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (&quot;f&quot;, 1 )&lt;/code&gt; ; operation #4 did not violate consistency , no transformation required.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;B&lt;/em&gt; := &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE ( 3 )&lt;/code&gt;, operation #6.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;B’&lt;/em&gt; := &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE ( 4 )&lt;/code&gt;, operation after the transformation.&lt;/li&gt;
  &lt;li&gt;Data at both ‘remote’ and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; systems before operations &lt;em&gt;A&lt;/em&gt; and &lt;em&gt;B&lt;/em&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ybxcd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;B&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; &lt;em&gt;A’&lt;/em&gt;&lt;/strong&gt; yields data &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ybxd&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yfbxd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;A&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; &lt;em&gt;B’&lt;/em&gt;&lt;/strong&gt; yields data &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yfbxcd&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yfbxd&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Again, both sequences of operations lead to the same state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The case of concurrent local and remote updates is the only case where conflict resolution is needed, and therefore the cases discussed above ensure that the transformations produced are always commutative.&lt;/p&gt;

&lt;h3 id=&quot;the-index-recomputation-algorithm&quot;&gt;The index recomputation algorithm&lt;/h3&gt;

&lt;p&gt;As mentioned in the previous section and discussed in the test case above, indexes are recomputed when operation messages are received by either Alice or Bob. We now discuss how the index recomputation is performed. The index recomputation follows the following rules:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Deletion is performed  &lt;em&gt;one-character-at-a-time&lt;/em&gt; in this implementation.&lt;/li&gt;
  &lt;li&gt;We maintain a list containing the previously executed operations. This list maintains the order of the sequence of operations that have been executed.&lt;/li&gt;
  &lt;li&gt;As discussed previously, transformation is required only when the locations of the current and last operation differs: last operation was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; and current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REMOTE&lt;/code&gt;, or vice-versa.&lt;/li&gt;
  &lt;li&gt;When transformation is required, we check the index of current and last operations, and the operation types. Operation types in our implementation can be either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currentOperation.Index &amp;gt; lastOperation.Index&lt;/code&gt; AND &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lastOperation.Type == DELETE&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currentOperation.Index = currentOperation.Index - 1&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currentOperation.Index &amp;gt; lastOperation.Index&lt;/code&gt; AND &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lastOperation.Type == INSERT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currentOperation.Index = currentOperation.Index + length (lastOperation.Data)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Indices are recomputed in steps 4 and 5. In both these steps, indices are recomputed only if the index of the current operation is greater than that of the last operation. This is done because only in this case can the current operation alter the consistency of the data.&lt;/p&gt;

&lt;p&gt;Suppose the data before the “last operation” executed was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abcd&lt;/code&gt; and the “last operation” was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT (“x”, 3)&lt;/code&gt;. Data after the execution of the “last operation” is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abcxd&lt;/code&gt;. Now suppose the “current operation” had an index of 2.  Regardless of what exactly the operation is, it is clear that it can be applied without any need for index recomputation, as there is no offset in the index of the “current operation”, whatever that operation might be.&lt;/p&gt;

&lt;p&gt;Let us now suppose that (beginning with the same initial data &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abcd&lt;/code&gt;) the “last operation” was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE(2)&lt;/code&gt;. After the execution of the “last operation”, the data is modified to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;abd&lt;/code&gt;. If the “current operation” has an index of 3, it can be seen in this example that there is no index 3 in the data after the execution of the “last operation”.  In this case, there is a need to offset the index of the “current operation” before it can be applied. To offset the index, we depend on the type of the current operation. If the “last operation” was an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt;, the “current operation”’s index must be offset by the length of the data, which was inserted by adding the index of the “current operation” with the length of the data in the “last operation”. If the “last operation” was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt;, the current index is offset by 1 (as we only allow deletion one character at a time).&lt;/p&gt;

&lt;h2 id=&quot;editor-implementation&quot;&gt;Editor implementation&lt;/h2&gt;

&lt;p&gt;Now that expectations are set, we implement a simplified operational transformation editor. We continue borrowing language from our previous discussion based on Figure 2. The first part of the implementation deals with setting up data structures. We have two main operations: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt;. These operations can be done either locally (by the same process where the program is executing) or could be sent as a synchronization message from a another process ( or remote user). Operations are tagged &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOCAL&lt;/code&gt; if they are performed by the process executing the program (Alice) and tagged &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REMOTE&lt;/code&gt; if they are sent from another process (Bob). The operations are described by the struct &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Op&lt;/code&gt;, which defines the structure of the operations which the editor (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor&lt;/code&gt;) can process. The editor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor&lt;/code&gt; itself has two fields: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data&lt;/code&gt;, which stores application data, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ops&lt;/code&gt;, which keeps a record of operations that have been processed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpLocation&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOCAL&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;OpLocation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;REMOTE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpLocation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;OpLocation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OTEditor&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Ops&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor&lt;/code&gt; has several methods that implement Operational Transformation. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppendOperation&lt;/code&gt; method is called whenever a new operation is performed by the user (either remote or local). All operations are added to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor.ops&lt;/code&gt; slice (think of it as an list). The data is stored in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor.Data&lt;/code&gt; field. When &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppendOperation&lt;/code&gt; is called, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor&lt;/code&gt; performs a transformation as previously discussed to compute the new value of the index where the data has to be inserted or deleted. This transformation is performed in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;performTransformation&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppendOperation&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; methods are straightforward, and we will not spend too much time on them except to note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; modifies the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OTEditor.Data&lt;/code&gt; field according to the operation that needs to be performed. Before modifying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data&lt;/code&gt; field,  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;performTransformation&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OTEditor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppendOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opLocation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Existing Data: %v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;New Operation received: %v&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OTEditor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Executing new operation: %v, current data: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// Recompute indices for the operation&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;performTransformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INSERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cannot perform operation %v, index out of bounds.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;b1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;b2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;b3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DELETE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cannot perform operation %v, index out of bounds.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;b1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;b3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;finalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ops&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ops&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ops&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Current value of data: %v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;performTransformation&lt;/code&gt; method  implements the heart of the application. Combined with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; method, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;performTransformation&lt;/code&gt; implements the algorithm for recomputing indices and ensuring that operations remain commutative.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OTEditor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;performTransformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;lastOp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// Transformation required only when synchronizing user changes.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastOp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOCAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastOp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastOp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastOp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastOp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lastOp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have implemented a very simple collaborative text editor. As we discussed before, one factor that we overlook in this implementation is that of establishing causality between a set of operations. The implementation, therefore, relies on the order on which the operations are received to establish consistency in the system. We will explore how these issues affect systems and how these can be resolved in my next blog post.&lt;/p&gt;

&lt;h2 id=&quot;whats-in-part-2&quot;&gt;What’s in Part 2?&lt;/h2&gt;

&lt;p&gt;In my next post, we will review the assumptions we made in this post and see how those assumptions can be removed. We will then develop a more generalized technique for distributed operational transformation and see how we can build a better collaborative editor.&lt;/p&gt;</content><author><name>Abhishek Singh</name></author><summary type="html">by Abhishek Singh · edited by Austen Barker and Lindsey Kuper</summary></entry><entry><title type="html">Manufacturing Consensus: An Overview of Distributed Consensus Implementations</title><link href="https://decomposition.al/CMPS290S-2018-09/2018/11/19/manufacturing-consensus-an-overview-of-distributed-consensus-implementations.html" rel="alternate" type="text/html" title="Manufacturing Consensus: An Overview of Distributed Consensus Implementations" /><published>2018-11-19T00:00:00+00:00</published><updated>2018-11-19T00:00:00+00:00</updated><id>https://decomposition.al/CMPS290S-2018-09/2018/11/19/manufacturing-consensus-an-overview-of-distributed-consensus-implementations</id><content type="html" xml:base="https://decomposition.al/CMPS290S-2018-09/2018/11/19/manufacturing-consensus-an-overview-of-distributed-consensus-implementations.html">&lt;p&gt;by Devashish Purandare · edited by Sohum Banerjea and Lindsey Kuper&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Consensus protocols, as the name suggests, are a class of techniques by which some number of distributed participants can agree on a single value – for instance, to resolve
conflicting values at different replicas of a data object in a replicated data store. Consensus
protocols are some of the most complex algorithms in distributed systems.
How do we get replicas to agree?&lt;/p&gt;

&lt;h3 id=&quot;dont-do-it&quot;&gt;Don’t do it!&lt;/h3&gt;

&lt;p&gt;The first rule of consensus is &lt;a href=&quot;https://github.com/aphyr/distsys-class#avoid-consensus-wherever-possible&quot;&gt;&lt;em&gt;“avoid using it wherever possible”&lt;/em&gt;&lt;/a&gt;.
Consensus is expensive in terms of performance, and it is notoriously hard to reason
about and implement. If you can avoid it, you should. There are several
ways to avoid running a full-blown consensus algorithm to resolve conflicts between replicas:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Skip coordination!&lt;/strong&gt;: &lt;a href=&quot;http://bloom-lang.net/calm/&quot;&gt;The CALM principle&lt;/a&gt; shows
us that if we can design our program so that “adding things to the input can only increase the output”, we
can reliably guarantee eventual consistency as long as all the updates are sent
to each replica at least once. The &lt;a href=&quot;http://bloom-lang.net/index.html&quot;&gt;Bloom programming language&lt;/a&gt; makes use of this idea.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Exploit commutativity and idempotence!&lt;/strong&gt;: &lt;a href=&quot;https://hal.inria.fr/inria-00555588/en/&quot;&gt;CRDTs&lt;/a&gt;
(Conflict-free Replicated Data Types) are built around the notion of monotonic growth with respect to a lattice. For instance, if you maintain a replicated set that can only grow by means of the set union operation, every replica of the set will eventually converge. There
are
several challenges with implementations of CRDTs, including “state explosion” (the
state maintained for each replica can grow exponentially, to the detriment of
storage and performance), complexity, and garbage collection. &lt;a href=&quot;/CMPS290S-2018-09/2018/11/12/implementing-a-garbage-collected-graph-crdt-part-1-of-2.html&quot;&gt;Austen’s previous blog
post goes into more depth about the specifics&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Talk is cheap; when in doubt, ask around!&lt;/strong&gt; &lt;em&gt;Gossip&lt;/em&gt; or &lt;em&gt;epidemic&lt;/em&gt; protocols are
widely used to resolve conficts between replicas. Introduced in a &lt;a href=&quot;http://bitsavers.trailing-edge.com/pdf/xerox/parc/techReports/CSL-89-1_Epidemic_Algorithms_for_Replicated_Database_Maintenance.pdf&quot;&gt;1987 Xerox PARC 
paper&lt;/a&gt;, gossip protocols make progress by sharing update information between replicas.
Replicas contact other, randomly chosen replicas and compare contents. Any differences found
are fixed by comparing timestamps, or using an application-specific conflict resolution approach. Rumor-mongering
is used to maintain hot data changes as special values, until a certain number of
replicas reflect them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;reasons-to-avoid-coordination-and-consensus-when-possible&quot;&gt;Reasons to avoid coordination and consensus when possible&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;It is slow&lt;/strong&gt;: Consensus requires a significant overhead of message passing,
locking, voting, and the like until all replicas agree. This is
time- and resource-intensive, and can be complicated by failures and delays in
the underlying network.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Partial failures&lt;/strong&gt;: One of the biggest issues with distributed systems is that partial failures can
happen, leading to arbitrary message drops, while the system continues to work. Partial failures are notoriously hard to detect, and can compromise the correctness property of any protocol not designed explicitly to deal with them.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;It is impossible!!&lt;/strong&gt;: In their Dijkstra-Award-winning paper &lt;a href=&quot;https://groups.csail.mit.edu/tds/papers/Lynch/jacm85.pdf&quot;&gt;“Impossibility of
Distributed Consensus with One Faulty Process”&lt;/a&gt;, Fischer, Lynch, and Patterson proved that in an asynchronous network environment, it is impossible to ensure
that distributed consensus protocols will succeed – the famous &lt;em&gt;FLP result&lt;/em&gt;.  A failure at a particular
moment can void the correctness of any consensus algorithm. As disheartening as
this seems, there’s a way around it. FLP assumes deterministic processes, and
by
making our algorithms non-deterministic (as in &lt;a href=&quot;https://allquantor.at/blockchainbib/pdf/ben1983another.pdf&quot;&gt;“Another Advantage of Free Choice:
Completely Asynchronous Agreement Protocols”&lt;/a&gt;) can guarantee that a solution is generated “as
long as a majority of the processes continue to operate.”&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;then-why-do-we-need-consensus&quot;&gt;Then why do we need consensus?&lt;/h3&gt;

&lt;p&gt;If consensus is so terrible, why are we still using complex techniques to
achieve it?&lt;/p&gt;

&lt;p&gt;Sometimes, eventually consistent isn’t good enough! Strong consistency conditions
such as &lt;a href=&quot;https://cs.brown.edu/~mph/HerlihyW90/p463-herlihy.pdf&quot;&gt;linearizability&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Serializability&quot;&gt;serializability&lt;/a&gt;
require not only that replicas’ final states agree, but that clients cannot observe different intermediate states on the way there. Ensuring that these conditions hold sometimes leaves us with no choice but to use coordination and
consensus protocols.&lt;/p&gt;

&lt;h3 id=&quot;how-do-consensus-protocols-work&quot;&gt;How do consensus protocols work?&lt;/h3&gt;

&lt;p&gt;It is really hard! &lt;a href=&quot;https://twitter.com/palvaro/status/1059609961360064512&quot;&gt;Ask the experts!&lt;/a&gt;
The abstract of &lt;a href=&quot;https://lamport.azurewebsites.net/pubs/Paxos-simple.pdf&quot;&gt;Paxos Made Simple&lt;/a&gt;
(rather infamously) states:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Paxos algorithm, when presented in plain English, is very simple.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will discuss why it can be hard to implement nevertheless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Listen and Learn:&lt;/strong&gt; Most consensus protocols involve similar steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;In an initial phase, replicas pick a Leader that they are going to listen to.  The Leader is picked by a majority of nodes using an election-like process.&lt;/li&gt;
  &lt;li&gt;Leaders propose values and changes.&lt;/li&gt;
  &lt;li&gt;These values are broadcast to Listeners, who select a correct value based on heuristics, such as a minimum quorum.&lt;/li&gt;
  &lt;li&gt;The rest of the replicas do not play part in picking a winner; they are told
the winning value and update themselves to reflect it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll discuss two popular consensus protocols, Paxos and Raft.&lt;/p&gt;

&lt;h2 id=&quot;implementing-paxos&quot;&gt;Implementing Paxos&lt;/h2&gt;

&lt;p&gt;Leslie Lamport’s paper &lt;a href=&quot;https://lamport.azurewebsites.net/pubs/lamport-Paxos.pdf&quot;&gt;“The Part-Time Parliament”&lt;/a&gt;
introduced the world to the Paxos algorithm – or it would have, had anyone understood it. Lamport followed up with &lt;a href=&quot;https://lamport.azurewebsites.net/pubs/lamport-Paxos.pdf&quot;&gt;“Paxos Made Simple”&lt;/a&gt;
after realizing that the original paper was a little too Greek for the target
audience. Since then, there have been numerous variations on, and optimizations of, the original Paxos algorithm.&lt;/p&gt;

&lt;h3 id=&quot;paxos-made-simple&quot;&gt;Paxos Made Simple&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://lamport.azurewebsites.net/pubs/lamport-Paxos.pdf&quot;&gt;“Paxos Made Simple”&lt;/a&gt; paper states three invariants for correctness:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;Only a value that has been proposed may be chosen,&lt;/li&gt;
    &lt;li&gt;Only a single value is chosen, and&lt;/li&gt;
    &lt;li&gt;A process never learns that a value has been chosen unless it actually has been.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The algorithm breaks down processes into Proposers (P), Acceptors (A), and
Learners (L). The following rules govern 
how acceptors accept proposals:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Each acceptor must accept the first value &lt;em&gt;v&lt;/em&gt; that it gets.&lt;/li&gt;
  &lt;li&gt;After that, the acceptor can only accept any proposal &lt;em&gt;n&lt;/em&gt; as long as it has value &lt;em&gt;v&lt;/em&gt; and &lt;em&gt;n&lt;/em&gt; is greater than its current proposal number.&lt;/li&gt;
  &lt;li&gt;Acceptors in the majority with the highest proposal number are picked winners.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Proposers start with a &lt;em&gt;prepare&lt;/em&gt; request with number &lt;em&gt;n&lt;/em&gt;. An accepted prepare request
means that the acceptor will never accept any other request with value less than
&lt;em&gt;n&lt;/em&gt;. The latest accepted value is communicated along with this response to the
proposer. Once a majority has accepted a value, it can be communicated as an
accept request to the acceptors as well as the Learners.&lt;/p&gt;

&lt;p&gt;The paper’s described system has a distinguished proposer and Learner elected by
the processes from among themselves. Lamport’s Paxos is simpler when there is a single P; issues arise when there are multiple proposers. But the paper is short on implementation details, and implementing Paxos in practice is nontrivial, as the next paper we’ll look at shows.&lt;/p&gt;

&lt;h3 id=&quot;paxos-made-live&quot;&gt;Paxos Made Live&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/paxos_made_live.pdf&quot;&gt;“Paxos Made Live”&lt;/a&gt;, originally an invited talk at PODC ‘07 from an engineering team at Google, offers us insight into Google’s Chubby system, which implements
distributed locking over the GFS filesystem using Paxos. Chubby uses
Multi-Paxos, which repeatedly runs instances of the Paxos algorithm so that a sequence of
values, such as a log, can be agreed upon. The implementation is similar to the
one we just discussed: elect a coordinator, broadcast an accept message, and if
accepted, broadcast a commit message. Coordinators have ordering, and
restrictions on values, to ensure that everybody settles on a single value.&lt;/p&gt;

&lt;p&gt;Because Multi-Paxos can leave behind some replicas (say, in a
network partition), they design a &lt;em&gt;catch up&lt;/em&gt; mechanism,  using a
technique not too different from &lt;a href=&quot;https://en.wikipedia.org/wiki/Write-ahead_logging&quot;&gt;write-ahead logging&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;optimizations&quot;&gt;Optimizations&lt;/h4&gt;

&lt;p&gt;If you can ensure that the leader doesn’t crash and network doesn’t cause arbitrary delays, the authors suggest
chaining updates across the Paxos instances, as there’s only a small chance
of network changes between subsequent transfers. This can eliminate doing the propose (prepare)
phase for each Paxos instance, saving a lot of time.&lt;/p&gt;

&lt;p&gt;You can add bias to the coordinator-picking process to prefer a single
coordinator, and skip the election process or avoid having competing
coordinators in a majority of cases, saving time.&lt;/p&gt;

&lt;h4 id=&quot;engineering-challenges&quot;&gt;Engineering Challenges&lt;/h4&gt;

&lt;p&gt;My favorite part of “Paxos Made Live” is the engineering challenges the authors encountered while
implementing Paxos on a real system for Chubby, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Hard disk failures: These can be addressed by converting the coordinator into a non-voter and using the logging mechanism to track everything until the next
instance of Paxos picks up.&lt;/li&gt;
  &lt;li&gt;Who’s the boss?: The replicas can elect a new coordinator without notifying
the present coordinator, causing issues. They solve the problem by adding leases
on each coordinator’s duration, during which other coordinators cannot be
elected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;“Paxos Made Live” glosses over their handling of the group membership problem:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;While group membership with the core Paxos algorithm is straightforward,
the exact details are non-trivial when we introduce Multi-Paxos, disk
corruptions, etc. Unfortunately the literature does not spell this out, nor
does it contain a proof of correctness for protocols related to group
membership changes using Paxos. We had to fill in these gaps to make group
membership work in our system. The details – though relatively minor – are
subtle and beyond the scope of this paper.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How did they solve this? We may never know.&lt;/p&gt;

&lt;h3 id=&quot;paxos-made-moderately-complex&quot;&gt;Paxos Made Moderately Complex&lt;/h3&gt;

&lt;p&gt;Much like “Paxos Made Simple”, &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=2673577&quot;&gt;“Paxos Made Moderately Complex”&lt;/a&gt;
implements a much more complex system than what the name implies. The authors implement Multi-Paxos and go into
great depth about their implementation and optimizations to it, both as pseudocode
and as C++/Python code (which is &lt;a href=&quot;http://paxos.systems/code.html&quot;&gt;available online&lt;/a&gt;). 
The authors discuss several Paxos variants, concluding that Paxos is more like a
family of protocols rather than a single implementation.&lt;/p&gt;

&lt;p&gt;The authors introduce a new term, &lt;em&gt;slots&lt;/em&gt;, to reason about
Paxos implementation. Slots are commands for each replica to drive its state
towards the right direction. In case different operations end up in the same
slot across different replicas, Paxos can be used to pick a winner. The slots
are not dissimilar to a write-ahead log discussed in other approaches. Confirmed
operations in slots can then be put in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slot\_out&lt;/code&gt; to be communicated to other
replicas. The system then describes several invariants over the slots and
replicas to ensure the Multi-Paxos algorithm remains safe and live.&lt;/p&gt;

&lt;p&gt;The authors also introduce the notions of &lt;em&gt;commanders&lt;/em&gt; and &lt;em&gt;scouts&lt;/em&gt;, commanders being analogous
to coordinators. The idea of scouts is interesting: scouts just act
during the prepare phase, ensuring enough responses before passing the data back
to their leaders. Leaders spawn scouts and wait for an &lt;em&gt;adopted&lt;/em&gt; message from them,
entering a passive mode loop while scouts try to get a majority response. Once in
the active mode, the leaders get consensus to decide which commands are agreed
upon in which slot (only one per slot) and use the commanders to dispatch the
commands in the second phase to all the replicas.&lt;/p&gt;

&lt;p&gt;This paper goes in-depth discussing the original Synod protocol — i.e., the subprotocol of Paxos that implements consensus, as opposed to handling state machine replication — and how it is
limited by practical considerations.&lt;/p&gt;

&lt;p&gt;The authors of “Paxos made Moderately Complex” also have a great resource which goes in depth about Paxos: &lt;a href=&quot;http://paxos.systems/&quot;&gt;paxos.systems&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;paxos-made-pragmatic&quot;&gt;Paxos Made Pragmatic&lt;/h4&gt;

&lt;p&gt;The authors of “Paxos Made Moderately Complex” note that satisfying the aforementioned invariants leads to rapid growth in the state that has to be maintained, as we keep all incoming messages at each slot along with the winner.
The authors provide a few ways to optimize the implementation and actually make it practical:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Reduce state: The system can reduce state maintained by keeping only the
accepted value for each slot. This can cause issues if there are competing leaders
or crashes, as agreed-upon values may be different for the same slot.&lt;/li&gt;
  &lt;li&gt;Garbage collection: Once values are agreed upon by a majority of replicas, it
is not necessary to keep the old values of applied slots. However, just deleting
them might cause an impression that they are empty and can be filled. The
authors address this by adding a variable to the acceptor state to track which
slots have been garbage-collected.&lt;/li&gt;
  &lt;li&gt;Flushing to disk: Periodically, the log can be truncated and flushed to disk, as
keeping the entire state in memory is expensive and can be lost in a power
failure.&lt;/li&gt;
  &lt;li&gt;Read-only operations do not change the state, yet need to get data from a
particular state. Treating such operations differently can help us avoid
expensive operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;riak_ensemble-implementing-vertical-paxos-in-erlang&quot;&gt;riak_ensemble: Implementing Vertical Paxos in Erlang&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/basho/riak_ensemble/tree/develop/doc#overview&quot;&gt;riak_ensemble&lt;/a&gt; consensus library
implements &lt;a href=&quot;https://www.microsoft.com/en-us/research/wp-content/uploads/2009/05/podc09v6.pdf&quot;&gt;Vertical Paxos&lt;/a&gt; (i.e., Paxos that allows hardware reconfiguration even if it is in the middle of agreement process)
in Erlang to create consensus ensembles on top
of key-value stores. riak_ensemble borrows ideas from other implementations, such as the leader
leases described in “Paxos Made Live”, and allows leadership changes, tracking metadata
with the clever use of &lt;a href=&quot;https://en.wikipedia.org/wiki/Merkle_tree&quot;&gt;Merkle trees&lt;/a&gt;. This allows creation of ensembles: consensus groups
that implement Multi-Paxos on a cluster. The authors discuss their implementation
in &lt;a href=&quot;https://www.youtube.com/watch?v=ITstwAQYYag&quot;&gt;this video&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;raft&quot;&gt;Raft&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://Raft.github.io&quot;&gt;Raft&lt;/a&gt; is often touted as a simpler alternative to
Paxos. Winner of a Best Paper award at Usenix ATC 2014, Raft promises a way to navigate through the dangerous and scary waters of consensus protocols.  The &lt;a href=&quot;https://www.usenix.org/system/files/conference/atc14/atc14-paper-ongaro.pdf&quot;&gt;original paper,&lt;/a&gt;
in fact includes an in-depth user study at Stanford to provide evidence that Raft is easier
to understand and explain than Paxos.  While it is an open question whether Raft achieved its simplicity goals, it is widely used in a lot of large-scale systems. In this section we will discuss the Raft protocol and some implementations and contrast it with Paxos.&lt;/p&gt;

&lt;h3 id=&quot;base-implementation&quot;&gt;Base Implementation&lt;/h3&gt;

&lt;p&gt;Raft performs leader elections to pick leaders for each round. The condition for
leadership is stronger in Raft than in Paxos, as leaders are the only nodes that handle reads
and writes to the log as well as log replication to all the replicas.
Conflicting entries in followers’ logs can be overwritten to reflect the leader.
The leader election protocol works with the help of a timeout mechanism for
sending heartbeats to collect votes. The election phase itself comes with a
timeout to reduce conflicts.&lt;/p&gt;

&lt;h3 id=&quot;formal-verification-and-implementation-of-raft-with-vard-and-etcd&quot;&gt;Formal verification and implementation of Raft with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vard&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcd&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/etcd-io/etcd&quot;&gt;etcd&lt;/a&gt; is a lightweight key-value store
implemented using Go. It uses the Raft protocol for distributed consensus to
manage its replicated log. etcd’s &lt;a href=&quot;https://github.com/etcd-io/etcd/tree/master/raft&quot;&gt;Raft library&lt;/a&gt; is also used by other big projects, like &lt;a href=&quot;https://github.com/etcd-io/etcd/blob/master/raft/README.md&quot;&gt;“Kubernetes, Docker Swarm, Cloud
Foundry Diego, CockroachDB, TiDB, Project Calico, Flannel, and more”&lt;/a&gt;, and is a feature-complete implementation of Raft in Go,
including some optional enhancements.&lt;/p&gt;

&lt;p&gt;The verification project &lt;a href=&quot;http://verdi.uwplse.org&quot;&gt;Verdi&lt;/a&gt; base their own key-value
store, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vard&lt;/code&gt;, on the etcd implementation of Raft. They implement the Raft protocol
in the Verdi framework and &lt;a href=&quot;http://verdi.uwplse.org/raft-proof.pdf&quot;&gt;verify&lt;/a&gt; it using &lt;a href=&quot;https://coq.inria.fr&quot;&gt;Coq&lt;/a&gt;, a popular
formal verification tool; from there, it can be extracted to OCaml and is &lt;a href=&quot;https://github.com/uwplse/verdi-Raft&quot;&gt;available
to use&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;raft-in-cockroachdb&quot;&gt;Raft in CockroachDB&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cockroachlabs.com/docs/stable/&quot;&gt;CockroachDB&lt;/a&gt; is an open source
alternative to &lt;a href=&quot;https://storage.googleapis.com/pub-tools-public-publication-data/pdf/65b514eda12d025585183a641b5a9e096a3c4be5.pdf&quot;&gt;Google’s Spanner&lt;/a&gt;,
a highly available distributed store which uses TrueTime, a globally synchronized clock system, to support ACID properties on top of a distributed data store. The big advantage of
these stores is that despite being scalable across continents, they allow linearizability and serializability, along with referential integrity. Raft is used extensively in CockroachDB to ensure that replicas remain consistent.&lt;/p&gt;

&lt;p&gt;CockroachDB implements &lt;a href=&quot;https://www.cockroachlabs.com/blog/scaling-Raft/&quot;&gt;Multi-Raft&lt;/a&gt;
on top of the Raft protocol to allow better
scalability. This involves certain changes to how Raft works. It divides
replicas into ranges, which locally implement Raft. Each range performs leader
elections and other Raft protocol operations. Ranges can have overlapping
memberships. Multi-Raft converts each node’s associated ranges into a group
for Raft, limiting the heartbeat exchange to once per tick.&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;

&lt;p&gt;As we’ve seen, consensus protocols can be hard to understand and implement.  Could programming language support for expressing these protocols help?  In my next post, we’ll consider languages that are specifically designed for the task of implementing distributed protocols such as consensus protocols, and compare implementations in those languages to the general-purpose language implementations discussed here.&lt;/p&gt;</content><author><name>Devashish Purandare</name></author><summary type="html">by Devashish Purandare · edited by Sohum Banerjea and Lindsey Kuper</summary></entry></feed>