Adding a counter cache column to a model is a common optimisation we make in order to avoid unnecessary queries when trying to aggregate data associated with that particular model. Rails provides us with a number of ways to maintain the counter cache column’s value. The first is to follow the rails convention and add
counter_cache: true to a
belongs_to association and ensure we have a correctly named
The other way to do it, is manually. In this case rails provides us with a few convenience methods to increment a given column.
The first is
increment! is defined as:
def increment!(attribute, by = 1) increment(attribute, by).update_attribute(attribute, self[attribute]) end
increment is defined as:
def increment(attribute, by = 1) self[attribute] ||= 0 self[attribute] += by self end
Which means that we’re first fetching the current attribute’s value, incrementing it then passing it on to
update_attribute before it can be saved. This method leads to a non-atomic database operation, that is to say that at one point, the count is different in memory than it is in the database (which can lead to race conditions).
The second is
increment_counter is defined as:
def increment_counter(counter_name, id) update_counters(id, counter_name => 1) end
which executes SQL like:
UPDATE "table_name" SET "counter_name" = "counter_name" + 1 WHERE id = 1
This means that we now have an atomic operation and the counter cache value is the same across the system.