Don't add a read replica until you've read this

shintoist4 pts0 comments

Don't add a read replica until you've read this | Blog | incident.io

Open main menu

Products

Solutions

Resources

Customers

Pricing

Careers

Get a demoLogin

incident.io<br>Brand Assets<br>Download .PNG logos<br>Download .SVG logos<br>Download Brand Guidelines<br>Visit brand center

Get a demoLogin

Open main menu

All postsEsc

Don't add a read replica until you've read this<br>The benefits of read replicas<br>Read-after-write consistency<br>Event handlers<br>Log sequence numbers to the rescue<br>Stamping LSN on users<br>Optimizations<br>Database connection pool routing strategies<br>Outcome

Don't add a read replica until you've read this

Johanna Larsson<br>July 21, 2026 — 23 min read

As the size and complexity of their relational database workload grows, every company eventually goes through the process of off-loading work on a read replica. It comes with lots of benefits, but at a cost of increased complexity. This article is about how we dealt with that, a lot of learnings, and some useful techniques.<br>incident.io is an incident management product relied on by thousands of customers to be the thing that supports them through anything from a minor blip to a full outage. Any disruption to that service has a significant impact on those users, and that’s always top of mind for our engineering team. Everything we build is designed to be performant, reliable, and gracefully degrading.<br>When large parts of the internet goes down, as they did for the AWS outage on 20 October 2025, we see a meaningful increase in alert volume as engineers across the globe are getting woken up. During events like this, we simply can’t fall over from the increased load. This means we always have to run with significant spare capacity. And so we’re always looking for opportunities to reduce the load on our DB, either through performance improvements or code redesign. While working on projects to control the resource utilization on our primary database, we clearly identified that we, like a lot of online services, run an overall read heavy workload.<br>We already had a read replica set up and some queries were already utilizing it, but it was something that we were doing on a case by case basis. We knew we could do more. We set ourselves the goal to move everything over to the read replica. For every query that could be move moved to the read replica, that’s additional capacity for our primary, and additional protection for those events of massive traffic that we design for.<br>The benefits of read replicas<br>But before that, let’s do a quick recap of why you’d want to introduce read replicas into your stack. They do bring complexity, having two databases and two database connection pools in your app logic is more to think about than just having the one. You also need to manage two things, where both often quickly become critical to running your service. Double the graphs and metrics and warnings to worry about. Not to mention, you’re now paying for two databases.<br>They bring a lot of benefits though. They’re the natural step to take to give people access to run operational queries on the production system, without risking those queries disrupting the production workload. A query run on the primary can cause overload, delays, contention, locks, and much more. On the read replica the blast radius is much smaller, although there are notable exceptions to this like hot_standby_feedback. But maybe the most interesting thing is that it opens the door to horizontal scaling. Relational databases generally don’t scale horizontally, just vertically. Writing to two primary databases is slower than to one, and for most of us spending more to get less performance is not a very interesting prospect. But read replicas are different, since they don’t need to coordinate writes, you can just have more of them and load balance read queries across them. That’s pretty cool.<br>Read-after-write consistency<br>Even before approaching this larger re-think we had already established some useful primitives. Our backend language is Go, but the basic techniques translate to some degree to any language.<br>The number one thing you need to deal with as you’re migrating work over to a read replica is read-after-write consistency. Or in other words, avoiding stale reads. The gist of it is, if you write something to the primary and then immediately after read the thing back but from the replica, there’s no guarantee that you get the same thing back. This gets worse the faster you read after writing. Now if you’re hand-crafting some beautiful artisanal code in your walled garden project, you can probably attempt explicitly picking the primary or read replica for each individual query through a code flow. But for practical reasons we often end up just limiting the read replica to the queries and code paths where we feel that nothing can go wrong.<br>That’s not what we’re looking for, we want to move everything over. That means we need automated detection of mutating queries, and to automatically fall over to the primary...

read replica primary queries after thing

Related Articles