Caching in Rails

Yehuda Cohn
3 min readMar 6, 2016

--

You’ve built the next big thing in web applications and you’ve deployed to production. Users have been clamoring to use your site and suddenly you’re flooded with thousands of requests — awesome! But your database can’t keep up. Pretty soon your site is running at dial up speeds (the not-so good ‘ol days), and your users have forsaken you for your faster competitors. This could happen to you. But it doesn’t have to.

While building an application to scale would likely require numerous adjustments, one simple optimization that you can implement is caching. Caching allows you to temporarily store data locally, so if, for example, the user makes a request that they’ve already made, there’s no need for a trip to the database — everything that’s needed is already there. This frees up your database to respond to original queries only, and cuts down on traffic that could overwhelm it.

Rails offers a few different methods for caching including page caching, action caching, SQL caching, and fragment caching. Page caching and action caching have actually been removed from Rails 4, but still exist as Gems that can be added to your project.

Page caching allows you to process a request for a specific page directly from the server, bypassing the Rails stack entirely. This is really fast, but won’t work for pages that require authentication, and forces you to be cognizant of cache expiration, and manually implement it where necessary.

Action caching is similar to page caching in that it serves up a fully cached page depending on the user request. The difference is that action caching will step into the Rails stack and run through any before filters you have on a particular action before serving the cache. This is good for situations in which you require authentication, but still requires you to manually set the cache expiration.

When enabled, SQL caching will automatically store the results of a specific query, but only for the duration of that request. This is great if you have a need to perform identical queries within the same request, but not all that helpful beyond that. If you’d like to have a SQL query cache persist across multiple requests, you will need to set it up manually (see a nice example here).

Fragment Caching

Fragment caching, as its name suggests, allows you to cache select parts of your database as necessary. You can use fragment caching at the view layer to render various partials (for example, comments on an article), and cut down on the number of queries required for serving that page. All you need to do is wrap the display logic in a cache block. For some real Rails magic, you can pass in an instance of the model to the cache block. This will generate a unique cache key that will automatically change if the model is updated. This means that there is no need to actively expire the cache, because Rails will load the newest version on its own. One thing to be aware of is that if the model has relational objects (say comments belong_to an article), the cached article object won’t update if a change is made to the comment. To account for this situation, just add the touch: true option to the dependent model. This tells Rails to automatically update the cache for the parent object in the event that the dependent object has changed. You can also nest caches using a technique called “Russian Doll Caching”. Essentially, this technique allows you to recycle inner caches (say a particular list item like an article title) even in a case where the outer cache may have expired due to updates (like a list of all recent articles).

In some cases, in order to keep your views more flexible, the best course of action is to cache at the model level. This allows you to reference various caches across views, and can really help cut down on the number of queries being made across your app. For an excellent tutorial on model caching, check out Railscasts #115.

There’s more to explore when it comes to caching, but hopefully this can help you get started. Scaling your app might seem tough, but you can give yourself an easy performance boost by implementing caching.

Resources

Rails Guides-Caching in Rails

Nate Berkopec’s Complete Guide — An excellent and thorough explanation of why, how, and how to.

Railscast #90 Fragment Caching-A clear tutorial on how to implement fragment caching (including Russian Doll Caching at the end).

Michael May’s Talk from Austin on Rails-A nice explanation including information on HTTP caching.

DHH on How Key Based Cache Expiration Works-A peek under the hood.

--

--