Improving Rails Application Performance with Caching

BackEnd Reading Time: 6 min.

  • views4222
  • 0comments
Share

One of the most common techniques used to improve application performance is caching. Thanks to this method, we can achieve a higher performance of our application by even a few times, without spending more on new, more robust servers. In a case when we are diagnosing performance bottlenecks, we should use this as the first strategy to try to solve our problem. We should consider a scaling of application only when we are not capable of more caching.

What should be stored in a cache?

We should store all kinds of long-running tasks that are often executed and queries which have an impact on the response time of the application. Thanks to that, we can avoid overloading our infrastructure by directly “hitting” for the result to cache instead of executing the same action each time. In more complex applications, where the number of operations is significant, the first step which we should take is to start to analyze statistics. Only based on them should we make a decision as to which operation should be cached and which should not. Otherwise, we can fill out our database to show the results which, in fact, are not that often executed or don’t take as long as we thought. In the case of Rails, the best starting point for finding application bottlenecks is log files. They contain all of the necessary information like how much time needs to be spent in ActiveRecord or how much time it takes for view rendering. This is crucial information that can help us make the right decision. Additionally, there are external tools like NewRelic or Skylight, which can also help us by visualizing these statistics.

How caching in Rails?

Rails is an excellent framework that has, by default delivery for the developers, many ready-to-use solutions; in the cache case, it is not different. We have a few cache techniques that can help us improve the performance of our application. To use them, first, we must make sure that option config.action_controller.perform_caching is set on true in a config file.

Page caching

NOTE: This technique is no longer available by default from Rails 4. If you want to use it, first you must add an actionpack-page_caching gem to the project.

It’s the most simple form of caching. During the first request, page content is dynamically generated and is saved as a static HTML file. In the next request, when a user wants access to this same URL, a page is not a dynamic build but is served from this static file. Though this technique is very fast, in practice, is not often used. The main reason is that we can use them only for a particular situation, mostly in cases when our application delivers the same content for all users, as with a blog. In cases when the page is served from the cache, the request doesn’t pass through RoR stack. This excludes the use of this technique for any kind of applications that require an authentication method.

Action caching

NOTE:┬áThis technique is no longer available by default from Rails 4. If you want to use it, first you must add an actionpack-action_caching gem to the project. It’s the most simple form of caching.

Similarly to page caching, this technique caches full pages; however, opposite to the previous method, the request must pass through Rails stack. This is the main difference, but thanks to that difference, we can use this approach for the applications that require authentication.

class ItemsController < ActionController
  before_filter :authenticate
  caches_action :show

  def show
    @items = Items.find(params[:id])
  end
end

Fragment caching

Fragment caching is probably the most often-used technique. They allow caching specified by a user block of code. This can include dynamic elements from front-end like a menu or user comments, but also JSON code generated by JBuilder. Thanks to this method, we are able to make “mixing,” where one part of our page is received from the cache and the second is dynamically generated each time.

json.cache! ["users/#{@cache_key}"] do
  json.status "success"
  json.data do
    json.message @msg
    json.array!(User.all) do |user|
      json.extract! user, :id, :email, :current_sign_in_ip
    end
  end
end

In most cases, when we want to refresh the content of our component every hour, we can do this by using the cache method. The only one thing which we must do is add the “:expires_in” parameter, which defines when the data stored in the cache will expire.

<% cache("top_selling", :expires_in => 4.hours ) do %>
    <% @top_items.each do |item| %>
	<%= item.name %>
    <% end %>
<% end %>

Low level caching

In a case when this is still not enough for us, we can always use the more advanced, low-level caching method. This technique allows us to manage the cache personally by Rails.cache. Object Rails.cache is a standardized interface which we can use to perform the particular operation on the cache database, no matter what engine exists at the backend.

Below short description often use methods.

  • Rails.cache.read(key) – get data located under certein key.
  • Rails.cache.write(key, data) – save data under specifiec key.
  • Rails.cache.fetch(key) do .. end – fetch data from specific key, if key dosn’t exist execute do .. end block and result save under key.
  • Rails.cache.delete(key) – delete data stored under key
  • Rails.cache.exists?(key) – check if particular key exist.

The configuration of the backend is located under config.cache_store in the particular environment file, where by default, we can use engines like :file_store, :memory_store, :drb_store, :mem_cache_store and :compressed_mem_cache_store. Each of these has some benefits, but in practice, the most often used database is Redis, where the integration is done by external gems.

json.cache! ["users/#{@cache_key}"] do
class PublicController < ApplicationController
  def index
    @cache_key = "users/#{User.maximum(:updated_at)}"
    @users = Rails.cache.fetch(@cache_key) do
       User.all
    end
  end
end

Useful links

Tagged with:

Tags: , ,