CodeWithAbdessamad

Replication

Replication in Distributed Systems

Replication is the process of copying data across multiple nodes to ensure availability, consistency, fault tolerance, and scalability. It’s a fundamental technique in distributed systems that addresses challenges like network partitions, node failures, and high latency. Below, I explain three common replication models with practical examples and their trade-offs.


1. Leader-Follower Replication

How it works:

One node (the leader) handles all write operations, while followers replicate data from the leader. Reads can be served by any node (leader or follower), but writes are strictly controlled by the leader.

Key characteristics:

  • Simple implementation
  • Strong consistency (writes are immediately visible to all nodes)
  • Single point of failure (the leader)
  • Higher write latency (all writes go through the leader)

Real-world example:

A bank’s transaction system where the leader processes all deposits/withdrawals, and followers replicate the ledger.

Simple implementation:

<code class="language-python">class LeaderFollowerNode:
<p>    def <strong>init</strong>(self, node_id, followers):</p>
<p>        self.node<em>id = node</em>id</p>
<p>        self.followers = followers</p>
<p>        self.data = {}</p>

<p>    def write(self, key, value):</p>
<p>        self.data[key] = value</p>
<p>        print(f"Leader {self.node_id} wrote {key}={value}")</p>
<p>        for follower in self.followers:</p>
<p>            print(f"Replicating {key}={value} to {follower}")</p>

<p>    def read(self, key):</p>
<p>        return self.data.get(key)</p>

<h1>Create nodes</h1>
<p>leader = LeaderFollowerNode("bank<em>leader", ["bank</em>follower1", "bank_follower2"])</p>
<p>follower1 = LeaderFollowerNode("bank_follower1", [])</p>
<p>follower2 = LeaderFollowerNode("bank_follower2", [])</p>

<h1>Process a transaction</h1>
<p>leader.write("account_123", "1000")</p>
<p>print("Leader read:", leader.read("account_123"))  # Output: 1000</p>
<p>print("Follower1 read:", follower1.read("account_123"))  # Output: None</code>

When to use:

  • Small-scale systems where consistency is critical
  • Applications with low write volume
  • Systems needing simple operational overhead

2. Multi-Leader Replication

How it works:

Multiple leaders handle writes for different subsets of data (e.g., by key range, region, or topic). Each leader operates independently, but writes are coordinated via consensus protocols (like Raft or Paxos).

Key characteristics:

  • Improved fault tolerance (no single leader)
  • Better scalability (load distributed across leaders)
  • Potential for write conflicts (solved via consensus)
  • Higher complexity in implementation

Real-world example:

A global e-commerce platform where:

  • Leader US_Region handles US customer transactions
  • Leader EU_Region handles EU transactions
  • Both leaders replicate to a shared global database

Simple implementation:

<code class="language-python">class MultiLeaderNode:
<p>    def <strong>init</strong>(self, node<em>id, key</em>ranges):</p>
<p>        self.node<em>id = node</em>id</p>
<p>        self.key<em>ranges = key</em>ranges</p>
<p>        self.data = {}</p>

<p>    def write(self, key, value):</p>
<p>        if key in self.key_ranges:</p>
<p>            self.data[key] = value</p>
<p>            print(f"Leader {self.node_id} wrote {key}={value}")</p>

<h1>Create leaders</h1>
<p>us<em>leader = MultiLeaderNode("US</em>Region", ["US<em>1000", "US</em>9999"])</p>
<p>eu<em>leader = MultiLeaderNode("EU</em>Region", ["EU<em>1000", "EU</em>9999"])</p>

<h1>Process transactions</h1>
<p>us<em>leader.write("US</em>1234", "50.00")</p>
<p>eu<em>leader.write("EU</em>5678", "20.00")</code>

When to use:

  • Large-scale systems with geographically distributed users
  • Applications needing regional data isolation
  • Systems requiring high availability without a single point of failure

3. Leaderless Replication

How it works:

No designated leader. Writes are handled via quorum-based consensus (e.g., majority of nodes must agree on a write). All nodes participate equally in read/write operations.

Key characteristics:

  • Zero single points of failure
  • High fault tolerance (works even with network partitions)
  • Higher latency (requires consensus)
  • Complex implementation (needs consensus protocols)

Real-world example:

A decentralized social media platform where:

  • Every user node can write posts
  • A quorum of 2 out of 3 nodes must agree before a post is published

Simple implementation (using quorum):

<code class="language-python">class LeaderlessNode:
<p>    def <strong>init</strong>(self, node<em>id, other</em>nodes):</p>
<p>        self.node<em>id = node</em>id</p>
<p>        self.other<em>nodes = other</em>nodes</p>
<p>        self.data = {}</p>

<p>    def write(self, key, value):</p>
<p>        self.data[key] = value</p>
<p>        print(f"Node {self.node_id} wrote {key}={value}")</p>
<p>        # Simulate quorum (2 out of 3 nodes)</p>
<p>        target<em>nodes = [n for n in self.other</em>nodes if n != self.node_id]</p>
<p>        if len(target_nodes) >= 1:  # Quorum reached</p>
<p>            print(f"Quorum confirmed: {self.node<em>id} + {target</em>nodes[0]}")</p>

<h1>Create 3 nodes (quorum of 2)</h1>
<p>nodeA = LeaderlessNode("A", ["B", "C"])</p>
<p>nodeB = LeaderlessNode("B", ["A", "C"])</p>
<p>nodeC = LeaderlessNode("C", ["A", "B"])</p>

<h1>Process a write</h1>
<p>nodeA.write("post_1", "Hello world!")</code>

When to use:

  • Systems requiring high availability (e.g., blockchain, IoT)
  • Applications with unpredictable network conditions
  • Environments where leader failure is catastrophic

Key Trade-offs Summary

Model Best for Critical Trade-offs
Leader-Follower Small systems, strict consistency Single point of failure; high write latency
Multi-Leader Global apps, regional data isolation Complex coordination; potential write conflicts
Leaderless Decentralized systems, high fault tolerance High latency; complex consensus protocols

Which to Choose?

  • Start simple: Use Leader-Follower for small projects.
  • Scale horizontally: Adopt Multi-Leader as your system grows.
  • Prioritize resilience: Use Leaderless for critical systems where downtime is unacceptable.

đź’ˇ Pro Tip: In production, always add eventual consistency (e.g., via Kafka or Redis) to balance consistency and performance. Never skip testing failure scenarios!

Replication is about balancing your system’s needs—consistency, availability, and cost. The right model depends on your specific use case, but these three approaches cover 90% of real-world scenarios. Start small, validate with your data, and scale strategically.