Skip to main content
How Caching Workslesson 4 of 6 · 2 min read

Reading From a Cache

The read path

The read path comes first, because it is three steps and it carries most of the value. Ask the cache for a key. If the answer is there, return it. If it is not, read the database, put a copy in the cache, and return that.

This is called cache-aside, and the name tells you where the cache sits: aside from the main path, not in it. Your code talks to both the cache and the database. The cache itself knows nothing about your database. Every advantage and every problem below comes from that one fact.

Think about what you gain. You only ever store things somebody actually asked for, so your memory goes on the products people look at, not the whole catalogue. You can store any shape you like, including a finished page that took three queries to build, which saves the work as well as the data. And if Redis dies, every read simply misses and goes to the database. Slower, but still working, as long as your database can take the load.

The problems that come with it

Now the problems, which are yours because the logic is yours. The first request for anything pays the full database cost, so a cache that has just been emptied is a cache that is not helping. Two requests can also miss at the same instant, both read the database, and both write the answer back. For a plain read that is harmless duplication. Paired with someone editing that product at the wrong moment, it can leave you storing a value that is already out of date.

Everything needs a time limit you store, even a generous one. A copy with no expiry, plus one missed invalidation, is a wrong answer that lives forever.

the shape of it
Your serverRedishit: return itDatabasemiss: read it1. ask2. if empty3. put it back
step 1 of 3
Your code owns all three steps. The cache never speaks to the database, which is what makes this cache-aside.
the read path in full
Java
Product getProduct(long id) {
  String key = "product:" + id;

  String copy = redis.get(key);
  if (copy != null) {
    return Product.fromJson(copy);          // hit, under a millisecond
  }

  Product p = db.queryOne(                  // miss, pay the full cost
    "SELECT id, name, price, stock FROM products WHERE id = ?", id);

  redis.setex(key, 300, p.toJson());        // put it back for next time
  return p;
}

Worked example

Arjun shipped this, keyed by product id, holding the finished page text, with a five minute limit. Within an hour, 96 of every 100 requests were answered by the cache.

His database went from 92 percent busy to 14. The typical page went from 400 milliseconds to 6.

The cold start caught him a fortnight later. A deploy restarted Redis at 8pm, every copy vanished at once, and 3,000 requests a second all missed together. His database sat at 100 percent for about ninety seconds until the cache filled up again. He now loads the top thousand products before sending traffic to a fresh instance.