<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Readings in Systems</title>
    <link>http://readings.shubheksha.com/</link>
    <description>Recent posts by Daniel</description>
    <generator>Hugo -- gohugo.io</generator>
    <copyright>&amp;copy; 2017 &lt;a href=&#39;https://shubheksha.com&#39;&gt; Shubheksha Jalan &lt;/a&gt;</copyright>
    <lastBuildDate>Thu, 22 Mar 2018 00:00:00 +0000</lastBuildDate>
    
        <atom:link href="http://readings.shubheksha.com/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Robustness in Complex Systems</title>
      <link>http://readings.shubheksha.com/papers/robustness-in-complex-systems/</link>
      <pubDate>Thu, 22 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/robustness-in-complex-systems/</guid>
      <description>

&lt;p&gt;Today we look at the paper titled “&lt;a href=&#34;https://www.gribble.org/papers/robust.pdf&#34;&gt;Robustness in Complex
Systems&lt;/a&gt;” published in 2001 by Steven
D. Gribble. All pull quotes are from the paper.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This paper argues that a common design paradigm for systems is fundamentally
flawed, resulting in unstable, unpredictable behavior as the complexity of the
system grows.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The “common design paradigm” refers to the practice of predicting the
environment the system will operate in and its failure modes. The paper states
that a system will deal with conditions that weren’t predicted as it becomes
more complex, hence it should be designed to cope with failure gracefully. The
paper explores these ideas with the help of “distributed data structures (DDD),
a scalable, cluster-based storage server”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By their very nature, large systems operate through the complex interaction of
many components. This interaction leads to a pervasive coupling of the elements
of the system; this coupling may be strong (e.g., packets sent between adjacent
routers in a network) or subtle (e.g., synchronization of routing advertisements
across a wide area network).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A common characteristic that such large systems exhibit is something known as
the &lt;a href=&#34;https://en.wikipedia.org/wiki/Butterfly_effect&#34;&gt;Butterfly Effect&lt;/a&gt; — a small
unexpected disturbance in the system resulting from the intricate interaction of
various components can result in a widespread change.&lt;/p&gt;

&lt;p&gt;A common goal for system design is robustness: the ability of a system to
operate correctly in various conditions and fail gracefully in an unexpected
situation. The paper argues against the common pattern of trying to predict a
certain set of operation conditions for the system and architecting it to work
well in &lt;em&gt;only those&lt;/em&gt; conditions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is also effectively impossible to predict all of the perturbations that a
system will experience as a result of changes in environmental conditions, such
as hardware failures, load bursts, or the introduction of misbehaving software.
Given this, we believe that any system that attempts to gain robustness solely
through precognition is prone to fragility.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;dds-a-case-study&#34;&gt;DDS: A Case Study&lt;/h3&gt;

&lt;p&gt;The hypothesis stated above is explored using a scalable, cluster-based storage
system Distributed Data Structures (DDD) — “a high-capacity, high-throughput
virtual hash table that is partitioned and replicated across many individual
storage nodes called bricks.”&lt;/p&gt;

&lt;p&gt;This system was built using a predictive design philosophy as the one described
above.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Based on extensive experience with such systems, we attempted to reason about
the behavior of the software components, algorithms, protocols, and hardware
elements of the system, as well as the workloads it would receive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the system operated within the scope of the assumptions made by the
designers, it worked fine. They were able to scale it &amp;amp; improve performance.
However, in the case when one or more of the assumptions about the operating
conditions were violated, the system behaved in unexpected ways resulting in
data loss or inconsistencies.&lt;/p&gt;

&lt;p&gt;Next, we talk about several such anomalies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.  Garbage Collection Thrashing and Bounded Synchrony&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The system designers used timeouts to detect failure of components in the
system. If a particular component didn’t respond within the specified time, it
was considered dead. They assumed bounded synchrony in the system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The DDS was implemented in Java, and therefore made use of garbage collection.
The garbage collector in our JVM was a mark-and-sweep collector; as a result, as
more active objects were resident in the JVM heap, the duration that the garbage
collector would run in order to reclaim a fixed amount of memory would increase.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the system was at saturation, even slight variations in load on the bricks
would increase the time taken by the garbage collector in turn dropping the
throughput of the brick. This is called &lt;strong&gt;GC thrashing&lt;/strong&gt;. The affected bricks
would lag behind their counterparts leading to a further degradation in
performance of the system.&lt;/p&gt;

&lt;p&gt;Hence, garbage collection violated the assumption of bounded synchrony when it
was nearing or beyond the saturation point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; &lt;strong&gt;Slow Leaks and Co-related Failure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another assumption made while designing the system was that the failures are
independent. DDS used replication to make the system fault-tolerant. The
probability of multiple replicas failing simultaneously was very small.&lt;/p&gt;

&lt;p&gt;However, this assumption was violated when they encountered a race condition in
their code that caused a memory leak without affecting correctness.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Whenever we launched our system, we would tend to launch all bricks at the same
time. Given roughly balanced load across the system, all bricks therefore would
run out of heap space at nearly the same time, several days after they were
launched. We also speculated that our automatic failover mechanisms exacerbated
this situation by increasing the load on a replica after a peer had failed,
increase the rate at which the replica leaked memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since all the replicas were subjected to a uniform load without taking
performance degradation and other issues into consideration.This created a
coupling between the replicas and…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;…when combined with a slow memory leak, lead to the violation of our assumption
of independent failures, which in turn caused our system to experience
unavailability and partial data loss&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. Unchecked Dependencies and Fail-stop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Based on the assumption that if a component timed out, it has failed, the
designers also assumed “fail-stop” failures, i.e., a component that has failed
will not resume functioning after a while. The bricks in the system performed
all long-latency work (disk I/O) in an asynchronous way. However, they failed to
notice that some parts of their code made use of blocking function calls. This
caused the main event-handling thread to be randomly borrowed leading to bricks
seizing inexplicably for a couple of minutes and resuming post.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While this error was due to our own failure to verify the behavior of code we
were using, it serves to demonstrate that the low-level interaction between
independently built components can have profound implications on the overall
behavior of the system. A very subtle change in behavior resulted in the
violation of our fail-stop assumption across the entire cluster, which
eventually lead to the corruption of data in our system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;towards-robust-systems&#34;&gt;Towards Robust Systems&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;..small changes to a complex, coupled system can result in large, unexpected
changes in behavior, possibly taking the system outside of its designers’
expected operating regime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few solutions which can help us make more robust systems:&lt;/p&gt;

&lt;h4 id=&#34;systematic-over-provisioning&#34;&gt;Systematic Over-provisioning&lt;/h4&gt;

&lt;p&gt;When approaching the saturation point, systems tend to become fragile to
accommodate unexpected behavior. One way to combat this is to deliberately
over-provision the system.&lt;/p&gt;

&lt;p&gt;However, this has its own set of issues: it leads to the under-utilization of
resources. It also requires predicting the expected operating environment and
hence the saturation point of the system. This can’t be done in an accurate
manner in most cases.&lt;/p&gt;

&lt;h4 id=&#34;use-admission-control&#34;&gt;Use Admission Control&lt;/h4&gt;

&lt;p&gt;Another technique is to start rejecting load once the system starts approaching
the saturation point. However, this requires predicting the saturation point —
something that’s not possible always, especially with large systems which have a
lot of contributing variables.&lt;/p&gt;

&lt;p&gt;Rejecting requests also consumes some resources from the system. Services
designed with admission control in mind usually have two operating modes: normal
where the requests are processed and an extremely lightweight mode where they’re
rejected.&lt;/p&gt;

&lt;h4 id=&#34;build-introspection-into-the-system&#34;&gt;Build Introspection into the system&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;an introspective system is one in which the ability to monitor the system is
designed in from the beginning.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A system which can be monitored and designers and operators can derive
meaningful measurements about its operation is much more robust than one a
black-box system. It’s easier to adapt such a system to change in its
environment, manage and maintain it.&lt;/p&gt;

&lt;h4 id=&#34;introduce-adaptivity-by-closing-the-control-loop&#34;&gt;Introduce adaptivity by closing the control loop&lt;/h4&gt;

&lt;p&gt;An example of a control loop is a human designers and operators adapting the
design in response to a change in its operating environment indicated through
various measurements. However, the timeline for such a control loop isn’t very
predictable. The authors argue that systems should be built with internal
control loops.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These systems incorporate the results of introspection, and attempt to adapt
control variables dynamically to keep the system operating in a stable or
well-performing regime.&lt;/p&gt;

&lt;p&gt;All such systems have the property that the component performing the adaptation
is able to hypothesize somewhat precisely about the effects of the adaptation;
without this ability, the system would be “operating in the dark”, and likely
would become unpredictable. A new, interesting approach to hypothesizing about
the effects of adaptation is to use statistical machine learning; given this, a
system can experiment with changes in order to build up a model of their
effects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;plan-for-failure&#34;&gt;Plan for failure&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Complex systems must expect failure and plan for it accordingly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A couple of techniques to do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;decoupling of components to contain failures locally&lt;/li&gt;
&lt;li&gt;minimize damage by using robust abstractions such as transactions&lt;/li&gt;
&lt;li&gt;minimize amount of time in failure state (using checkpointing to recover
rapidly)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this paper, the authors argue that designing systems by assuming the
constraints and nature of its operation, failures and behavior often leads to
fragile and unpredictable systems. We need a radically different approach to
build systems that are more robust in the face of failure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This different design paradigm is one in which systems are given the best
possible chance of stable behavior (through techniques such as
over-provisioning, admission control, and introspection), as well as the ability
to adapt to unexpected situations (by treating introspection as feedback to a
closed control loop). Ultimately, systems must be designed to handle failures
gracefully, as complexity seems to lead to an inevitable unpredictability.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title>A Design Methodology For Reliable Software Systems</title>
      <link>http://readings.shubheksha.com/papers/design-methodoly-for-reliable-software-systems/</link>
      <pubDate>Sat, 03 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/design-methodoly-for-reliable-software-systems/</guid>
      <description>

