How to implement read-your-writes sticky routing in Python
A mock StickyRouter class that routes all requests for the same key to the same node, ensuring read-after-write consistency.
Python code
32 linesimport random
class StickyRouter:
def __init__(self, nodes):
self.nodes = nodes
self.routes = {}
def route(self, key):
if key not in self.routes:
self.routes[key] = random.choice(self.nodes)
return self.routes[key]
def read(self, key):
node = self.routes.get(key, random.choice(self.nodes))
return f"Read '{key}' from {node}"
def write(self, key, value):
node = self.route(key)
return f"Write '{key}={value}' to {node}"
if __name__ == "__main__":
router = StickyRouter(["node-a", "node-b", "node-c"])
print(router.write("user:42", "Alice"))
print(router.read("user:42"))
# Simulate another write — should hit the same node
print(router.write("user:42", "Alice-updated"))
# Different key gets a different (arbitrary) node
print(router.write("user:99", "Bob"))
Output
Write 'user:42=Alice' to node-b
Read 'user:42' from node-b
Write 'user:42=Alice-updated' to node-b
Write 'user:99=Bob' to node-a
How it works
The route method assigns a random node the first time a key is seen, then caches that mapping in self.routes. The write method always calls route so subsequent writes for the same key hit the same node, giving read-your-writes consistency when a matching read follows. The read method uses self.routes.get(key, random.choice(self.nodes)) so a key that was never written gets a fresh arbitrary node. Because the mapping is in-memory, this mock demonstrates the pattern without a real load balancer or session store.
Common mistakes
- Using a new random choice on every read/write, which breaks stickiness
- Not handling keys that were never written — reads should fall back to a random node
- Forgetting that the routing table must be shared across processes or instances in real deployments
Variations
- Use a consistent hash ring (e.g., hash of key modulo number of nodes) for deterministic routing without a route table
- Back the routing map with Redis or a database so stickiness survives restarts
Real-world use cases
- Directing a user's session requests to the same backend cache or database shard for consistency.
- Keeping API callers pinned to the same worker that holds an in-memory rate limit or cursor state.
- Routing sticky consumers to the same Kafka partition or message queue for ordered processing.
Sponsored
More from Microservices patterns
- BFF aggregation pattern: combine multiple service responses in Python easy
- Backward Compatible Schema Evolution in Python medium
- Bulkhead Thread Pool per Service Mock in Python medium
- CQRS with Separate Read and Write Repositories in Python medium
- Cache-Aside Pattern in Python: Per-Service Mock easy
- Consumer Driven Contract Pact Mock in Python medium
Keep learning
Related tutorials and quizzes for this topic.