Phase 1: Foundations

Distributed systems fundamentals & CAP theorem

Intermediate ~4 min read
Think of it this way A friendly analogy. Read this if the technical version feels dense. Show Hide

Imagine you want to build the coolest, biggest library ever, one that holds millions of books and can help everyone find what they need, all at once! Putting all those books and librarians into one giant building would be too crowded and slow. If that building closed, no one could get books. So, smart people created many smaller library branches spread out everywhere. Each branch holds some books, and they all work together as one big, helpful library system. If one branch is busy, another can help. If one branch has to close, others are still open. This way, more people get books quickly, and the system almost never completely shuts down.

When many branches work together, tricky situations pop up. How do they all stay on the same page? This is where three big ideas come in. First is Consistency. This means if a popular book is borrowed from Branch A, every other branch immediately knows it's gone. So if you ask at Branch B, they won't accidentally say it's available. Everyone sees the same, up-to-the-minute list. Second is Availability. This means no matter what, when you ask for a book, some branch can always respond. Even if they don't have your book, they can say "Sorry, try another branch." The library is always open for business.

The third big idea is Partition Tolerance. Imagine a bad storm cuts the internet between Branch A and Branch B. They can't talk anymore! This is a "network partition" – the system is split. Even if cut off, the system still needs to try and work. Branch A serves its local customers, and Branch B does the same. They can't just stop because a cable broke. The CAP theorem (Consistency, Availability, and Partition Tolerance) tells us you can only guarantee two out of these three properties at the same time when a network partition happens. For example, if you prioritize Availability and Partition Tolerance, Branch B might temporarily not know Branch A just lent out a popular book (sacrificing Consistency). Or, if Consistency and Availability are crucial, the whole system might pause if a branch gets cut off (sacrificing Partition Tolerance).

There's no single "best" choice; it depends on what's most important for that particular library or system. When you help build large digital services, like a website or a game many people play, understanding these trade-offs helps you decide how your system should behave when things get tricky. This means you can design robust and efficient systems, making smart choices about handling information and staying running, even when parts of them struggle to communicate.

Distributed systems involve multiple independent computers working together as a single, cohesive system. Their primary advantages for SREs are enhanced scalability (handling more users/data), improved fault tolerance (the system can continue operating even if some components fail), and higher availability. However, this architecture introduces significant complexity: managing concurrent operations, handling network latency, ensuring data consistency across multiple nodes, and dealing with partial failures (where some parts of the system are down, but others are not). Think of large-scale web services, microservice architectures, or distributed databases like Cassandra or MongoDB – these all rely on distributed system principles.

At the core of understanding these complexities is the CAP theorem, a fundamental principle for distributed data stores. CAP stands for Consistency, Availability, and Partition Tolerance. The theorem states that in a distributed system, you can only guarantee two out of these three properties at the same time, in the presence of a network partition. Let's quickly define them: Consistency (C) means all clients see the same data at the same time, regardless of which node they query. Availability (A) means every request receives a (non-error) response, even if it might not be the most recent data. Partition Tolerance (P) means the system continues to operate despite network failures that split the system into isolated groups of nodes (a "network partition").

As an SRE, network partitions are an inevitable reality in any sufficiently large-scale distributed system – they will happen. Because of this, you effectively must choose Partition Tolerance (P) as a given. This leaves you with a critical decision: design your system to be either CP (Consistent and Partition-tolerant) or AP (Available and Partition-tolerant). A CP system prioritizes strong consistency; if a partition occurs, it might make some nodes unavailable to ensure that any data returned is consistent. Examples include databases requiring ACID properties like PostgreSQL (when configured for strong consistency) or ZooKeeper. An AP system prioritizes availability; during a partition, it continues to serve requests, potentially returning stale data, and addresses consistency issues later (often via "eventual consistency"). Many NoSQL databases like Cassandra or DynamoDB are designed as AP systems. Your choice depends heavily on your application's tolerance for data staleness versus downtime.

Key Takeaways

  • Distributed systems offer scalability, fault tolerance, and availability, but introduce challenges like consistency.
  • The CAP theorem states that in a distributed system, you can only guarantee two of Consistency, Availability, or Partition Tolerance during a network partition.
  • Network partitions are unavoidable, so you must choose Partition Tolerance and then decide between Consistency (CP) or Availability (AP).
  • CP systems prioritize data accuracy by potentially sacrificing uptime during partitions (e.g., strong consistency databases).
  • AP systems prioritize uptime by potentially serving stale data during partitions and resolving consistency later (e.g., many NoSQL databases).

Code Example

python
# Conceptual state of a distributed system during a network partition.
# This illustrates the *problem* CAP theorem addresses, not its implementation.

# Imagine two nodes holding a copy of 'data'
node_1_data = {"item_id": 123, "quantity": 100}
node_2_data = {"item_id": 123, "quantity": 100}

print("Network partition detected.")

# Client writes to Node 1, Node 1 updates locally.
node_1_data["quantity"] = 90
print(f"Node 1 updated quantity to {node_1_data['quantity']}")

# A different client writes to Node 2, Node 2 updates locally.
node_2_data["quantity"] = 95
print(f"Node 2 updated quantity to {node_2_data['quantity']}")

# Now, if the partition resolves, we have conflicting states:
print(f"Node 1's quantity: {node_1_data['quantity']}")
print(f"Node 2's quantity: {node_2_data['quantity']}")

# An SRE choice:
# CP system: Node 2 would have rejected the write during partition (unavailable).
# AP system: Both writes succeed, and reconciliation is needed later (inconsistent).

How this code works

This code conceptually illustrates the fundamental problem that the CAP theorem addresses: maintaining data consistency in a distributed system during a network partition. It doesn't implement CAP theorem, but rather sets up the scenario where its trade-offs become necessary. The code simulates two independent servers, node_1_data and node_2_data, each holding a copy of an item's quantity. The initial setup with node_1_data and node_2_data as distinct Python dictionaries is crucial; it perfectly simulates physically separate servers holding independent data copies.

The simulation begins by declaring a "Network partition detected." Following this, the code shows isolated updates: one client modifies node_1_data['quantity'] to 90, while another client simultaneously updates node_2_data['quantity'] to 95. These operations happen without each node being aware of the other's changes, just as in a real partition. The final print statements reveal the core issue: after the partition, node_1_data['quantity'] and node_2_data['quantity'] hold different values for the same item_id, resulting in conflicting states. This divergence highlights the dilemma: a CP system would have prevented one write, prioritizing consistency, whereas an AP system allows both, leading to the need for later reconciliation.