&lt;p&gt;Let’s dig into &lt;a href=&#34;https://valbonne-consulting.com/papers/classic/Liskov_72-Design_Methodology_for_Reliable_Software_Systems.pdf&#34;&gt;A Design Methodology For Reliable Software
Systems&lt;/a&gt;
published by Barbara Liskov in 1972.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/1600/0*YBoSTQ9iJHRhc3jm.jpeg&#34; alt=&#34;&#34; /&gt;
&lt;span class=&#34;figcaption_hack&#34;&gt;&lt;a href=&#34;https://www.defit.org/wp-content/uploads/2012/06/puzzle-modularity-320x259.jpeg&#34;&gt;Credit&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The focus of this paper is on how to make reliable software systems and what
techniques can help us achieve that. Reliability here implies that a system
works as expected under a given set of conditions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The unfortunate fact is that the standard approach to building&lt;br&gt; systems,
involving extensive debugging, has not proved successful in producing reliable
software, and there is no reason to suppose it ever will. Although improvements
in debugging techniques may lead to the detection of more errors, this does not
imply that all errors will be found. There certainly is no guarantee of this
implicit in debugging: as Dijkstra said, “Program testing can be used to show
the presence of bugs, but never to show their absence.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To be confident that our system works correctly, we need testing that meets the
following conditions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can generate a minimal set of relevant test cases&lt;/li&gt;
&lt;li&gt;All test cases in the set can be generated&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;The solutions to these problems do not lie in the domain of debugging, which has
no control over the sources of the problems. Instead, since it is the system
design which determines how many test cases there are and how easily they can be
identified, the problems can be solved most effectively during the design
process: The need for exhaustive testing must influence the design.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paper further argues that reliability is a major issue with complex systems.
It goes on to define complex systems as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The system has many states and it’s difficult to organize program logic to
handle all of them correctly&lt;/li&gt;
&lt;li&gt;It requires several people working together in a coordinated manner&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;criteria-for-a-good-design&#34;&gt;Criteria for a Good Design:&lt;/h3&gt;

&lt;p&gt;To tame the design of a complex system, we need to use modularization and divide
the program into several modules (sub programs, later on referred to as
partitions in the paper to avoid overloading the term “modules”) which can be
compiled separately but are connected to other modules.&lt;/p&gt;

&lt;p&gt;The connections are defined by Parnas as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The connections between modules are the assumptions which the modules make about
each other.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Although the idea of modularity sounds like a great tool for building large
complex software systems, it can introduce additional complexity if not done
right.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The success of modularity depends directly on how well modules are chosen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some common issues are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A module does too many things&lt;/li&gt;
&lt;li&gt;A common function is distributed among many different modules&lt;/li&gt;
&lt;li&gt;A module behaves unexpectedly with common data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next question that arises: &lt;strong&gt;What is good modularity&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;We use two techniques to answer that: &lt;strong&gt;levels of abstraction&lt;/strong&gt; to tackle the
inherent complexity of the system and &lt;strong&gt;structured programming&lt;/strong&gt; to represent
the design in software.&lt;/p&gt;

&lt;h4 id=&#34;levels-of-abstraction&#34;&gt;Levels of abstraction:&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Levels of abstraction…provide a conceptual framework for achieving a clear and
logical design for a system. The entire system is conceived as a hierarchy of
levels, the lowest levels being those closest to the machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A group of related functions make up a level of abstraction. Each level can have
the following two types of functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;External: These functions can be called by functions in other levels&lt;/li&gt;
&lt;li&gt;Internal: These functions do a common task within the level and cannot be called
by other functions in a different level&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Levels of abstraction are governed by the following two rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each level has exclusive control over some kind of resource&lt;/li&gt;
&lt;li&gt;Lower levels aren’t aware of higher levels and can’t reference them in any way.
However, higher levels can ask lower levels to perform an action or for info.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&#34;structured-programming&#34;&gt;Structured Programming:&lt;/h4&gt;

&lt;p&gt;A structured program defines the way control passes among various partitions in
a system. It is defined by the following two rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The program is developed in a top down fashion and divided into levels (the
notion of levels here is different from that of levels of abstraction because
the first rule isn’t satisfied)&lt;/li&gt;
&lt;li&gt;Only the following control structures can be used: concatenation, selection&lt;br&gt;
of the next statement based on the testing of a condition,&lt;br&gt; and iteration.
Jumping using &lt;code&gt;goto&lt;/code&gt; isn’t permitted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Back to the question that was posed earlier: &lt;strong&gt;how do we define good
modularity&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;In a modular system that is also reliable, the connections between partitions
are limited as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They need to follow the rules imposed by levels of abstraction and the
structured programming&lt;/li&gt;
&lt;li&gt;Passing of data between partitions should be done using explicit arguments
passed to external functions of another partition&lt;/li&gt;
&lt;li&gt;Partitions should be logically independent — the functions within a partition
should support its own abstraction &lt;em&gt;only&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next question that arises after we’ve figured out how to defined good
modularity is — &lt;strong&gt;how do we achieve it in our design&lt;/strong&gt;?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The traditional technique for modularization is to analyze the execution-time
flow of the system and organize the system structure around each major
sequential task. This technique leads to a structure which has very simple
connections in control, but the connections in data tend to be complex.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Partitions supports abstractions that a system designer finds helpful when
thinking about the system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Abstractions are introduced in order to make what the system is doing clearer
and more understandable; an abstraction is a conceptual simplification because
it expresses what is being done without specifying how it is done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paper then presents some guidelines for identifying different types of
abstractions while designing a system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Abstraction of resources: for every hardware resource on the system, we can map
characteristics of the abstract resource to the underlying resource&lt;/li&gt;
&lt;li&gt;Abstract characteristics of data: how it is stored&lt;/li&gt;
&lt;li&gt;Simplification via limiting information the partition needs to know or has
access to&lt;/li&gt;
&lt;li&gt;Simplification via generalization by identifying functions that perform a common
task. Such functions can be grouped together in one partition. “The existence of
such a group simplifies other partitions, which need only appeal to the
functions of the lower partition rather than perform the tasks themselves.”&lt;/li&gt;
&lt;li&gt;System maintenance and modification: functions performing a task whose
definition is prone to change in the future should be part of independent
partitions. For example, functions which deal with connecting to a particular
kind of storage back end so that if a different back end is used in the future,
only functions in that partition will be affected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we have some idea about how we can achieve good modularity while
designing our system, &lt;strong&gt;how do we proceed with it&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;The first phase is to identify a set of abstractions that represent the eventual
behavior of the system in a general way. The next phase “establishes the data
connections between the partitions and describes the flow of control among the
partitions”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The second phase occurs concurrently with the first; as abstractions&lt;br&gt; are
proposed, their utility and practicality are immediately investigated.&lt;/p&gt;

&lt;p&gt;A partition has been adequately investigated when&lt;br&gt; its connections with the
rest of the system are known&lt;br&gt; and when the designers are confident that they
understand&lt;br&gt; exactly what its effect on the system will be.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next question one would ask is:** how do we identify when the design is
finished**?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;All major abstractions have been identified and been linked to a partition. The
system resources have been divided among the various partitions and their
positions in the hierarchy have been defined&lt;/li&gt;
&lt;li&gt;The interfaces and flow of control among the partitions is clearly defined. The
test cases for each partition have been identified&lt;/li&gt;
&lt;li&gt;A basic user guide for the system can be written&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Program Design in the Unix Environment</title>
      <link>http://readings.shubheksha.com/papers/program-design-in-unix/</link>
      <pubDate>Mon, 29 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/program-design-in-unix/</guid>
      <description>

&lt;h1 id=&#34;program-design-in-the-unix-environment-a-summary&#34;&gt;Program Design in the Unix Environment: A Summary&lt;/h1&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/1600/0*O-H_d2lDRBmnCgFG.jpg&#34; alt=&#34;&#34; /&gt;
&lt;span class=&#34;figcaption_hack&#34;&gt;&lt;a href=&#34;http://www.adamalthus.com/blog/2013/04/04/the-composable-enterprise/&#34;&gt;Credit&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Today, let’s take a look at “&lt;a href=&#34;http://harmful.cat-v.org/cat-v/unix_prog_design.pdf&#34;&gt;Program Design in the Unix
Environment&lt;/a&gt;” published in
1983 by Pike and Kernighan.&lt;/p&gt;

&lt;p&gt;The paper opens by listing why Unix has been successful and is a commentary on
the&lt;a href=&#34;https://en.wikipedia.org/wiki/Unix_philosophy&#34;&gt; Unix philosophy&lt;/a&gt; and its
benefits. It does so by taking examples and discussing trade offs where programs
diverged from the Unix philosophy.&lt;/p&gt;

&lt;p&gt;The reasons for Unix’s success:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Portability: the kernel &amp;amp; applications are written in C, hence they can be moved
from system to system without being re-written in the assembly language
particular to that system.&lt;/li&gt;
&lt;li&gt;The same OS runs on different hardware, so the users are already familiar and
don’t have to relearn when new hardware is released.&lt;/li&gt;
&lt;li&gt;The vendors can ship same software with each machine despite changes in
hardware.&lt;/li&gt;
&lt;li&gt;The system was not too big and easy to modify since everything was written in C.&lt;/li&gt;
&lt;li&gt;It provided a new philosophy based on the use of general purpose tools which did
one thing well and could be combined to do a particular task instead of creating
giant monolithic tools that serve only one purpose.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The paper argues that the use and design of tools is closely related — how they
fit together is the main subject of this essay.&lt;/p&gt;

&lt;p&gt;The paper then dives into &lt;code&gt;cat&lt;/code&gt;— the Unix command line utility for
concatenating and printing files — it copies its input to its output. The input
is usually a sequence of one more of files or the standard input. The output is
a file or the standard output.&lt;/p&gt;

&lt;p&gt;The main purpose of &lt;code&gt;cat&lt;/code&gt; was to act as a utility to concatenate files. It can
be combined with the pipe (&lt;code&gt;|&lt;/code&gt;) operator to further enhance to extend its
utility through output redirection.&lt;/p&gt;

&lt;p&gt;Other systems, on the other hand, try to dump a bunch of related functionality
into a single command which is against the Unix philosophy. It also creates a
lock-in of functionality that might to useful to other programs.&lt;/p&gt;

