Counter Cache: How to get started

number of tasks under a project or the number of comments in a post or the number of users in an organization or anything similar is a common requirement in most rails applications. The code for doing it is also simple- @project.tasks.count; but the problem with this code is that every time you run it, you are counting the number of tasks of that project one by one. So, the speed of execution decreases with more number of rows. This code will slow down your page load, if you are displaying the details of more than one project in your page as shown below. project_list To speed this up, rails gives you an in-build mechanism called “Counter Cache“. As the name suggests, it literally means to cache the number of referenced rows it has (number of tasks a project has). Example code definition

class Projects < ActiveRecord::Base
  has_many :tasks
end
class Task < ActiveRecord::Base
  belongs_to :project
end
To implement counter_cache, you need to pass in the counter_cache: true option along with the belongs_to relationship. You also need to add a migration to add an extra column called tasks_count to store the count. This needs to be added to the other model, which has the has_many reference.
class Task < ActiveRecord::Base
  belongs_to :project, counter_cache: true
end
Migration
class AddTasksCounterCacheToProjects < ActiveRecord::Migration[5.0]
  def change
    add_column :projects, :tasks_count, :integer
  end
end
If you are adding counter cache to an existing system, you need to update your tasks_count with the existing counts. To do that, one can use the code given below. Either place the code along with the migration or run it in console in both production/development environments.
Project.find_each { |project| Project.reset_counters(project.id, :tasks) }
Also note that the tasks_count is just the default column name; if you wish to change it with another name, just pass that name along with the :counter_cache option as below.
class Task < ActiveRecord::Base
  belongs_to :project, counter_cache: :not_the_default_column_name
end
Now, to use the counter cache in your calculations, you should use the method “size” instead of “count”. The method “size” will use the counter_cache if its present, where as using “.count” itself would do the actual sql count.
<% Project.all.each do |project| %>
  <%= project.name %> (<%= project.tasks.size %>)
<% end %>
Points to Remember
  • :counter_cache is the optional attribute of a belongs_to relationship
  • It requires the creation of an extra column (tasks_count) in the table which has the has_many relationship
  • One can use another column name by passing the name along with :counter_cache option
  • To use the counter_cache, one must use the “.size” method
Speed Improvements Speed with just count speed_with_counter_cache  ]]>