Readyset Weekly: Issue 76
The Cost of Keeping Postgres Healthy: Vacuum, Bloat, and Maintenance
PostgreSQL reported the largest popularity gain of any system in the first half of 2026, and for new projects it's increasingly the default choice. Its extension ecosystem and reliability make it a strong choice for data teams. What can get left out of the "just use Postgres" advice is the operational work that comes with running it at scale, and most of that effort traces back to one elegant design decision.
Postgres uses MVCC, multi-version concurrency control, so that ordinary reads and writes don't block each other. When you update a row, Postgres doesn't overwrite it. It writes a new version and leaves the old one in place, marked with the transaction IDs that help determine which versions each transaction can see. That's what lets a long-running read see a consistent snapshot while writes keep flowing. It's an excellent design, but it has a cost: updates and deletes leave old row versions behind. Once no transaction needs those versions, something has to clean them up.
That something is vacuum, and on a busy database it isn't an occasional chore. It's a continuous, load-bearing part of how Postgres runs. Autovacuum works in the background to make the space dead tuples occupy available for reuse. When it falls behind, dead row versions accumulate and can contribute to bloat: wasted space that makes tables and indexes larger than the live data warrants. That can increase the work required for scans and put more pressure on memory and I/O. The database keeps working, getting more and more expensive to run.
Two things turn this process into ongoing work:
- The defaults can let cleanup lag on large tables. With the default update/delete settings, a table becomes eligible for autovacuum when the number of obsolete row versions exceeds a threshold of 50 plus 20% of the estimated row count. On a 100-million-row table, that's roughly 20 million obsolete versions. Other triggers can start vacuum earlier, but this threshold can still leave too much cleanup between runs on a heavily updated table. On heavily updated tables, keeping cleanup on pace may require lowering the scale factor per table and giving autovacuum enough resources to keep up.
- Vacuum isn't only about space. Postgres uses 32-bit transaction IDs, which cycle through about 4 billion values. Vacuum "freezes" old row versions as those IDs wrap around. The relevant safety window is around 2 billion transaction IDs, and Postgres normally starts preventing vacuum much earlier. If that maintenance falls far enough behind, the database approaches the transaction ID wraparound safety limit, and to protect your data it will stop accepting transactions that require transaction IDs, which blocks writes. Restoring normal operation requires resolving any cleanup blockers and vacuuming the affected database. That's why vacuum is non-negotiable rather than optional.
None of this argues against Postgres; vacuum overhead is part of the price of MVCC, and for most workloads it's a price well worth paying for reads and writes that don't block each other. If your team is running Postgres at scale, treat vacuum as a first-class part of operations: tuning autovacuum per table, watching bloat and transaction ID age, and keeping an eye out for the long-running transactions and idle connections that quietly hold vacuum back. For teams looking to take more of that maintenance off their own plate, this post on automating Postgres maintenance walks through how to approach it.
One thing that is easy to neglect is reducing the read pressure on the primary in the first place. Autovacuum competes with your live query traffic for the same CPU and I/O, so a primary that's saturated serving repeated reads has less headroom for vacuum to keep up. Moving those repeated reads off the primary, a cache like Readyset, gives maintenance more room to run when those resources are the bottleneck. It's one more reason busy Postgres databases can benefit from not serving every read from the primary.
🔍 What We're Watching in the Database Ecosystem
Your Postgres Database Is Slow, and It Isn't Postgres
Azure switches off host caching when a managed disk reaches 4 TiB, and Postgres has no way to tell you it happened. A Stormatics customer spent most of a week tuning shared_buffers and work_mem before discovering that the missing cache was causing their slow reads. Umair Shahid walks through four storage traps that can look like database tuning problems, and how to check for each one.
What REPACK (CONCURRENTLY) costs while it runs
Postgres 19 brings online table rewrites into core, no extension needed, so managed services get them too. Radim Marek measured 18.8 GB of WAL and 107 seconds on a 60 million row table against pg_repack's 33.5 GB and 162 seconds. The catch is a cluster-wide replication slot that holds back vacuum on every database while it runs.
pgBackRest and PostgreSQL failover: why archive_mode matters
A team promoted a standby with archive_mode=off and tried backing up from a downstream standby to avoid a restart. Stefan Fercot rebuilds it across three cascading nodes to show why a backup and a restore can both succeed while recovery from older backups across the failover still fails.
In Case You Missed It
September Monthly Product Update: This was a big month for RDST, now Apache 2.0 licensed with free hosted inference and no API key required, plus our single-node benchmark hitting 4.3M queries per second (6.4x the prior release on the same hardware), a batch of MySQL and Postgres cache-correctness fixes, and more.
MySQL BRZL
Watch: "Building the Invisible Cache": Tanmay Sinha's talk from MySQL BR Conf 2026 is now up. It covers why the MySQL query cache failed, why rebuilding it by hand on Redis leaves the hardest parts to your team, and how transparent caching changes that. It also answers the question the audience kept asking: what's the difference between Redis and Readyset?
We're getting more social, catch the latest on Bluesky, X, dev.to, and Medium too.
Know a database aficionado who'd appreciate this? Forward it their way.
Get Readyset Weekly in your inbox
One email a week — what shipped, what we learned, and what is worth reading.