&lt;p&gt;Advantages of the Unix approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The shell and the programs it can invoke provide a uniform access to system
facilities. Eg: the filename arguments are expanded by the shell in a similar
fashion for each command. Because of pipes, we don’t need every command to deal
with pre- and post- processing of input.&lt;/li&gt;
&lt;li&gt;Growth is easy when functions are well separated.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example: the ` (backquote) operator was added to convert the output of one
program into input of another without requiring changes in any other program as
it is interpreted by the shell. All programs the shell invokes acquire this
feature automatically. If each program that required this feature interpreted
it, it’d be very hard to enforce uniformity and carry out further
experimentation as each new idea would affects all the programs that would want
to use it.&lt;/p&gt;

&lt;p&gt;However, in the future versions of &lt;code&gt;cat&lt;/code&gt;, many new options were introduced, like
for printing line numbers and non-printable characters.&lt;/p&gt;

&lt;p&gt;The authors argue that instead of adding those options to &lt;code&gt;cat&lt;/code&gt; itself, either
existing programs should’ve been used or new programs should’ve been created.
For example, line number functionality could’ve been provided by using &lt;code&gt;pr&lt;/code&gt;.
However, there was no program which allowed printing of non-printable characters
hence warranting the creation of a new one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Such a modification confuses what &lt;code&gt;cat&lt;/code&gt;’s job is  concatenating files 
with&lt;br&gt; what it happens to do in a common special case  showing a file on the
terminal. A UNIX program should do one thing well, and leave unrelated tasks to
other programs. &lt;code&gt;cat&lt;/code&gt;’s job is to collect the data in files. Programs that
collect data shouldn’t change the data; &lt;code&gt;cat&lt;/code&gt; therefore shouldn’t transform its
input.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whenever we split something into multiple programs, we sacrifice some
efficiency. But since &lt;code&gt;cat&lt;/code&gt; is usually used without any options, it makes sense
to have the most common cases be the most efficient.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Separate programs are not always better than wider options; which is better
depends on the problem. Whenever one needs a way to perform a new function, one
faces the choice of whether to add a new option or write a new program (assuming
that none of the programmable tools will do the job conveniently). The&lt;br&gt;
guiding principle for making the choice should be that each program does one
thing. Options are appropriately added to a program that already has the right
functionality. If there is no such program, then a new program is called for. In
that case, the usual criteria for program design should be used: the program
should be as general as possible, its default behavior should match the most
common usage, and it should cooperate with other programs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;Let’s consider another issue: dealing with fast terminal lines. How to deal with
output from &lt;code&gt;cat&lt;/code&gt; scrolling off the top of the screen?&lt;/p&gt;

&lt;p&gt;There are two approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tell each command about terminal properties so it does the right thing&lt;/li&gt;
&lt;li&gt;Write a command that handles only terminals without modifying other programs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s consider examples of both approaches: &lt;code&gt;lsc&lt;/code&gt; and &lt;code&gt;ls&lt;/code&gt; which prints out the
list of files in a directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lsc&lt;/code&gt; varies its output depending on the input — it displays the list in a
columnar fashion across the screen so that the o/p fits if it’s outputting to
the terminal whereas &lt;code&gt;ls&lt;/code&gt; displays everything in a single column.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By retaining single column output to files or pipes, &lt;code&gt;lsc&lt;/code&gt;ensures compatibility
with programs like &lt;code&gt;grep&lt;/code&gt;or &lt;code&gt;wc&lt;/code&gt;that expect things to be printed one per line.
This ad-hoc adjustment of the output format depending on the destination is not
only distasteful, it is unique  no standard UNIX command has this property.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The authors argue that the columnation facility is useful in general &amp;amp; shouldn’t
be locked away in just &lt;code&gt;lsc&lt;/code&gt; and be inaccessible to other programs. They
advocate for a different program whose primary job is columnation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Similar reasoning suggests a solution for the general problem of data flowing
off screens (columnated or not): a separate program to take any input and print
it a screen at a time. Such programs are by now widely available, under names
like pg and more. This solution affects no other programs, but can be used with
all of them. As usual, once the basic feature is right, the program can be
enhanced with options…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;Based on the previous example, the authors also talk about different cases where
some functionality is locked away in a specific program, like input history in
the terminal, which would be better off as a central service. All interactive
programs could benefit from it.&lt;/p&gt;

&lt;p&gt;They conclude with how augmenting existing commands with features/options is not
desirable in Unix, it goes against its basic philosophy which is to make a
program do one thing well and several such programs can be composed to
accomplish a more complex task.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The key to problem solving on the UNIX system is to identify the right primitive
operations and to put them at the right place. UNIX programs tend to solve
general problems rather than special cases. In a very loose sense, the programs
are orthogonal, spanning the space of jobs to be done (although with a fair&lt;br&gt;
amount of overlap for reasons of history, convenience or efficiency). Functions
are placed where they will do the most good: there shouldn’t be a pager in every
program that produces output any more than there should be filename pattern
matching in every program that uses filenames.&lt;/p&gt;

&lt;p&gt;One thing that UNIX does not need is more features. It is successful in part
because it has a small number of good ideas that work well together. Merely
adding features does not make it easier for users to do things  it just makes
the manual thicker. The right solution in the right place is always more
effective&lt;br&gt; than haphazard hacking.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title>Building On Quicksand</title>
      <link>http://readings.shubheksha.com/papers/building-on-quicksand/</link>
      <pubDate>Mon, 04 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/building-on-quicksand/</guid>
      <description>

&lt;p&gt;Let’s try to break down the paper “&lt;a href=&#34;http://arxiv.org/ftp/arxiv/papers/0909/0909.1788.pdf&#34;&gt;Building On Quicksand&lt;/a&gt;” published by Pat Helland and David Campbell in 2009. All pull quotes are from the paper.&lt;/p&gt;

&lt;p&gt;The paper focuses on the design of large, fault-tolerant, replicated distributed systems and how it’s evolving based on changing requirements over time. It starts of by stating “Reliable systems have always been built out of unreliable components”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As the granularity of the unreliable component grows (from a mirrored disk to a system to a data center), the latency to communicate with a backup becomes unpalatable. This leads to a more relaxed model for fault tolerance. The primary system will acknowledge the work request and its actions without waiting to ensure that the backup is notified of the work. This improves the responsiveness of the system because the user is not delayed behind a slow interaction with the backup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fault-tolerant systems can be made of many components and their goal is keep functioning correctly when one of those components fail. We don’t consider &lt;strong&gt;&lt;em&gt;Byzantine&lt;/em&gt;&lt;/strong&gt; failures in this discussion, but instead the &lt;strong&gt;&lt;em&gt;fail fast&lt;/em&gt;&lt;/strong&gt; model where either a component works correctly or it fails.&lt;/p&gt;

&lt;p&gt;The paper goes on to compare two versions of the Tandem NonStop system — one that used synchronous &lt;a href=&#34;https://en.wikipedia.org/wiki/Application_checkpointing&#34;&gt;checkpointing&lt;/a&gt; and one that used asynchronous checkpointing. Refer section 3 of the paper for all the details. I’d like to briefly touch upon the difference between the two checkpointing strategies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Synchronous checkpointing: in this case, with every write to the primary, state needed to be sent to the backup. Only after the write was acknowledged by the backup did the primary send a response to the client who issued the write request. This ensured that when the primary fails, the backup can take over without losing any work.&lt;/li&gt;
&lt;li&gt;Asynchronous checkpointing: in this strategy, the primary acknowledges &amp;amp; commits the write as soon as it processes it without waiting for a reply from the backup. This technique has improved latency but also poses other challenges addressed later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;log-shipping&#34;&gt;Log Shipping&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;A classic database system has a process that reads the log and ships it to a backup data-center. The normal implementation of this mechanism commits transactions at the primary system (acknowledging the user’s commit request) and asynchronously ships the log. The backup database replays the log, constantly playing catch-up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The mechanism described above is termed as log shipping. The main problem this poses is that when the primary fails and the back up takes over, some recent transactions might be lost.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This inherently opens up a window in which the work is acknowledged to the client but it has not yet been shipped to the backup. A failure of the primary during this window will lock the work inside the primary for an unknown period of time. The backup will move ahead without knowledge of the locked up work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The introduction of asynchrony into the system hasan advantage in terms of latency, response time and performance but it makes the system more prone to the possibility of losing work when the primary fails. There are two ways to deal with this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Discard the work locked in the primary when it fails. Whether a system can do that or not depends on the requirements and business rules.&lt;/li&gt;
&lt;li&gt;Have a recovery mechanism to sync the primary with backups when it comes back up and retry lost work. This is possible only if the operations can be retried in an idempotent way and the out-of-order retries are possible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The system loses the notion of what the authors call “an authoritative truth”. Nobody knows the accurate state of the system at any given point of time if the work is allowed to be locked in an unavailable backup or primary.&lt;/p&gt;

&lt;p&gt;This leads to the conclusion that business rules in a system with asynchronous checkpointing are probabilistic:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a primary uses asynchronous checkpointing and applies a business rule on the incoming work, it is necessarily a probabilistic rule. The primary, despite its best intentions cannot know it will be alive to enforce the business rules.
When the backup system that participates in the enforcement of these business rules is asynchronously tied to the primary, the enforcement of these rules inevitably becomes probabilistic!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The authors state that commutative operations (operations that can be reordered) can be allowed to execute independently and reordered as long as business rules are preserved. However, this is hard to do with storage systems because the write operation isn’t commutative.&lt;/p&gt;

&lt;p&gt;Another consideration is that work of a single operation is idempotent, i.e., executing the operation any number of time should result in the same state of the system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To ensure this, applications typically assign a unique number or ID to the work. This is assigned at the ingress to the system (i.e. whichever replica first handles the work). As the work request rattles around the network, it is easy for a replica to detect that it has already seen that operation and, hence, not do the work twice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The authors suggest that different operations within a system can provide different consistency guarantees depending on the business requirements. Some operations can choose classic consistency over availability and vice versa.&lt;/p&gt;

&lt;p&gt;Next, the authors argue that as soon there is no notion of authoritative truth in a system, all of computing boils down to three things: memories, guesses and apologies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memories: you can only hope that your replica remembers what it has already seen.&lt;/li&gt;
&lt;li&gt;Guesses: Due to only partial knowledge being available, the replicas take actions based on local state and may be wrong. “In any system which allows a degradation of the absolute truth, any action is, at best, a guess.” Any action in such a system has a high probability of being successful, but it’s still just a guess.&lt;/li&gt;
&lt;li&gt;Apologies: Mistakes are inevitable, hence every business needs to have an apology mechanism in place either through human intervention or by automating it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The paper next discusses the topic of eventual consistency by taking the Amazon shopping cart built using Dynamo &amp;amp; a system for clearing checks as examples. The work coming into these systems is uniquely identified and is processed by a single replica. It flows to other replicas as and when connectivity permits. The requests coming into these systems are commutative (reorderable) and can be processed at different replicas in different orders.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Storage systems alone cannot provide the commutativity we need to create robust systems that function with asynchronous checkpointing. We need the business operations to reorder. Amazon’s Dynamo does not do this by itself. The shopping cart application on top of the Dynamo storage system is responsible for the semantics of eventual consistency and commutativity. The authors think it is time for us to move past the examination of eventual consistency in terms of updates and storage systems. The real action comes when examining application based operation semantics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next they discuss two strategies for allocating resources in replicas that might not be able to communicate with each other:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Over-provisioning: the resources are partitioned between replicas such that each has a fixed subset of resources they can allocate. No replica can allocate a resource that’s not actually available.&lt;/li&gt;
&lt;li&gt;Over-booking: the resources are can be individually allocated without ensuring strict partitioning. This may lead to the replicas allocating a resource that’s not truly available, promising something they can’t deliver.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The paper talks also about something termed as the “seat reservation pattern” which is a compromise between over-provisioning and over-booking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Anyone who has purchased tickets online will recognize the “Seat Reservation” pattern where you can identify potential seats and then you have a bounded period of time, (typically minutes), to complete the transaction. If the transaction is not successfully concluded within the time period, the seats are once again marked as “available”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;acid-2-0&#34;&gt;ACID 2.0&lt;/h4&gt;

&lt;p&gt;The classic definition of ACID stands for “Atomic, Consistent, Isolated, and Durable”, it’s goal is to make the application think that there is a single computer which isn’t doing anything else when the transaction is being processed. The authors talk about a new definition for ACID which stands for: Associative, Commutative, Idempotent, and Distributed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The goal for ACID2.0 is to succeed if the pieces of the work happen: At least once, anywhere in the system, in any order. This defines a new KIND of consistency. The individual steps happen at one or more system. The application is explicitly tolerant of work happening out of order. It is tolerant of the work happening more than once per machine, too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Going by the classic definition of ACID, fault tolerance is based on having a linear history. If we want to achieve the same guarantees in a distributed system, it’ll require concurrency control mechanisms which “tend to be fragile”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When the application is constrained to the additional requirements of commutativity and associativity, the world gets a LOT easier. No longer must the state be checkpointed across failure units in a synchronous fashion. Instead, it is possible to be very lazy about the sharing of information. This opens up offline, slow links, low quality datacenters, and more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In conclusion,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We have attempted to describe the patterns in use by many applications today as they cope with failures in widely distributed systems. It is the reorderability of work and repeatability of work that is essential to allowing successful application execution on top of the chaos of a distributed world in which systems come and go when they feel like it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;P.S. — If you made it this far and would like to receive a mail whenever I publish one of these posts, sign up &lt;a href=&#34;http://eepurl.com/dcHGFP&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Harvest, Yield, and Scalable Tolerant Systems</title>
      <link>http://readings.shubheksha.com/papers/harvest-yield/</link>
      <pubDate>Mon, 30 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/harvest-yield/</guid>
      <description>

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/300/0*PlLhXyx7tBvL4fVm.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;This post presents a summary of the paper “&lt;a href=&#34;https://pdfs.semanticscholar.org/5015/8bc1a8a67295ab7bce0550886a9859000dc2.pdf&#34;&gt;Harvest, Yield, and Scalable Tolerant Systems&lt;/a&gt;” published by Eric Brewer &amp;amp; Amando Fox in 1999.&lt;/p&gt;

&lt;p&gt;This paper deals with the trade offs between consistency and availability for large systems. It’s very easy to point to CAP and assert that no system can have consistency and availability. However, there is a catch. CAP has been misunderstood in a variety of ways. As Coda Hale explains in his excellent blog post “&lt;a href=&#34;https://codahale.com/you-cant-sacrifice-partition-tolerance/&#34;&gt;You Can’t Sacrifice Partition Tolerance&lt;/a&gt;”:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Of the CAP theorem’s Consistency, Availability, and Partition Tolerance, Partition Tolerance is mandatory in distributed systems. You cannot &lt;strong&gt;not&lt;/strong&gt; choose it. Instead of CAP, you should think about your availability in terms of &lt;em&gt;yield&lt;/em&gt; (percent of requests answered successfully) and &lt;em&gt;harvest&lt;/em&gt; (percent of required data actually included in the responses) and which of these two your system will sacrifice when failures happen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This paper focuses on increasing the availability of large scale systems by fault toleration, containment &amp;amp; isolation.&lt;/p&gt;

&lt;p&gt;This paper focuses on increasing the availability of large scale systems by fault toleration, containment &amp;amp; isolation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We assume that clients make queries to servers, in which case there are at least two metrics for correct behavior: yield, which is the probability of completing a request, and harvest, which measures the fraction of the data reflected in the response, i.e. the completeness of the answer to the query.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The two metrics, &lt;strong&gt;harvest&lt;/strong&gt; and &lt;strong&gt;yield&lt;/strong&gt; can be summarized as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Harvest&lt;/strong&gt; : data in response/total data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Example&lt;/em&gt;: If one of the nodes is down in a 100 node cluster, the harvest is 99% for the duration of the fault.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Yield&lt;/strong&gt; : requests completed with success/total number of requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Yield is different from uptime. Yield specifically deals with the number of requests, not just the time the system wasn’t able to respond to requests.&lt;/p&gt;

&lt;p&gt;The paper argues that while there are certain systems which require perfect responses to queries every single time, there are systems that can tolerate imperfect answers once in a while. To increase the overall availability of our systems, we need to carefully think through the required consistency and availability guarantees it needs to provide.&lt;/p&gt;

&lt;h4 id=&#34;trading-harvest-for-yield-probabilistic-availability&#34;&gt;Trading Harvest for Yield — Probabilistic Availability&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Nearly all systems are probabilistic whether they realize it or not. In particular, any system that is 100% available under single faults is probabilistically available overall (since there is a non-zero probability of multiple failures)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fox and Brewer talk about understanding the probabilistic nature of availability. This helps in understanding and limiting the impact of faults by making decisions about what needs to be available and what kind of faults can the system deal with.&lt;/p&gt;

&lt;p&gt;They outline the linear degradation of harvest in case of multiple node faults. The harvest is directly proportional to the number of nodes that are functioning correctly, hence it’s decreases/increases linearly. Two strategies are suggested for increasing the yield:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Random distribution of data on the nodes. If one of the nodes goes down, the average-case and worst-case fault behavior doesn’t change. Instead if the distribution isn’t random, then depending type of data, the impact of a fault maybe variable. For example, if only one of the nodes stored info related to a user’s account balance and it goes down, he entire banking system will not be able to work.&lt;/li&gt;
&lt;li&gt;Replicating the most important data. This reduces the impact in case one of the nodes containing a subset of high-priority data goes down and also improves harvest.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Another notable observation made in the paper is that while it is possible to replicate all of your data, it doesn’t do a lot to improve your harvest/yield while increases the cost of operation substantially. This is because the internet works based on best-in-effort protocols which can never guarantee 100% harvest/yield.&lt;/p&gt;

&lt;h4 id=&#34;application-decomposition-and-orthogonal-mechanisms&#34;&gt;Application Decomposition and Orthogonal Mechanisms&lt;/h4&gt;

&lt;p&gt;The second strategy focuses on the benefits of orthogonal system design. It starts out stating that large systems are composed of sub systems which independently cannot tolerate failures but fail in a way that allows the entire system to continue functioning with some impact on utility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The actual benefit is the ability to provision each subsystem’s state management separately, providing strong consistency or persistent state only for the subsystems that need it, not for the entire application. The savings can be significant if only a few small subsystems require the extra complexity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paper states that orthogonal components are completely independent of each other having no run time interface to other components except a configuration interface maybe. This allows each individual component to fail independently minimizing its impact on the overall system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Composition of orthogonal subsystems shifts the burden of checking for possibly harmful interactions from runtime to compile time, and deployment of orthogonal guard mechanisms improves robustness for the runtime interactions that do occur, by providing improved fault containment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Over all, the goal of this paper was to motivate research in the field of designing fault-tolerant and highly available large scale systems. Also, to think carefully about the consistency and availability guarantees the application needs to provide and the trade offs it is capable of making in terms of harvest vs yield.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>In Search of An Understandable Consensus Algorithm (Raft)</title>
      <link>http://readings.shubheksha.com/papers/raft/</link>
      <pubDate>Thu, 12 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/raft/</guid>
      <description>

&lt;p&gt;This post summarizes the Raft consensus algorithm presented in the paper &lt;a href=&#34;https://www.usenix.org/system/files/conference/atc14/atc14-paper-ongaro.pdf&#34;&gt;In Search of An Understandable Consensus Algorithm&lt;/a&gt; by Diego Ongaro and John Ousterhout. All pull quotes are taken from that paper.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/512/0*U14WseYPLL8tHj0V.png&#34; alt=&#34;&#34; /&gt;&lt;figcaption align=&#34;center&#34;&gt;&lt;a href=&#34;https://github.com/raft/logo/tree/3d2c4d5ca0d9c4fb8d5c28a82c4a43e576673b06&#34;&gt;Credit&lt;/a&gt;&lt;/figcaption&gt;&lt;/p&gt;

&lt;h4 id=&#34;raft&#34;&gt;Raft:&lt;/h4&gt;

&lt;p&gt;Raft is a distributed consensus algorithm. It was designed to be easily understood. It solves the problem of getting multiple servers to agree on a shared state even in the face of failures. The shared status is usually a data structure supported by a replicated log. We need the system to be fully operational as long as a majority of the servers are up.&lt;/p&gt;

&lt;p&gt;Raft works by electing a leader in the cluster. The leader is responsible for accepting client requests and managing the replication of the log to other servers. The data flows only in one direction: from leader to other servers.&lt;/p&gt;

&lt;p&gt;Raft decomposes consensus into three sub-problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leader Election: A new leader needs to be elected in case of the failure of an existing one.&lt;/li&gt;
&lt;li&gt;Log replication: The leader needs to keep the logs of all servers in sync with its own through replication.&lt;/li&gt;
&lt;li&gt;Safety: If one of the servers has committed a log entry at a particular index, no other server can apply a different log entry for that index.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/441/1*ptMVsjmS6nVkDCERRkCPEQ.png&#34; alt=&#34;&#34; /&gt;&lt;figcaption align=&#34;center&#34;&gt;Raft ensures these properties are true at all times.&lt;/figcaption&gt;&lt;/p&gt;

&lt;h4 id=&#34;basics&#34;&gt;Basics:&lt;/h4&gt;

&lt;p&gt;Each server exists in one of the three states: leader, follower, or candidate.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/580/1*_B3mkKkJiCXJDQJNdd17KA.png&#34; alt=&#34;&#34; /&gt;&lt;figcaption align=&#34;center&#34;&gt;State changes of servers&lt;/figcaption&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In normal operation there is exactly one leader and all of the other servers are followers. Followers are passive: they issue no requests on their own but simply respond to requests from leaders and candidates. The leader handles all client requests (if a client contacts a follower, the follower redirects it to the leader). The third state, candidate, is used to elect a new leader.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Raft divides time into &lt;strong&gt;terms&lt;/strong&gt; of arbitrary length, each beginning with an election. If a candidate wins the election, it remains the leader for the rest of the term. If the vote is split, then that term ends without a leader.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;term number&lt;/strong&gt; increases monotonically. Each server stores the &lt;strong&gt;current term number&lt;/strong&gt; which is also exchanged in every communication.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;.. if one server’s current term is smaller than the other’s, then it updates its current term to the larger value. If a candidate or leader discovers that its term is out of date, it immediately reverts to follower state. If a server receives a request with a stale term number, it rejects the request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Raft makes use of two remote procedure calls (RPCs) to carry out its basic operation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RequestVotes is used by candidates during elections&lt;/li&gt;
&lt;li&gt;AppendEntries is used by leaders for replicating log entries and also as a heartbeat (a signal to check if a server is up or not — it doesn’t contain any log entries)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;leader-election&#34;&gt;Leader election&lt;/h4&gt;

&lt;p&gt;The leader periodically sends a heartbeat to its followers to maintain authority. A leader election is triggered when a follower times out after waiting for a heartbeat from the leader. This follower transitions to the candidate state and increments its &lt;strong&gt;term number&lt;/strong&gt;. After voting for itself, it issues RequestVotes RPC in parallel to others in the cluster. Three outcomes are possible:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The candidate receives votes from the majority of the servers and becomes the leader. It then sends a heartbeat message to others in the cluster to establish authority.&lt;/li&gt;
&lt;li&gt;If other candidates receive AppendEntries RPC, they check for the term number. If the term number is greater than their own, they accept the server as the leader and return to follower state. If the term number is smaller, they reject the RPC and still remain a candidate.&lt;/li&gt;
&lt;li&gt;The candidate neither loses nor wins. If more than one server becomes a candidate at the same time, the vote can be split with no clear majority. In this case a new election begins after one of the candidates times out.
&amp;gt; Raft uses randomized election timeouts to ensure that split votes are rare and that they are resolved quickly. To prevent split votes in the first place, election timeouts are chosen randomly from a fixed interval (e.g., 150–300ms). This spreads out the servers so that in most cases only a single server will time out; it wins the election and sends heartbeats before any other servers time out. The same mechanism is used to handle split votes. Each candidate restarts its randomized election timeout at the start of an election, and it waits for that timeout to elapse before starting the next election; this reduces the likelihood of another split vote in the new election.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&#34;log-replication&#34;&gt;Log Replication:&lt;/h4&gt;

&lt;p&gt;The client requests are assumed to be write-only for now. Each request consists of a command to be executed ideally by the replicated state machines of all the servers. When a leader gets a client request, it adds it to its own log as a new entry. Each entry in a log:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains the client specified command&lt;/li&gt;
&lt;li&gt;Has an index to identify the position of entry in the log (the index starts from 1)&lt;/li&gt;
&lt;li&gt;Has a &lt;strong&gt;term number&lt;/strong&gt; to logically identify when the entry was written&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It needs to replicate the entry to all the follower nodes in order to keep the logs consistent. The leader issues AppendEntries RPCs to all other servers in parallel. The leader retries this until all followers safely replicate the new entry.&lt;/p&gt;

&lt;p&gt;When the entry is replicated to a majority of servers by the leader that created it, it is considered committed. All the previous entries, including those created by earlier leaders, are also considered committed. The leader executes the entry once it is committed and returns the result to the client.&lt;/p&gt;

&lt;p&gt;The leader maintains the highest index it knows to be committed in its log and sends it out with the AppendEntries RPCs to its followers. Once the followers find out that the entry has been committed, it applies the entry to its state machine in order.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Raft maintains the following properties, which together constitute the Log Matching Property&amp;gt; • If two entries in different logs have the same index and term, then they store the same command.&amp;gt; • If two entries in different logs have the same index and term, then the logs are identical in all preceding entries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When sending an AppendEntries RPC, the leader includes the &lt;strong&gt;term number&lt;/strong&gt; and index of the entry that immediately precedes the new entry. If the follower cannot find a match for this entry in its own log, it rejects the request to append the new entry.&lt;/p&gt;

&lt;p&gt;This consistency check lets the leader conclude that whenever AppendEntries returns successfully from a follower, they have identical logs until the index included in the RPC.&lt;/p&gt;

&lt;p&gt;But the logs of leaders and followers may become inconsistent in the face of leader crashes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In Raft, the leader handles inconsistencies by forcing the followers’ logs to duplicate its own. This means that conflicting entries in follower logs will be overwritten with entries from the leader’s log.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The leader tries to find the last index where its log matches that of the follower, deletes extra entries if any, and adds the new ones.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The leader maintains a nextIndex for each follower, which is the index of the next log entry the leader will send to that follower. When a leader first comes to power, it initializes all nextIndex values to the index just after the last one in its log.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whenever AppendRPC returns with a failure for a follower, the leader decrements the &lt;strong&gt;nextIndex&lt;/strong&gt; and issues another AppendEntries RPC. Eventually, nextIndex will reach a value where the logs converge. AppendEntries will succeed when this happens and it can remove extraneous entries (if any) and add new ones from the leaders log (if any). Hence, a successful AppendEntries from a follower guarantees that the leader’s log is consistent with it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With this mechanism, a leader does not need to take any special actions to restore log consistency when it comes to power. It just begins normal operation, and the logs automatically converge in response to failures of the Append-Entries consistency check. A leader never overwrites or deletes entries in its own log.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;safety&#34;&gt;Safety:&lt;/h4&gt;

&lt;p&gt;Raft makes sure that the leader for a term has committed entries from all previous terms in its log. This is needed to ensure that all logs are consistent and the state machines execute the same set of commands.&lt;/p&gt;

&lt;p&gt;During a leader election, the RequestVote RPC includes information about the candidate’s log. If the voter finds that its log it more up-to-date that the candidate, it doesn’t vote for it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Raft determines which of two logs is more up-to-date by comparing the index and term of the last entries in the logs. If the logs have last entries with different terms, then the log with the later term is more up-to-date. If the logs end with the same term, then whichever log is longer is more up-to-date.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;cluster-membership&#34;&gt;Cluster membership:&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;For the configuration change mechanism to be safe, there must be no point during the transition where it is possible for two leaders to be elected for the same term. Unfortunately, any approach where servers switch directly from the old configuration to the new configuration is unsafe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Raft uses a two-phase approach for altering cluster membership. First, it switches to an intermediate configuration called &lt;strong&gt;joint consensus.&lt;/strong&gt; Then, once that is committed, it switches over to the new configuration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The joint consensus allows individual servers to transition between configurations at different times without compromising safety. Furthermore, joint consensus allows the cluster to continue servicing client requests throughout the configuration change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Joint consensus combines the new and old configurations as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log entries are replicated to all servers in both the configurations&lt;/li&gt;
&lt;li&gt;Any server from old or new can become the leader&lt;/li&gt;
&lt;li&gt;Agreement requires separate majorities from both old and new configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a leader receives a configuration change message, it stores and replicates the entry for join consensus &lt;em&gt;C&amp;lt;old, new&amp;gt;&lt;/em&gt;. A server always uses the latest configuration in its log to make decisions even if it isn’t committed. When joint consensus is committed, only servers with &lt;em&gt;C&amp;lt;old, new&amp;gt;&lt;/em&gt; in their logs can become leaders.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is now safe for the leader to create a log entry describing C&amp;lt;new&amp;gt; and replicate it to the cluster. Again, this configuration will take effect on each server as soon as it is seen. When the new configuration has been committed under the rules of C&amp;lt;new&amp;gt;, the old configuration is irrelevant and servers not in the new configuration can be shut down.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A fantastic visualization of how Raft works can be found &lt;a href=&#34;http://thesecretlivesofdata.com/raft/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;More material such as talks, presentations, related papers and open-source implementations can be found &lt;a href=&#34;https://raft.github.io/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have dug only into the details of the basic algorithm that make up Raft and the safety guarantees it provides. The paper contains lot more details and it is super approachable as the primary goal of the authors was understandability. I definitely recommend you read it even if you’ve never read any other paper before.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Viewstamped Replication Revisited</title>
      <link>http://readings.shubheksha.com/papers/vr-revisited/</link>
      <pubDate>Mon, 02 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/vr-revisited/</guid>
      <description>

&lt;p&gt;This article will distill the contents of the academic paper &lt;a href=&#34;http://pmg.csail.mit.edu/papers/vr-revisited.pdf&#34;&gt;Viewstamped Replication Revisited&lt;/a&gt; by Barbara Liskov and James Cowling. All quotations are taken from that paper.&lt;/p&gt;

&lt;p&gt;It presents an updated explanation of Viewstamped Replication, a replication technique that handles failures in which nodes crash. It describes how client requests are handled, how the group reorganizes when a replica fails, and how a failed replica is able to rejoin the group.&lt;/p&gt;

&lt;h4 id=&#34;introduction&#34;&gt;Introduction&lt;/h4&gt;

&lt;p&gt;The Viewstamped Replication protocol, referred to as VR, is used for replicated services that run on many nodes known as replicas. VR uses state machine replication: it maintains state and makes it accessible to the clients consuming that service.&lt;/p&gt;

&lt;p&gt;Some features of VR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VR is primarily a replication protocol, but it provides consensus too.&lt;/li&gt;
&lt;li&gt;VR doesn’t use any disk I/O — it uses replicated state for persistence.&lt;/li&gt;
&lt;li&gt;VR deals only with crash failures: a node is either functioning or it completely stops.&lt;/li&gt;
&lt;li&gt;VR works in an asynchronous network like the internet where nothing can be concluded about a message that doesn’t arrive. It may be lost, delivered out of order, or delivered many times.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;replica-groups&#34;&gt;Replica Groups&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;VR ensures reliability and availability when no more than a threshold of f replicas are faulty. It does this by using replica groups of size 2f + 1; this is the minimal number of replicas in an asynchronous network under the crash failure model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can provide a simple proof for the above statement: in a system with f crashed nodes, we need at least the majority of f+1 nodes that can mutually agree to keep the system functioning.&lt;/p&gt;

&lt;p&gt;A group of f+1 replicas is often known as a &lt;strong&gt;quorum.&lt;/strong&gt; The protocol needs the quorum intersection property to be true to work correctly. This property states that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The quorum of replicas that processes a particular step of the protocol must have a non-empty intersection with the group of replicas available to handle the next step, since this way we can ensure that at each next step at least one participant knows what happened in the previous step.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;architecture&#34;&gt;Architecture:&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/569/1*88uNSrwWtrYWlRKqBJXBJg.png&#34; alt=&#34;&#34; /&gt;&lt;figcaption align=&#34;center&#34;&gt;VR architecture&lt;/figcaption&gt;&lt;/p&gt;

&lt;p&gt;The architecture of VR is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user code is run on client machines on top of a VR proxy.&lt;/li&gt;
&lt;li&gt;The proxy communicates with the replicas to carry out the operations requested by the client. It returns the computed results from the replicas back to the client.&lt;/li&gt;
&lt;li&gt;The VR code on the side of the replicas accepts client requests from the proxy, executes the protocol, and executes the request by making an up-call to the service code.&lt;/li&gt;
&lt;li&gt;The service code returns the result to the VR code which in turn sends a message to the client proxy that requested the operation.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&#34;overview&#34;&gt;&lt;strong&gt;Overview&lt;/strong&gt;&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;The challenge for the replication protocol is to ensure that operations execute in the same order at all replicas in spite of concurrent requests from clients and in spite of failures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If all the replicas should end in the same state, it is important that the above condition is met.&lt;/p&gt;

&lt;p&gt;VR deals with the replicas as follows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primary&lt;/strong&gt; : Decides the order in which the operations will be executed&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secondary:&lt;/strong&gt; Carries out the operations in the same order as selected by the primary&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the primary fails?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VR allows different replicas to assume the role of primary if it fails over time.&lt;/li&gt;
&lt;li&gt;The system moves through a series of &lt;strong&gt;views&lt;/strong&gt;. In each view, one replica assumes the role of primary.&lt;/li&gt;
&lt;li&gt;The other replicas watch the primary. If it appears to be faulty, then they carry out a &lt;strong&gt;view-change&lt;/strong&gt; to select a new primary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We consider the following three scenarios of the VR protocol:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normal case processing of user requests&lt;/li&gt;
&lt;li&gt;View changes to select a new primary&lt;/li&gt;
&lt;li&gt;Recovery of a failed replica so that it can rejoin the group&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;vr-protocol&#34;&gt;VR protocol&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/444/1*xNjTLZXpPq_n9CYiXAfD2w.png&#34; alt=&#34;&#34; /&gt;&lt;figcaption align=&#34;center&#34;&gt;State of VR at a replica&lt;/figcaption&gt;&lt;/p&gt;

&lt;p&gt;The state maintained by each replica is presented in the figure above. Some points to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The identity of the primary isn’t stored but computed using the view number and the configuration.&lt;/li&gt;
&lt;li&gt;The replica with the smallest IP is replica 1 and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The client side proxy also maintains some state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It records the configuration.&lt;/li&gt;
&lt;li&gt;It records the current view number to track the primary.&lt;/li&gt;
&lt;li&gt;It has a client id and an incrementing client request number.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;normal-operation&#34;&gt;Normal Operation&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Replicas participate in processing of client requests only when their status is normal.&lt;/li&gt;
&lt;li&gt;Each message sent contains the sender’s view number. Replicas process only those requests which have a view number that matches what they know. If the sender replica is ahead, it drops the message. If it’s behind, it performs a state transfer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/601/1*M4kfj1UbzM0f5_Zf2RpCYg.png&#34; alt=&#34;&#34; /&gt;&lt;figcaption align=&#34;center&#34;&gt;Normal mode operation&lt;/figcaption&gt;&lt;/p&gt;

&lt;p&gt;The normal operation of VR can be broken down into the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The client sends a REQUEST message to the primary asking it to perform some &lt;strong&gt;operation&lt;/strong&gt; , passing it the &lt;strong&gt;client-id&lt;/strong&gt; and the &lt;strong&gt;request number&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The primary cross-checks the info present in the client table. If the request number is smaller than the one present in the table, it discards it. It re-sends the response if the request was the most recently executed one.&lt;/li&gt;
&lt;li&gt;The primary increases the &lt;strong&gt;op-number&lt;/strong&gt; , appends the request to its log, and updates the client table with the new request number. It sends a PREPARE message to the replicas with the current view-number, the operation-number, the client’s message, and the &lt;strong&gt;commit-number&lt;/strong&gt; (the operation number of the most recently committed operation).&lt;/li&gt;
&lt;li&gt;The replicas won’t accept a message with an &lt;strong&gt;op-number&lt;/strong&gt; until they have all operations preceding it. They use state transfer to catch up if required. Then they add the operation to their log, update the client table, and send a PREPAREOK message to the primary. This message indicates that the operation, including all the preceding ones, has been prepared successfully.&lt;/li&gt;
&lt;li&gt;The primary waits for a response from &lt;em&gt;f&lt;/em&gt; replicas before committing the operation. It increments the &lt;strong&gt;commit-number&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt; After making sure all operations preceding the current one have been executed, it makes an up-call to the service code to execute the current operation. A REPLY message is sent to the client containing the view-number, request-number, and the result of the up-call.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usually the PREPARE message is used to inform the backup replicas of the committed operations. It can also do so by sending a COMMIT message.&lt;/p&gt;

&lt;p&gt;To execute a request, a backup has to make sure that the operation is present in its log and that all the previous operations have been executed. Then it executes the said operation, increments its &lt;strong&gt;commit-number&lt;/strong&gt; , and updates the client’s entry in the client-table. But it doesn’t send a reply to the client, as the primary has already done that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a client doesn’t receive a timely response to a request, it re-sends the request to all replicas. This way if the group has moved to a later view, its message will reach the new primary. Backups ignore client requests; only the primary processes them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;view-change-operation&#34;&gt;View change operation&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Backups monitor the primary: they expect to hear from it regularly. Normally the primary is sending PREPARE messages, but if it is idle (due to no requests) it sends COMMIT messages instead. If a timeout expires without a communication from the primary, the replicas carry out a view change to switch to a new primary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is no leader election in this protocol. The primary is selected in a round robin fashion. Each member has a unique IP address. The next primary is the backup replica with the smallest IP that is functioning. Each number in the group is already aware of who is expected to be the next primary.&lt;/p&gt;

&lt;p&gt;Every executed operation at the replicas must survive the view change in the order specified when it was executed. The up-call is carried out at the primary only after it receives &lt;em&gt;f&lt;/em&gt; PREPAREOK messages&lt;em&gt;.&lt;/em&gt; Thus the operation has been recorded in the logs of at least f+1 replicas (the old primary and f replicas).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Therefore the view change protocol obtains information from the logs of at least f + 1 replicas. This is sufficient to ensure that all committed operations will be known, since each must be recorded in at least one of these logs; here we are relying on the quorum intersection property. Operations that had not committed might also survive, but this is not a problem: it is beneficial to have as many operations survive as possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;A replica that notices the need for a view change advances its &lt;strong&gt;view-number&lt;/strong&gt; , sets its status to &lt;strong&gt;view-change&lt;/strong&gt; , and sends a START-VIEW-CHANGE message. A replica identifies the need for a view change based on its own timer, or because it receives a START-VIEW-CHANGE or a DO-VIEW-CHANGE from others with a &lt;strong&gt;view-number&lt;/strong&gt; higher than its own.&lt;/li&gt;
&lt;li&gt;When a replica receives &lt;em&gt;f&lt;/em&gt; START-VIEW-CHANGE messages for its view-number, it sends a DO-VIEW-CHANGE to the node expected to be the primary. The messages contain the state of the replica: the log, most recent operation-number and commit-number, and the number of the last view in which its status was normal.&lt;/li&gt;
&lt;li&gt;The new primary waits to receive f+1 DO-VIEW-CHANGE messages from the replicas (including itself). Then it updates its state to the most recent based on the info from replicas (see paper for all rules). It sets its number as the &lt;strong&gt;view-number&lt;/strong&gt; in the messages, and changes its &lt;strong&gt;status&lt;/strong&gt; to normal. It informs all other replicas by sending a STARTVIEW message with the most recent state including the new log, &lt;strong&gt;commit-number&lt;/strong&gt; and &lt;strong&gt;op-number&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;The primary can now accept client requests. It executes any committed operations and sends the replies to clients.&lt;/li&gt;
&lt;li&gt;When the replicas receive a STARTVIEW message, they update their state based on the message. They send PREPAREOK messages for all uncommitted operations present in their log after the update. They execute these operations to to be in sync with the primary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To make the view change operation more efficient, the paper describes the following approach:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The protocol described has a small number of steps, but big messages. We can make these messages smaller, but if we do, there is always a chance that more messages will be required. A reasonable way to get good behavior most of the time is for replicas to include a suffix of their log in their DO-VIEW-CHANGE messages. The amount sent can be small since the most likely case is that the new primary is up to date. Therefore sending the latest log entry, or perhaps the latest two entries, should be sufficient. Occasionally, this information won’t be enough; in this case the primary can ask for more information, and it might even need to first use application state to bring itself up to date.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;recovery&#34;&gt;Recovery&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;When a replica recovers after a crash it cannot participate in request processing and view changes until it has a state at least as recent as when it failed. If it could participate sooner than this, the system can fail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The replica should not “forget” anything it has already done. One way to ensure this is to persist the state on disk — but this will slow down the whole system. This isn’t necessary in VR because the state is persisted at other replicas. It can be obtained by using a recovery protocol provided that the replicas are failure independent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a node comes back up after a crash it sets its status to recovering and carries out the recovery protocol. While a replica’s status is recovering it does not participate in either the request processing protocol or the view change protocol.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The recovery protocol is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The recovering replica sends a RECOVERY message to all other replicas with a nonce.&lt;/li&gt;
&lt;li&gt;Only if the replica’s status is normal does it reply to the recovering replica with a RECOVERY-RESPONSE message. This message contains its view number and the nonce it received. If it’s the primary, it also sends its log, op-number, and commit-number.&lt;/li&gt;

&lt;li&gt;&lt;p&gt;When the replica has received f+1 RECOVERY-RESPONSE messages, including one from the primary, it updates its state and changes its status to normal.
&amp;gt; The protocol uses the nonce to ensure that the recovering replica accepts only RECOVERY-RESPONSE messages that are for this recovery and not an earlier one.&lt;/p&gt;

&lt;h4 id=&#34;reconfiguration&#34;&gt;Reconfiguration&lt;/h4&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reconfiguration deals with epochs. The epoch represents the group of replicas processing client requests. If the threshold for failures, f, is adjusted, the system can either add or remove replicas and transition to a new epoch. It keeps track of epochs through the &lt;strong&gt;epoch-number&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Another status, namely transitioning, is used to signify that a system is moving between epochs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The approach to handling reconfiguration is as follows. A reconfiguration is triggered by a special client request. This request is run through the normal case protocol by the old group. When the request commits, the system moves to a new epoch, in which responsibility for processing client requests shifts to the new group. However, the new group cannot process client requests until its replicas are up to date: the new replicas must know all operations that committed in the previous epoch. To get up to date they transfer state from the old replicas, which do not shut down until the state transfer is complete.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The VR sub protocols need to be modified to deal with epochs. A replica doesn’t accept messages from an older epoch compared to what it knows, such as those with an older &lt;strong&gt;epoch-number&lt;/strong&gt;. It informs the sender about the new epoch.&lt;/p&gt;

&lt;p&gt;During a view-change, the primary cannot accept client requests when the system is transitioning between epochs. It does this by checking if the topmost request in its log is a RECONFIGURATION request. A recovering replica in an older epoch is informed of the epoch if it is part of the new epoch or if it shuts down.&lt;/p&gt;

&lt;p&gt;The issue that comes to mind is that the client requests can’t be served while the system is moving to a new epoch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The old group stops accepting client requests the moment the primary of the old group receives the RECONFIGURATION request; the new group can start processing client requests only when at least f + 1 new replicas have completed state transfer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This can be dealt with by “warming up” the nodes before reconfiguration happens. The nodes can be brought up-to-date using state transfer while the old group continues to reply to client requests. This reduces the delay caused during reconfiguration.&lt;/p&gt;

&lt;p&gt;This paper has presented an improved version of Viewstamped Replication, a protocol used to build replicated systems that are able to tolerate crash failures. The protocol does not require any disk writes as client requests are processed or even during view changes, yet it allows nodes to recover from failures and rejoin the group.&lt;/p&gt;

&lt;p&gt;The paper also presents a protocol to allow for reconfigurations that change the members of the replica group, and even the failure threshold. A reconfiguration technique is necessary for the protocol to be deployed in practice since the systems of interest are typically long lived.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some Constraints &amp; Trade-offs In The Design of Network Communications</title>
      <link>http://readings.shubheksha.com/papers/constraints-and-trade-offs-network-comm/</link>
      <pubDate>Tue, 26 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/constraints-and-trade-offs-network-comm/</guid>
      <description>

&lt;p&gt;This post distills the content presented in the paper &lt;a href=&#34;http://dsg.tuwien.ac.at/linksites/teaching/courses/AdvancedDistributedSystems/download/1975_Akkoyunlu,%20Ekanadham,%20Huber_Some%20constraints%20and%20tradeoffs%20in%20the%20design%20of%20network%20communications.pdf&#34;&gt;&lt;strong&gt;“Some Constraints &amp;amp; Trade-offs In The Design of Network Communications”&lt;/strong&gt;&lt;/a&gt; published in 1975 by E. A. Akkoyunlu et al.&lt;/p&gt;

&lt;p&gt;This paper focuses on the inclusion of Inter Process Communication (IPC) primitives and the consequences of doing so. It explores, in particular, the time-out and the insertion property feature described in detail below with respect to distributed systems of sequential processes without system buffering &amp;amp; interrupts.&lt;/p&gt;

&lt;p&gt;It also touches upon the two generals problem which states that it’s impossible for two processes to agree on a decision over an unreliable network.&lt;/p&gt;

&lt;h3 id=&#34;introduction&#34;&gt;Introduction:&lt;/h3&gt;

&lt;p&gt;The design of an Inter Process Communication Mechanism (IPCM) can be described by stating the behavior of the system &amp;amp; the required services. The features to be included in the IPCM are very critical as they might be interdependent, hence the design process should begin with a detailed spec. This involves thorough understanding of the consequences of each decision.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The major aim of the paper is to point out the interdependence of the features to be incorporated in the system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paper states that at times the incompatibility between features is visible from the start. Yet, sometimes two features which seem completely unrelated end up affecting each other significantly. If the trade-offs involved aren’t explored at the beginning, it might not be possible to include desirable features. Trying to accommodate conflicting features results into messy code at the cost of elegance.&lt;/p&gt;

&lt;h4 id=&#34;intermediate-processes&#34;&gt;Intermediate Processes:&lt;/h4&gt;

&lt;p&gt;Let’s suppose a system doesn’t allow indirect communication between processes that cannot establish a connection. The users just care about the logical sender and receiver of the messages: they don’t care what path the messages take or how many processes they travel through to reach their final destination. In such a situation, intermediate processes come to our rescue. They’re not a part of the IPCM but are inserted between two processes that can’t communicate directly through a directory or broker process when the connection is set up. They’re the only ones aware of the indirect nature of communication between the processes.&lt;/p&gt;

&lt;h3 id=&#34;centralized-vs-distributed-systems&#34;&gt;Centralized vs Distributed Systems:&lt;/h3&gt;

&lt;h4 id=&#34;centralized-communication-facility&#34;&gt;Centralized Communication Facility&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Has a single agent which is able to maintain all state information related to the communication happening in the system&lt;/li&gt;
&lt;li&gt;The agent can also change the state of the system in a well-defined manner&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, if we consider the IPCM to be the centralized agent, it’ll be responsible for matching the SEND &amp;amp; RECEIVE requests of two processes, transferring data between their buffers and relaying appropriate status to both.&lt;/p&gt;

&lt;h4 id=&#34;distributed-communication-facility&#34;&gt;Distributed Communication Facility&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;No single agent has the complete state information at any time&lt;/li&gt;
&lt;li&gt;The IPCM is made of several individual components which coordinate, exchange and work with parts of state information they possess.&lt;/li&gt;
&lt;li&gt;A global change can take a considerable amount of time&lt;/li&gt;
&lt;li&gt;If one of the components crashes, the activity of other components still interests us&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/617/1*1xL5XULHeYCPNkGwBSA3jQ.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Figure 1, P1 and P2 are the two communicating processes on different machines over a network with their own IPCMs and P is the interface which enables this, with parts that lie on both machines. P handles the details of the network lines.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If one machine or a communication link crashes, we want the surviving IPCM’s to continue their operation. At least one component should detect a failure and be able to communicate. (In the case of a communication link failure, both ends must know.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Case 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Distributed communication can also happen on the same machine given that there are one or more intermediate processes taking part in the system. In that case, P, P1 &amp;amp; P2 will be processes on the same system with identical IPCMs. P is an intermediate processes which facilitates the communication between P1 &amp;amp; P2.&lt;/p&gt;

&lt;p&gt;Transactions between P1 &amp;amp; P2 consist of two steps: P1 to P and P to P2. Normally, the status returned to P1 would reflect the result of the P1 to P transfer, but P1 is interested in the status of the over all transaction from P1 to P2. One way to deal with this is a &lt;strong&gt;delayed status return&lt;/strong&gt;. The status isn’t sent to the sender immediately after the transaction occurs but only when the sender issues a SEND STATUS primitive. In the example above, after receiving the message from P1, P further sends it to P2, doesn’t send any status to P1 &amp;amp; waits to receive a status from P2. When it receives the appropriate status from P2, it relays it to P1 using the SEND STATUS primitive.&lt;/p&gt;

&lt;h4 id=&#34;special-cases-of-distributed-facility&#34;&gt;Special Cases of Distributed Facility&lt;/h4&gt;

&lt;p&gt;This section starts out by stating some facts and reasoning around them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FACT 0: A perfectly reliable distributed system can be made to behave as a centralized system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Theoretically, this is possible if:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The state of different components of the system is known at any given time&lt;/li&gt;
&lt;li&gt;After every transaction, the status is relayed properly between the processes through their IPCMs using reliable communication.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, this isn’t possible in practice because we don’t have a perfect reliable network. Hence, the more realistic version of the above fact is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FACT I: A distributed IPCM can be made to simulate a centralized system provided that:&amp;gt; 1. The overall system remains connected at all times, and&amp;gt; 2. When a communication link fails, the component IPCM’s that are connected to it know about it, and&amp;gt; 3. The mean time between two consecutive failures is large compared to the mean transaction time across the network.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paper states that if the above conditions are met, we can establish communication links that are reliable enough to simulate a centralized systems because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There is always a path from the sender to the receiver&lt;/li&gt;
&lt;li&gt;Only one copy of an undelivered message will be retained by the system in case of a failure due to link failure detection. Hence a message cannot be lost if undelivered and will be removed from the system when delivered.&lt;/li&gt;
&lt;li&gt;A routing strategy and a bound on the failure rate ensures that a message moving around in a subset of nodes will eventually get out in finite time if the target node isn’t present in the subset.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The cases described above are special cases because they make a lot of assumptions, use inefficient algorithms and don’t take into account network partitions leading to disconnected components.&lt;/p&gt;

&lt;h3 id=&#34;status-in-distributed-systems&#34;&gt;Status in Distributed Systems&lt;/h3&gt;

&lt;h4 id=&#34;complete-status&#34;&gt;Complete Status&lt;/h4&gt;

&lt;p&gt;A complete status is one that relays the final outcome of the message, i.e., whether it reached its destination.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FACT 2: In an arbitrary distributed facility, it is impossible to provide complete status.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/656/1*c_Y_LRv7qg85Zc-8ySHsow.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Assume that a system is partitioned into two disjoint networks, leaving the IPCMs disconnected. Now, if IPCM1 was awaiting a status from IPCM2, there is no way to get it and relay the result to P1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider figure 2, if there isn’t a reliable failure detection mechanism present in the system and IPCM2 sends a status message to IPCM1, then it can never be sure it reached or not without an acknowledgement. This leads to an infinite exchange of messages.&lt;/p&gt;

&lt;h4 id=&#34;time-outs&#34;&gt;&lt;strong&gt;Time-outs&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;Time-outs are required because the system has finite resources and can’t afford to be deadlocked forever. The paper states that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FACT 3: In a distributed system with timeouts, it is impossible to provide complete status (even if the system is absolutely reliable).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/632/1*88KSoj8CL1KbcdAO1D64ZQ.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;In figure 3, P1 is trying to send P2 a message through a chain of IPCMs.&lt;/p&gt;

&lt;p&gt;Suppose if I1 takes data from P1 but before it hears about the status of the transaction, P1’s request times out. IPCM1 has now knowledge about the final outcome whether the data was successfully received by P2. Whatever status it returns to P1, it may prove to be incorrect. Hence, it’s impossible to provide complete status in a distributed facility with time-outs.&lt;/p&gt;

&lt;h4 id=&#34;insertion-property&#34;&gt;Insertion Property&lt;/h4&gt;

&lt;p&gt;An IPCM has insertion property if we insert an intermediate process P between two processes P1 &amp;amp; P2 that wish to communicate such that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;P is invisible to both P1 &amp;amp; P2&lt;/li&gt;
&lt;li&gt;The status relayed to P1 &amp;amp; P2 is the same they’d get if directly connected
&amp;gt; FACT 4: In a distributed system with timeouts, the insertion property can be possessed only if the IPCM withholds some status information that is known to it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Delayed status is required to fulfill the insertion property. Consider that the message is sent from P1 to P2. What happens if P receives P1’s message, it goes into await-status state but it times out before P could learn about the status?&lt;/p&gt;

&lt;p&gt;We can’t tell P1 the final outcome of the exchange as that’s not available yet. We also can’t let P know that it’s in await-status state because that would mean that the message was received by someone. It’s also not possible that P2 never received the data because such a situation cannot arise if P1 &amp;amp; P2 are directly connected &amp;amp; hence violates the insertion property.&lt;/p&gt;

&lt;p&gt;The solution to this is to provide an ambiguous status to P1, one that is as likely to be possible if the two processes were connected directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thus, a deliberate suppression of what happened is introduced by providing the same status to cover a time-out which occurs while awaiting status and, say, a transmission error.&lt;/p&gt;

&lt;h3 id=&#34;logical-physical-messages&#34;&gt;Logical &amp;amp; Physical Messages&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;p&gt;The basic function of an IPCM is the transfer and synchronization of data between two processes. This may happen by dividing the physical messages originally sent by the sender process as a part of a single operation into smaller messages, also known as logical message for the ease of transfer.&lt;/p&gt;

&lt;h4 id=&#34;buffer-size-considerations&#34;&gt;Buffer Size Considerations&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/1006/1*Earu7g36OncjTNW3tX5Iiw.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;As depicted in figure 5, if a buffer mismatch arises, we can take the following approaches to fix it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define a system-wide buffer size. This is extremely restrictive, especially within a network of heterogenous systems&lt;/li&gt;
&lt;li&gt;Satisfy the request with the small buffer size &amp;amp; inform both the processes involved what happened. This approach requires that the processes are aware of the low level details of the communication.&lt;/li&gt;
&lt;li&gt;Allowing partial transfers. In this approach, only the process that issued the smaller request (50 words) is woken up. All other processes remain asleep awaiting further transfers. If the receiver’s buffer isn’t full, an EOM (End Of Message) indicator is required to wake it up.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&#34;partial-transfers-and-well-known-ports&#34;&gt;Partial Transfers and Well-Known Ports&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/830/1*KRomNePcfLVotyKvmtFczg.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;In figure 6, a service process using a well-known port is accepting requests for sever user processes, P1…Pn. If P1 sends a message to the service process that isn’t complete and doesn’t fill its buffer, we need to consider the following situations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The well-known port is reserved for P1. No other process can communicate with the service process using it till P1 is done.&lt;/li&gt;
&lt;li&gt;When the service process times out while P1 is preparing to send the second and final part of the message, we need to handle it without informing P1 that the first part has been ignored. P1 isn’t listening for incoming messages from the service process.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since none of these problems arise without partial transfers, one solution is to ban them altogether. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the approach taken in ARPANET where the communication to well known ports are restricted to short, complete messages which are used to setup a separate connection for subsequent communication.&lt;/p&gt;

&lt;h4 id=&#34;buffer-processes&#34;&gt;Buffer Processes&lt;/h4&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/906/1*wNbOeOWpJCUD4vGdMiy_vw.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;This solution is modeled around the creation of dynamic processes.&lt;/p&gt;

&lt;p&gt;Whenever P1 wishes to transfer data to the service process, a new process S1 is created and receives messages from P1 till the logical message is completed, sleeping as and when required. Then it sends the complete physical message to the service process with EOM flag set. Thus no partial transfers happen between S1 and the service process, they’re all filtered out before that.&lt;/p&gt;

&lt;p&gt;However, this kind of a solution isn’t possible with well-known ports. S1 is inserted between P1 and the service process when the connection is initiailized. However, in the case of well-known ports, no initialization takes place.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In discussing status returned to the users, we have indicated how the presence of certain other features limits the information that can be provided.&amp;gt; In fact, we have shown situations in which uncertain status had to be returned, providing almost no information as to the outcome of the transaction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even though the inclusion of insertion property complicates things, it is beneficial to use the weaker version of it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Finally, we list a set of features which may be combined in a working IPCM:&amp;gt; (1) Time-outs&amp;gt; (2) Weak insertion property and partial transfer&amp;gt; (3) Buffer processes to allow&amp;gt; (4) Well-known ports — with appropriate methods to deal with partial transfers to them.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title>A Note on Distributed Systems</title>
      <link>http://readings.shubheksha.com/papers/a-note-on-distributed-systems/</link>
      <pubDate>Mon, 18 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>http://readings.shubheksha.com/papers/a-note-on-distributed-systems/</guid>
      <description>

&lt;h3 id=&#34;a-note-on-distributed-systems-a-summary&#34;&gt;A Note on Distributed Systems: A Summary&lt;/h3&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn-images-1.medium.com/max/1024/1*tYxWuyksovxA1Thu8PggPQ.jpeg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;This post distills the material presented in the paper titled &lt;a href=&#34;http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.7628&#34;&gt;&lt;strong&gt;“A Note on Distributed Systems”&lt;/strong&gt;&lt;/a&gt; published in 1994 by Jim Waldo et al.&lt;/p&gt;

&lt;p&gt;The paper presents the differences between local &amp;amp; distributed computing in the context of Object Oriented Programming, explaining why treating them to the be same is incorrect and leads to applications that aren’t robust or reliable.&lt;/p&gt;

&lt;h4 id=&#34;introduction&#34;&gt;Introduction:&lt;/h4&gt;

&lt;p&gt;The paper kicks off by stating that the current work in distributed systems is modeled around objects, more specifically, a &lt;strong&gt;unified view of objects:&lt;/strong&gt; objects are defined by their supported interfaces &amp;amp; the operations they support. Naturally, this can be extended to imply that objects in the same address space, or in a different address space on the same machine, or on a different machine all behave in a similar manner: their location is an implementation detail.&lt;/p&gt;

&lt;p&gt;Let’s define the most common terms in this paper:&lt;/p&gt;

&lt;h4 id=&#34;local-computing&#34;&gt;Local Computing:&lt;/h4&gt;

&lt;p&gt;It deals with programs that are confined to a single address space only.&lt;/p&gt;

&lt;h4 id=&#34;distributed-computing&#34;&gt;Distributed Computing:&lt;/h4&gt;

&lt;p&gt;It deals with programs that can make calls to objects in different address spaces either on the same machine or on a different machine.&lt;/p&gt;

&lt;h4 id=&#34;the-vision-of-unified-objects&#34;&gt;The Vision of Unified Objects:&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Implicit in this vision is that the system will be “objects all the way down”; that is, that all current invocations or calls for system services will be eventually converted into calls that might be to an object residing on some other machine. There is a single paradigm of object use and communication used no matter what the location of the object might be.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This refers to the assumption that all objects are defined only in terms of their interfaces. Their implementation which also includes location of the object (remote/local), is independent of their interfaces and is hidden from the programmer. Hence, as far the programmer is concerned, they write the same type of call for every object, whether local or remote and the system takes care of actually sending the message by figuring out the underlying mechanisms which aren’t visible to the programmer writing the application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The hard problems in distributed computing are not the problems of how to get things on and off the wire.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paper goes on to define what are the toughest challenges of building a distributed systems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Latency&lt;/li&gt;
&lt;li&gt;Memory Access&lt;/li&gt;
&lt;li&gt;Partial failure &amp;amp; concurrency&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ensuring reasonable performance while dealing with all of the above doesn’t make the life of the a distributed systems engineer any easier. Moreover, the lack of any central resource or state manager just adds on to the various challenges. Let’s observe each of these one by one.&lt;/p&gt;

&lt;h4 id=&#34;latency&#34;&gt;Latency:&lt;/h4&gt;

&lt;p&gt;This is perhaps the fundamental difference between local &amp;amp; distributed object invocation. The paper claims that a remote call is four to five times slower than a local call. If the design of a system fails to recognize this fundamental difference, it is bound to suffer from serious performance problems especially if it relies heavily on remote communication. A thorough understanding of the application being designed is required to decide which objects should be kept together and which can be placed remotely.&lt;/p&gt;

&lt;p&gt;If the goal is to unify the difference in latency, then we’ve two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rely on the hardware to get faster with time in order to eliminate the difference in effeciency&lt;/li&gt;
&lt;li&gt;Develop tools which allow us to visualize communication patterns between different objects and move them around as required. Since location is an implementation detail, this shouldn’t be too hard to achieve.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;memory&#34;&gt;Memory:&lt;/h4&gt;

&lt;p&gt;Another difference that’s very relevant to the design of distributed systems is the pattern of memory access between local and remote objects. A pointer in the local address space isn’t valid in a remote address space. We’re left with two choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The developer must be made aware of the difference between the access patterns&lt;/li&gt;
&lt;li&gt;The system handles all memory access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To unify the differences in access between local &amp;amp; remote access, we need to let the system handle all aspects of access to memory. There are several way to do that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Distributed shared memory&lt;/li&gt;
&lt;li&gt;Using the OOP paradigm, compose a system entirely of objects, i.e., dealing only with object references. The transfer of data between address spaces can be dealt with by marshalling &amp;amp; unmarshalling the data by the layer underneath. This approach, however, makes the use of address-space-relative pointers obsolete.
&amp;gt; The danger lies in promoting the myth that “remote access and local access are exactly the same” and not enforcing the myth. An underlying mechanism that does not unify all memory accesses while still promoting this myth is both misleading and prone to error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hence, it’s important for programmers to be made aware of the various differences between accessing local &amp;amp; remote objects so that they don’t get bitten by not knowing what’s happening under the covers.&lt;/p&gt;

&lt;h4 id=&#34;partial-failure-concurrency&#34;&gt;Partial failure &amp;amp; concurrency&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Partial failure is a central reality of distributed computing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paper argues that while both local &amp;amp; distributed systems are subject to failure, it’s a lot harder to discover what went wrong in case of distributed systems. For a local systems, either everything is shut down or there is some central authority which can detect what went wrong (the OS, for example).&lt;/p&gt;

&lt;p&gt;However, in the case of a distributed system, determining what has failed is extremely difficult since there is no global state or resource manager available to keep track of everything happening in and across the system, hence there is no way to inform other components which may be functioning correctly which ones have failed. Components in a distributed system fail independently.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A central problem in distributed computing is insuring that the state of the whole system is consistent after such a failure; this is a problem that simply does not occur in local computing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order for a system to withstand partial failure, it’s important that it deals with indeterminacy and the objects react to it in a consistent manner. The interfaces must be able to state the cause of failure if possible and allow the reconstruction of a “reasonable state” in case the cause can’t be determined.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The question is not “can you make remote method invocation look like local method invocation?” but rather “what is the price of making remote method invocation identical to local method invocation?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two approaches come to mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Treat all interfaces &amp;amp; objects as local. The problem with this approach is that it doesn’t take into account the failure models associated with distributed systems and hence it’s indeterministic by nature.&lt;/li&gt;
&lt;li&gt;Treat all interfaces &amp;amp; objects as remote. The flaw with this approach is that over-complicates local computing. It adds on a ton of work for objects that are never accessed remotely.
&amp;gt; A better approach is to accept that there are irreconcilable differences between local and distributed computing, and to be conscious of those differences at all stages of the design and implementation of distributed applications.
* * *&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
  </channel>
</rss>