Read replicas
A read replica is a second database that follows the primary. Besu has no routing layer: you
create a second Database over a second connection and decide, at each call site, which one
the query runs on.
Two databases
Each Database wraps one driver, and a driver wraps one pool. Two connection strings produce
two independent pools.
Sqlx is the same type for both, so every helper written against
&Database<besu_postgres::Sqlx> accepts either.
Routing reads and writes
There is no read() / write() split and no annotation that marks a query as read-only. The
routing is ordinary code: writes and anything that must be current go to the primary, and
tolerant reads go to the replica.
Read-your-own-writes is the trap. A replica lags, so a row inserted on the primary may not be visible there yet. Read the row back from the primary when the request depends on it, or carry the value you already have instead of re-reading it.
Transactions stay on one database
A transaction is one connection, so it is one database. A read that must see the
transaction's own uncommitted writes has to go through tx; the replica is a different
connection with a different view, and it lags.
What is not here
- No automatic failover. If the primary is unreachable, the error is the driver's; nothing promotes the replica.
- No lag tracking. Besu cannot tell you how far behind a replica is, and does not wait for it.
- No load balancing. Two calls to the replica use one pool; how its connections are distributed is sqlx's business.
- No read-only enforcement. A
Databaseover a replica will happily accept an insert; the server must be configured to refuse it.
Gotchas
- A replica URL is not a replica. Pointing a second
Databaseat the same server gives you a second pool, not replication; the split is the deployment's. - Sharing across tasks needs an
Arc.Databaseis notClone, but it isSend + Sync, soArc<Database<Dr>>can be cloned into tasks and dereferences to the&Databasea query needs. - The pool size multiplies. Each database opens its own pool, so two replicas plus a primary can be three times the connections the server expects.
- A migration runs where you point it. Run the migrator against the primary, not the replica; a replica usually refuses DDL, and a migration applied to one is not replicated.
