Read replicas
Read this page in the documentation
Read replicas ReplicaManager routes read queries across a set of replica databases, tracks how far each one is behind the primary, and takes unhealthy replicas out of rotation. It is a standalone component — you send queries to it explicitly, so nothing is silently routed away from the primary behind your back. Each replica gets its own connection pool, sized independently — a reporting replica can hold a bigger pool than a latency-sensitive one. Routing strategies Strategy | Picks | --- | --- | round-robin (default) | The next replica in rotation, skipping known-unhealthy ones. | least-connections | The healthy replica with the fewest in-flight queries. | lowest-lag | The healthy replica with the smallest measured lag. | least-connections and lowest-lag both fall back to round-robin when no replica has usable stats yet — before the first monitor cycle, for instance — so selection never throws just because monitoring has not warmed up. Where each query goes Everything inside a transaction goes to the primary, including its reads — otherwise a read could miss a write made moments earlier in the same transaction. Outside one, a read immediately after a write may land on a replica that has not caught up. Lag detection checkLag(name) measures how far one replica trails, in milliseconds: Dialect | Query | Source | --- | --- | --- | MySQL / MariaDB | SHOW SLAVE STATUS | SecondsBehindMaster × 1000 | PostgreSQL | SELECT EXTRACT(EPOCH FROM (now() - pglastxactreplaytimestamp())) 1000 AS lag | replay timestamp | Anything else | — | returns null and logs a warning | startLagMonitor() runs that check for every replica on the configured interval and maintains health: maxFailures consecutive failures (or lag over maxAcceptableLag) marks a replica unhealthy and takes it out of rotation; a good check puts it back. stopLagMonitor() ends the loop — call it during shutdown or the process will not exit. Events ReplicaManager is an EventEmitter. These are the hooks for metrics and alerting: Event | Fired when | --- | --- | replica:added / replica:removed | Membership changes. | replica:healthy / replica:unhealthy | A replica's health flips. | replica:lag-detected | Every successful lag measurement, with the value. | replica:selected | A query picked a replica. | all-replicas-unhealthy | Nothing is left to route to — with autoFailover, reads should go to the primary. | monitor:started / monitor:stopped | Monitor lifecycle. | Inspecting state Call | Returns | --- | --- | getReplicaStats() | Map<name, ReplicaStatus> — healthy, lag, activeConnections, lastCheck, error. | getReplicaStat(name) | One replica's status. | getHealthyReplicas() | Names currently in rotation. | getReplicaCount() / getReplicaNames() | Membership. | isReplicationEnabled() | Whether routing is on. | getReplica(name) | The underlying ReplicaPool — getConnection(), releaseConnection(), query(), close(). | Using it in an application queryReplica() takes raw SQL. Model finders always go to the connection they were defined on, so the usual shape is a small helper that decides which side a read belongs on: Two rules worth keeping: Never send writes to a replica. They are read-only; the driver will error, and on some setups a write would be silently lost. Do not read your own write from a replica. After a write, read from the primary until you know replication has caught up — replication lag is real even when it is small. checkLag() gives you the number to reason with. For routing to genuinely different databases rather than replicas of one, see Multiple databases. Shutdown Related reading Connection pooling Multiple databases Logging — query and slow-query events