Search This Blog
Master Python from the inside out. Here, we don't just write code; we look under the hood at memory management, data types, and logic, all while applying the mindfulness and philosophy of the Bhagavad Gita to our development journey.
Featured
- Get link
- X
- Other Apps
PostgreSQL High Availability — Streaming Replication
BACKEND SERIES
Day 33: PostgreSQL High Availability — Streaming Replication & Read-Write Splitting in Python
⏳ Context: As web applications grow, 80% to 90% of database traffic is typically read-heavy (fetching feeds, user profiles, and analytics). Funneling all read and write traffic through a single PostgreSQL node creates a bottleneck. To scale horizontally, production architectures deploy a **Primary Database** for state mutations (`INSERT`/`UPDATE`/`DELETE`) and stream changes to multiple **Read Replicas** for query workloads. Today, we build a production-grade Read-Write routing engine in Python using SQLAlchemy 2.0 and asyncpg.
1. Physical Streaming Replication: WAL Record Transmission
PostgreSQL replication relies on the **Write-Ahead Log (WAL)**. Every transaction is appended to WAL buffers before disk commit. In **Physical Streaming Replication**, the Primary node streams raw WAL byte blocks to Read Replicas over a TCP connection. Replicas constantly apply these WAL records in read-only recovery mode.
-- Run on Primary to monitor connected replicas and byte lag SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, pg_wal_lsn_diff(sent_lsn, replay_lsn) AS byte_lag FROM pg_stat_replication;
2. Dynamic Read-Write Engine Routing in Python
To implement Read-Write splitting seamlessly in Python backends, we configure SQLAlchemy 2.0 with primary and replica engine pools:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import sessionmaker import random # Primary Engine (All Mutations) primary_engine = create_async_engine("postgresql+asyncpg://user:pass@primary-db:5432/main") # Read Replica Engines (Read Queries) replica_engines = [ create_async_engine("postgresql+asyncpg://user:pass@replica-1:5432/main"), create_async_engine("postgresql+asyncpg://user:pass@replica-2:5432/main") ] class RoutingAsyncSession(AsyncSession): def get_bind(self, mapper=None, clause=None, **kw): if self._flushing or (clause is not None and not clause.is_select): return primary_engine # Round-robin or random selection across read replicas return random.choice(replica_engines)
3. Enforcing Read-Your-Own-Writes Consistency
To prevent users from seeing stale data immediately after updating records, backends use a **Sticky Primary Window** strategy: when a user issues a state mutation, all read queries for that specific user session are pinned to the Primary database for 5 seconds before returning to replica load balancing.
The Shareable Quote: "Scaling reads across replicas is easy; preserving user-perceived consistency during replication lag requires intentional session routing."
🛠️ Day 33 Project: Build a Dual-Engine Routing FastAPI Service
Build a FastAPI microservice featuring a custom SQLAlchemy AsyncSession router that splits writes to primary and reads across replica pools.
- Set up primary and read-replica SQLAlchemy async engines.
- Override `AsyncSession.get_bind()` to dynamically route SQL statements based on clause type.
- Implement a Redis-backed sticky session middleware to pin mutated user sessions to the primary engine for 3 seconds.
Tomorrow, we explore database caching and invalidation strategies: Day 34: Caching Architecture — Redis Cache-Aside, Write-Through, and Cache Stampede Prevention (Singleflight Pattern).
- Get link
- X
- Other Apps
Popular Posts
Python Production File Handling — aiofiles, mmap & Atomic Writes (2026)
- Get link
- X
- Other Apps
Why September 2026 Changes Android Forever: The Keep Android Open Fight
- Get link
- X
- Other Apps
Comments
Post a Comment
?: "90px"' frameborder='0' id='comment-editor' name='comment-editor' src='' width='100%'/>