Cutting Cross-Region Read Latency with YugabyteDB Follower Reads and Bounded Staleness
YugabyteDB follower reads let nearby replicas serve SELECTs with a bounded staleness guarantee, cutting cross-region read latency. Here's how to configure, verify, and size the trade-off.
02 Mar 2026, 13:13 UTC

If your YugabyteDB cluster spans three regions and your tablet leaders happen to live in one of them, every read from the other two regions pays a 50–150 ms round trip. For read-heavy workloads — product catalogs, dashboards, session lookups — that latency dominates. Follower reads let nearby replicas serve those reads, and the bounded staleness mode gives you a tunable knob instead of a binary "consistent or stale" choice. This piece covers how the feature works, a concrete configuration, and the trade-offs you need to size correctly. All examples assume YugabyteDB 2.14 or later, where YSQL bounded-staleness follower reads are fully supported; verify against your version's docs before rolling out.
Why reads cross regions in the first place
YugabyteDB replicates each tablet (its shard unit) with Raft. In an RF=3 cluster spread across three regions, each tablet has one leader and two followers. By default, all reads and writes go to the leader, because only the leader guarantees you're reading the latest committed state. Writes must go to the leader regardless — that's inherent to Raft. But reads don't strictly need the leader if your application can tolerate data that's a few milliseconds behind.
That's the opening follower reads exploit: a follower in the client's region already holds a nearly-current copy of the data, replicated through normal Raft replication. Serving the read locally turns a 100 ms cross-region round trip into sub-millisecond local access.
Bounded staleness: a lag ceiling, not eventual consistency
The key setting is the staleness bound. Rather than "read whatever the follower has" (pure eventual consistency), you declare a maximum acceptable lag. The follower serves the read only if it has replicated up to a point within that window of the leader's timeline; otherwise the query falls back to the leader automatically. Correctness of the bound is preserved — you never read data older than your configured threshold.
Two important caveats about the semantics:
- The bound is a maximum, not a typical value. Under replication backlog — a slow follower, network trouble, heavy write load — lag can exceed the bound and reads fall back to the leader. You'll see latency spikes exactly when the system is stressed.
- Setting the bound too high (say, above 10 seconds) degrades the guarantee toward near-eventual consistency. A user might not see their own write if their next read lands on a lagging follower. Size the bound for the worst staleness your application can tolerate, not the average.
Configuring follower reads in YSQL
Follower reads are controlled per session with two settings. Run these in your application session (any database user can set them; no superuser required):
SET yb_read_from_followers = on;
SET yb_follower_read_staleness_ms = 5;The staleness value is in milliseconds and applies to subsequent read queries in that session or transaction. A few operational notes:
- Only plain
SELECTstatements are eligible. DDL, DML, andSELECT ... FOR UPDATE/FOR SHAREalways route to the leader, as does anything under serializable isolation. - The GUC is session-scoped. If you use PgBouncer in transaction pooling mode, sessions get reused and settings can leak or vanish between transactions. Either use session pooling or set the GUC explicitly at the start of each transaction.
- Keep the default
enable_leaderless_consistent_reads=false; follower reads assume it.
A worked example: three-region cluster
Consider an RF=3 cluster placed across three regions:
yb-ctl create --rf 3 --placement_info \
"cloud1.region1.zone1,cloud1.region2.zone1,cloud1.region3.zone1"Insert some data, then from a client in region2, run with follower reads enabled:
SET yb_read_from_followers = on;
SET yb_follower_read_staleness_ms = 5;
EXPLAIN ANALYZE SELECT * FROM orders WHERE id = 1;Check the plan output for an indication the read was served by a follower, and compare latency against the same query with yb_read_from_followers = off. Don't assume a specific output format — verify what your version's EXPLAIN actually reports.
To confirm placement and lag, inspect the system views yb_servers() and yb_tablet_replicas(), and use yb-admin get_tablet_replica_status <tablet_id> to see per-replica lag. On the metrics side, watch the follower-read request latency and fallback counters on the tablet servers. A practical target: keep leader fallback below ~5% under your normal write load. If fallback is high, either your staleness bound is too tight for your replication lag, or a follower is unhealthy.
Trade-offs and failure behavior
The honest cost of follower reads is that you're trading recency for latency, and the failure modes deserve a test before production:
- Read-your-writes is not guaranteed. A write goes to the leader; a follow-up read within the staleness window may hit a follower that hasn't received it yet. If your app needs read-your-writes, route those reads to the leader (a separate session or a transaction without follower reads).
- Stress degrades gracefully but visibly. Simulate a partition or pause replication on one follower and confirm reads fall back to the leader rather than erroring or returning over-stale data. Run a write-heavy load (e.g., pgbench) and watch the fallback metric to validate your staleness setting.
- Maintenance events matter. Tablet splitting and leader rebalancing temporarily increase follower lag, which spikes fallback. Expect this during such operations.
- xCluster targets are excluded. Follower reads don't apply on an xCluster replica cluster; scale reads there with read replicas instead.
Where to start
Pick one read-heavy, staleness-tolerant query path — catalog browsing is the classic candidate — enable follower reads for just that session, and measure: local-read latency versus before, fallback rate under load, and replica lag during your busiest hour. If fallback stays low and the staleness bound matches what the business logic can tolerate, expand to more query paths. The feature earns its keep when most of your reads can be served within a few milliseconds of lag; if your workload demands fresh reads everywhere, the leader is the right answer and the cross-region cost is the price of consistency.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.