Caching Part 1: Intro

What Is Caching And Why Should You Use It?

Anyone that has ever worked with high volume sites has at some point had to optimize one or more parts of the application. The most common ways to go about that are profiling the application code and database queries. Once you know where the bottlenecks are, you have a couple of options, depending on the use case:

  • refactoring the code so it runs faster, such as extracting code from foreach loops or optimizing the SQL,
  • offloading the process to the background (I wrote about that in another post),
  • cache the results

Refactoring and optimizing code can get you only so far, sooner or later you will hit a wall and will need to look for other options. Running some heavy calculations can be sent to background workers, but if you need the results immediately, caching just might be your best bet.

Caching lets you do the calculation once and use those results many times in the future. But for how long should you keep results in the cache?

Time-based Expiry Vs. Evict On Update

Some use cases lend themselves to time-based expiry. In some situations stale data is OK and displaying a listing without new items for 45 minutes doesn’t represent a problem. Content sites such as blogs can gain from caching items for, say, two hours or more, knowing that articles would not change that fast or even at all. Or you might even know how long to cache if you know upfront when that data will become old: you can cache the fact that a venue is currently open because you know from its working hours that it closes in 458 minutes.

On the other hand, for some things you can’t know when they will change, maybe a product has gotten out of stock and you want to immediately display up-to-date information. Here, deleting the item from cache right after the update seems to be the best solution.

Warming The Cache Up Front Vs. Caching On Demand

After the cache expires or is deleted, next time we need that piece of information, we do the heavy calculation again and then store the results in the cache. This will work for sites with lower traffic, but what if you get slashdotted? What happens if we have multiple requests requesting that info? Dozens or hundreds of processes could be doing the heavy calculation and storing the same info back to cache. This is called a cache slam or cache stampede and can be mitigated in a number of ways. One is to actually prolong the TTL of a stale cache item so that other requests return stale data while the current process recreates the item in the cache. The other way is to create the cache item right after doing the action that rendered it obsolete. This is called pre-warming the cache.

To pre-warm the cache, you might just call the method that gets the data you are caching. For example, right after you update a product, you call the method that gets the product as well as the method that gets the listing of product that product was a member of. And cache the result. This is a great candidate for a background job and repository pattern together with a view presenter will help in unifying the way you get the data both in a background job and during the “normal” request.

When Not To Solve Your Problem With Caching?

There is no silver bullet and caching is no exception. There are some cases when caching simply doesn’t fit. Storing search results or sensitive information such as credit card numbers just doesn’t make any sense, and opting for another solution to the performance problem is the better way.

Using Multiple Levels Of Cache

One caching layer might not be enough for your application. If you request the data for logged in user, you might hit the cache. But getting the logged in user is being done multiple times during a single request. You should not hit Redis every time you need the same data: use an intermediary, run-time cache such as the property on the object that is responsible for getting the logged in user. If the property is null , get the object from cache/database. If not, return it.

In next part, I’ll be writing about how to implement caching, cache tagging and invalidating cache