KibiPay
HomeBlog › Postgres

The Read-Replica Pattern: Keeping Analytics Off the Payments Primary

7 min read Postgres
PostgresData PlatformArchitecture
The Read-Replica Pattern: Keeping Analytics Off the Payments Primary

The database that processes your payments has exactly one job that matters: accept writes, fast, correctly, all day. The quickest way to ruin that job is to let a heavy analytical query — a full-table scan for a month-end report, an analyst's curious cross join — land on the same instance. KibiPay solves this the classic way: a dedicated read replica, and a discipline about who is allowed to talk to which endpoint.

Two endpoints, two audiences

The Postgres cluster is managed by CloudNativePG, which gives every cluster two stable service names:

The rule is simple and enforced by credentials, not politeness: the reporting service and the ad-hoc SQL platform connect to fsaol-pg-ro. They have no write path, so even a bug can't mutate a payment, and their scans compete for the standby's I/O rather than the primary's.

Why a physical replica beats "just add read connections"

You could point read traffic at the primary and trust connection pooling to keep things civil. That works right up until a report query acquires a big shared buffer footprint, evicts the hot payment pages from cache, and every write suddenly slows down because it's now hitting disk. A physical standby has its own buffer cache and its own CPU. An analyst's mistake stays on the analyst's instance.

Streaming, not exporting

The standby is kept current by streaming replication — it continuously receives the primary's write-ahead log and replays it, so it is at most seconds behind. That matters for the reporting story: you are not querying last night's ETL export, you are querying essentially-live data, minus a small, bounded replication lag. For the four standing regulatory reports and for most dashboards, seconds-old data is exactly right, and the tiny lag is a fair price for never touching the primary.

Isolated by node, too

CloudNativePG places the standby on a different physical node from the primary via pod anti-affinity. So the replica isn't only a performance boundary — it is a fault boundary. Lose the node hosting the primary and the standby is right there, on different hardware, ready to be promoted.

Scaling reads later

The nice property of this pattern is that it grows. Today the cluster runs a primary plus one standby. Need more read capacity for BI? Bump the instance count and CloudNativePG streams a second standby from the primary online, without disrupting it. Read scale becomes a number in a manifest, not a migration.

One primary that only ever does payments, one standby that absorbs everything that merely looks — that separation is the quiet foundation the whole analytics stack sits on. The interesting trade-off is how the standby follows the primary: see async vs sync standbys, RPO, and not blocking writes.

Lag is a feature, not a bug

People new to replicas sometimes worry about the standby being slightly behind the primary, but for this workload the small lag is exactly right. Reporting and BI ask questions about what has happened — yesterday's volumes, this month's settled value, the current break queue — and for those questions data that's seconds old is indistinguishable from live. In exchange for that tiny staleness you get a hard guarantee that no analytical scan, however heavy, can ever slow down a payment write, because the two workloads run on physically separate instances with separate caches and separate CPUs. The contract is enforced by more than convention: the reporting service and the ad-hoc SQL platform hold no write credentials at all, so even a bug can't mutate a payment from the read side. And when you eventually need more read throughput — more dashboards, more analysts, a bigger BI footprint — you add capacity by raising the instance count and letting the cluster stream another standby from the primary online, without a migration or downtime. Read scale becomes a number, not a project — and because the standby is placed on a different physical node from the primary, that same read replica doubles as a fault boundary, ready to be promoted if the primary's node fails.

See it in motion

KibiPay connects UK Faster Payments, Bacs, CHAPS, Mojaloop mobile money, a mock ACH rail and Solana behind one API, with a cross-rail alias directory, ISO 20022 messaging and real-time fraud & AML screening.

Open the live console Directory demo