How to Implement Read-After-Write Consistency Mock in Python

Simulate strong versus eventual read-after-write consistency with a primary and replica store, demonstrating the difference in data visibility over time.

Medium Python 3.9+ Aug 9, 2026 Database scaling & optimization 13 views 0 copies

Python code

42 lines
Python 3.9+
import time


class MockStorage:
    def __init__(self, write_delay=0.1):
        self.store = {}
        self.replica = {}
        self.write_delay = write_delay

    def write(self, key, value):
        # Write to primary storage immediately
        self.store[key] = value
        # Simulate async replication delay
        time.sleep(self.write_delay)
        # The write is now visible to reads
        self.replica[key] = value

    def read(self, key, strong_consistency=True):
        if strong_consistency:
            # Strong consistency: read from primary
            return self.store.get(key)
        else:
            # Eventual consistency: read from replica
            return self.replica.get(key)


if __name__ == "__main__":
    storage = MockStorage(write_delay=0.2)

    # Initial state
    print(f"Before write - eventual read: {storage.read('game_score', strong_consistency=False)}")

    # Write a value
    storage.write("game_score", 1500)

    # Immediately after write (but before replication delay)
    print(f"After write - strong read: {storage.read('game_score', strong_consistency=True)}")
    print(f"After write - eventual read (before replication): {storage.read('game_score', strong_consistency=False)}")

    # Wait for replication to complete
    time.sleep(0.1)
    print(f"After delay - eventual read: {storage.read('game_score', strong_consistency=False)}")

Output

stdout
Before write - eventual read: None
After write - strong read: 1500
After write - eventual read (before replication): None
After delay - eventual read: 1500

How it works

The MockStorage class models a simple database system with a primary store and an async replication delay. When write() is called, it writes to the primary immediately, then sleeps to simulate replication lag before updating the replica. The read() method offers two consistency levels: strong reads always hit the primary store, guaranteeing the latest value, while eventual reads hit the replica, which may lag briefly. This pattern demonstrates why strongly-consistent reads are safer for critical data but cost more in latency, while eventual consistency gives faster reads at the risk of seeing stale data. The timing in __main__ shows the window where strong and eventual reads disagree before replication finishes.

Common mistakes

  • Forgetting that `time.sleep` in write blocks the caller instead of truly replicating asynchronously in real systems
  • Using eventual reads for critical operations like order confirmation or balance checks
  • Not accounting for replication lag when testing data-dependent logic

Variations

  1. Use a separate thread or a queue to simulate truly asynchronous replication without blocking the writer
  2. Add a `replication_lag` counter or simulate out-of-order replication for more realistic behavior

Real-world use cases

  • Unit-testing application code that must behave correctly when reads see stale replicas after a write.
  • Demonstrating consistency models to teammates when designing a feature that reads user-profile updates.
  • Validating a caching layer's invalidation strategy where eventual consistency is acceptable for non-critical reads.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Database scaling & optimization

Related tutorials and quizzes for this topic.