Skip to content
← All issues

Readyset Weekly: Issue 63

When the Data Changes, What Does Your Cache Know?

The hardest part of caching isn't serving a query quickly. It's keeping the cached result correct after the underlying data changes. When a cached result is stale, nothing errors, nothing slows down, a user just sees a result that was true a moment ago and isn't anymore.

Every approach to caching or precomputing query results is really an approach to this one problem: when the underlying data changes, how does the cached result stay correct? The answers differ mostly in where the work happens, when it happens, and how much staleness is tolerated along the way.

Here's what each choice commits you to:

  • Cache with a TTL. Store the result, let it expire after some number of seconds, and recompute when it lapses. Simple to reason about, but the TTL is a guess about how much staleness you can tolerate. Set it long, and users see old data; set it short, and you're recomputing so often the cache barely earns its place. The data doesn't change on a timer, so tuning one to match is always approximate.
  • Cache with explicit invalidation. Instead of guessing, you clear or update the cached entry whenever the underlying data changes. More accurate when it works, but now the write path and the cache are coupled through logic you maintain by hand, often spread across the codebase. Miss one write path, and that entry goes stale quietly until something else evicts it. This is the approach that often accumulates the most operational risk.
  • Materialized views. Let the database store the precomputed result. In Postgres, a standard materialized view recomputes the whole result when you refresh it, so it's stale between refreshes, and each refresh reruns the full query. Postgres doesn't do incremental refresh natively (the pg_ivm extension is still experimental), and MySQL doesn't have materialized views at all, so teams often build a similar pattern with a summary table and a refresh job.

The thread running through all of these is a tradeoff between freshness, cost, and how much of the correctness burden lands on you. TTLs trade accuracy for simplicity. Manual invalidation trades simplicity for freshness and hands you the maintenance. Full-refresh materialized views trade freshness for read speed, paying the cost of recomputing the entire result on each refresh.

There's also a different maintenance model. Incremental view maintenance keeps a precomputed result correct by tracking the changes to the underlying data and updating only the part of the result each change affects, rather than recomputing the whole thing or waiting for a timer. You get the read speed of a cached result without maintaining invalidation logic and without the staleness window a scheduled refresh leaves open. It isn't magic; some query shapes are harder to maintain incrementally than others, but for the queries it fits, correctness stops being something you manage and becomes something the system maintains for you.

That's the model Readyset is built on. It keeps cached query results in sync with your database by incrementally maintaining them as data changes through the replication stream, without cache keys, TTLs, or manual invalidation logic.

🔍 What We're Watching in the Database Ecosystem

The Failover Brownout: Rethinking MySQL Group Replication HA

A thoughtful reminder that failover doesn't mean instant recovery. When a lagging secondary gets promoted in MySQL Group Replication, it keeps rejecting writes until it drains its apply queue, so the cluster hits a "brownout" where reads work but writes are blocked, for a window proportional to how far behind that node was.

Rethinking the Database From Storage Up

Databricks co-founder Reynold Xin makes the case that most of what makes databases painful to scale traces back to one design choice: the write-ahead log and the data files both living on a single machine. That coupling is what turns durability into a disk-flush gamble, makes read replicas and high availability mean cloning the whole box, and lets a heavy analytical query starve your transactional traffic.

In Case You Missed It

New on our blog:

We broke down the real cost of read replicas at scale, where they earn their keep, where they stop paying off, and why SQL-layer caching handles repetitive reads for a fraction of the spend.

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.