File: read-repair/read_repair.py

Date: 2026-05-29

Time: 09:04

read-repair/read_repair.py

Purpose

This file implements read repair, a consistency mechanism used in leaderless (Dynamo-style) replication systems. It's a reference implementation of the concept from Chapter 5 of *Designing Data-Intensive Applications* — specifically the technique where stale replicas are detected and corrected lazily during read operations, rather than requiring a synchronous protocol.

The module owns two responsibilities: (1) quorum-based reads and writes across a set of in-memory replicas, and (2) opportunistic repair of stale replicas discovered during reads.

Key Components

Replica

A single node holding a versioned key-value store (key -> (value, version)). The version acts as a simple scalar clock — put accepts a write only if the incoming version is >= the current version. This is the version-gating contract: stale writes are silently rejected (return False), which prevents read repair from regressing a replica that has already advanced.

ReadRepairStore

The coordinator that sits in front of N replicas and enforces quorum semantics.

InsufficientReplicasError

Raised when fewer replicas are available than the quorum requirement. This is the only custom exception — it gates both reads and writes.

Patterns

Quorum intersection. The constructor checks R + W > N and warns (via warnings.warn, not an exception) if the condition isn't met. This is the classic Dynamo quorum rule: if R + W > N, at least one replica in every read quorum must overlap with the write quorum, guaranteeing the read sees the latest write. The warning-not-error design lets you intentionally run with weaker consistency (e.g., R=1, W=1 for availability).

Lazy repair. Read repair only fixes the replicas that were *queried* during the read — not all replicas. This is the defining characteristic: it piggybacks consistency healing onto normal read traffic. Replicas that are never read stay stale until anti-entropy runs.

Version as scalar clock. Versions are simple incrementing integers, determined by the coordinator scanning all replicas for the current max. This avoids conflicts but assumes a single logical writer (no concurrent coordinators racing on version assignment). In a real system, vector clocks or hybrid logical clocks would replace this.

First-N selection. Both put and get pick the first N available replicas from the list. There's no randomization or load balancing — the selection is deterministic based on replica order. This is a simplification that makes behavior predictable for testing.

Dependencies

Imports: Only warnings from the standard library. No external dependencies — this is a self-contained simulation.

Imported by: testreadrepair.py and testertestread_repair.py, which exercise the quorum and repair behavior.

Flow

Write path

1. Filter to available replicas; raise if fewer than W.

2. Scan all replicas (including unavailable — note the code reads from self.replicas, not available) to find the max existing version for the key.

3. Assign newversion = maxversion + 1.

4. Write to the first W available replicas.

Read path

1. Filter to available replicas; raise if fewer than R.

2. Read the key from the first R available replicas.

3. Find the response with the highest version.

4. If any of the R replicas had a stale or missing value, push the best value to them (read repair).

5. Return the value, version, and repair metadata.

Anti-entropy path

1. Scan all available replicas for the max version.

2. Push that version to any available replica that's behind.

Invariants

Error Handling

Topics to Explore

Beliefs