Why cache? In a nutshell, caching can speed up your Rails application. Especially with Ruby, a language which isn’t very fast, caching is essential. Your developers probably hear the word “cache” a lot, it happens to be a French word and translates to mean “to hide.” Caching is storing a particular amount of data generated during the request-response cycle in a cache and reuse it while responding to similar requests. This means the requests your users make in the future are fulfilled a lot faster since there’s no need to produce the very same data once again. Nearly all Rails applications, serious about performance could use more caching. This caching must be done intelligently though, to achieve fast server response times.
Caching comes with a lot of benefits since it’s effective in speeding up your application and improving its user experience. Thanks to caching, even if your website runs on a single server with one database, it can sustain about thousands of concurrent users. Caching will reduce the data access time, reduce latency, and improve input/output. Rails comes with various caching techniques: page, action, fragment, model, and HTTP caching. Your developers are most probably using multiple techniques in conjunction with each other.
A good developer who has mastered the various caching techniques and understands when and where to leverage them, can ensure your Rails applications will serve millions of views without exorbitant response times or server bills. Most developers don’t give this the attention it requires because there’s always a new feature to be added, or the next user story. This debt will start mounting and eventually, the performance ends up being a priority only when the whole thing topples. To make matters worse, caching isn’t always easy and the best caching practices seem to be changing every other week. On a Rails application, 300ms per request is not unheard of even without caching, chiefly if you’ve been diligent with your use of ActiveRecord and SQL queries. Even so, it’s a lot easier when you use caching.
Caching in Rails
Your Rails cache stores all the cached content except cached pages. Cached pages end up being stored as HTML on disk. Also, your rails application offers a lot more than just fragment caching.
Page Caching: The idea behind this is quite simple, the whole HTML page is saved to a file inside the public directory and on subsequent requests, this file gets sent directly to the user without any need to render the view and layout again. This only works for simple applications though. Chances are, you have many pages which look different for different users. Hence, page caching is not the best option for you. However, it shines for semi-static pages. Rails has now made this accessible only through separate gems.
Action caching: It works a lot like page caching, however rather than right away sending the page stored within the public directory, it strikes the Rails stack. By doing this, it runs before actions which can, for instance, handle authentication logic. Action caching is also available as a separate gem, now.
Fragment Caching: Now fragment caching is when you only want to cache a section of the page and not the entire page. In other words, when you want to store parts of views in the cache.
Model Caching: Otherwise known as low level caching is used to cache a certain query. Even so, this method can be employed to store any data. This functionality is part of Rails’ core.
HTTP Caching: This relies on HTTP headers. These headers are sent by the client to see when the content of the page was last modified and if it's unique id has changed.
However, preliminary optimization is the root of all evil, so your developers should assess which caching method suits you best, before implementing it.
Fragment caching, as the name suggests, caches only a fragment of your page. This is a functionality which exists in your Rails’ core, so your developers don’t have to add it manually. Fragment Caching lets a fragment of view logic to be encased in a cache block, and when the next request comes in, it’s served out of the cache store. Dynamic web applications tend to build pages which come with a variety of components and not all of which will have the same caching characteristics. Fragment caching is ideal, when different parts of the page need to be cached and expired separately.
If you’re new to this, ignore action caching and page caching. The situations where these two techniques prove useful is narrow and in fact these features were removed from Rails as of 4.0. I recommend getting comfortable with fragment caching instead. Fragment caching is a lot more useful than full page caching, especially in complex applications. They are usually built with smaller components in circumstances where it would not be productive to cache entire pages. Caching entire pages would mean, a single change to a particular part of the site would need re-rendering of the entire page and storing it again. This means the advantages of caching will soon become moot. It is more judicious to target smaller bits.
Cons of adding a lot of fragment caching
This is where profiling comes in. Rather than trying to hazard a guess what areas of your application are performance hotspots, fire up a profiling tool to tell you exactly which fragments of the page are slow. Make use of the rack-mini-profiler, it gives you a really good breakdown of where all your time is going to, during a particular server response. Profiling will prevent you from over caching a page and rather targeting the right fragments.
- Though caching is good for your app. Too much of a good thing is bad. Too much caching leads to a large data pile up, which in turn could cause issues when updating your database.
- It could result in difficulty updating the data, especially true if you have hundreds of users cache and trying to figure out which cache is whose when updating it.
- If you are using Memcached a lot, your Rails app will start slowing down the moment it exceeds the memory limit, since you’re storing everything (cache) in memory.
- Don't take it too far. Requests to Memcached don't come for free. You can maximize the advantages by caching a small number of large fragments instead of a large number of small fragments.
Fetching anything over the network is both slow and expensive. The capability to reuse and cache previously fetched data is an important part of optimizing for performance. Nevertheless, to truly gain all the benefits of caching, one must cache intelligently and not merely, as a force of habit.