- Programming Language: Go 1.26.0
- Container: Docker Compose
- Database: PostgreSQL 18 (1 primary + 2 streaming replicas)
This project demonstrates the read replica pattern: a single writable primary streams its write-ahead log (WAL) to one or more read-only replicas. Writes go to the primary; reads are spread across the replicas to scale read throughput. Because replication is asynchronous, replicas can be slightly behind — this project also demonstrates that lag and how to deal with it.
- Streaming replication — replicas clone the primary with
pg_basebackupand then continuously replay WAL. - Write routing — all writes go to the primary; replicas reject writes.
- Read load balancing — reads are distributed across replicas round-robin.
- Replication lag — measuring how long a write takes to appear on a replica.
- Read-your-writes consistency — reading from the primary when a user must immediately see their own change.
- Monitoring — inspecting
pg_stat_replicationon the primary.
flowchart TB
W["writes"] --> P[("Primary :5436<br/>accepts writes")]
P -- "streams WAL (async)" --> R1[("Replica 1 :5437<br/>read-only")]
P -- "streams WAL (async)" --> R2[("Replica 2 :5438<br/>read-only")]
RD["reads (round-robin)"] --> R1
RD --> R2
docker compose up -dThe primary initialises first; each replica then waits for it, runs
pg_basebackup, and starts as a hot standby. Wait until all three are healthy:
docker compose psgo mod tidygo run main.goThe application demonstrates writes hitting the primary, replicas rejecting
writes, load-balanced reads across replicas, observed replication lag, the
read-your-writes mitigation, and live replication status from
pg_stat_replication.
| Aspect | Read Replicas | Sharding |
|---|---|---|
| Scales | Reads | Reads and writes |
| Data on each node | Full copy | A subset |
| Write capacity | Single primary (bottleneck) | Many primaries |
| Consistency | Eventually consistent reads | Strong within a shard |
| Complexity | Lower | Higher |
docker compose down -v