Rails 7.1 makes increment_counter and decrement_counter accept a by argument

railsAugust 31, 2023Dotby Alkesh Ghorpade

In Ruby on Rails, the increment_counter and decrement_counter are functionalities offered by ActiveRecord. These functions are utilized to increment or decrement the value within a designated column within a database table, specifically targeting a particular record. They prove handy for updating counts or statistical data associated with a record, all while bypassing the necessity to fetch and store the complete record.

The basic syntax of the increment_counter method is as follows:

Model.increment_counter(:column_name, id)
  • Model: The name of the ActiveRecord model representing the database table.
  • :column_name: The name of the column you want to increment.
  • id: The ID of the specific record you want to increment the counter.

The increment_counter method will increase the column value by 1 each time it gets executed.

Before Rails 7.1

The increment_counter and decrement_counter methods are used primarily to maintain counter cache columns that store aggregate values.

For example, if you have a Company model with a column named employees_count and you want to increment the view count of a specific company with an ID of 1, you would use:

Company.increment_counter(:employees_count, 1)

This would generate an SQL query that updates the employees_count column of the companies table for the record with an ID of 1. The increment_counter operates at the database level, so it doesn't retrieve the actual record from the database. This can be more efficient than loading the entire record, incrementing the counter, and then saving it back to the database.

But there are a few cases where you might want to increase the value of employees_count by N, where N is greater than 1.

Before Rails 7.1, the possible ways were

  • Call the increment_counter N times
N.times do
  Company.increment_counter(:employees_count, 1)
end
  • Call the update_all query
Company.where(id: id).update_all("employees_count = employees_count + 5")

This approach is less convenient than increment_counter. The increment_counter function automatically manages the usage of COALESCE and effectively addresses challenges related to concurrency.

company = Company.find(1)

company.increment(:employees_count, 5)

The increment method modifies the employees_count within the memory and subsequently writes this modified value to the database. However, this approach could overwrite a recently updated value due to race conditions.

In Rails 7.1

Rails 7.1 makes increment_counter and decrement_counter accept a 'by' argument.

Company.increment_counter(:employees_count, 1, by: 5)

This modification will also align with the current increment methods within Rails. These existing methods, such as ActiveRecord::Persistence#increment and ActiveSupport::CacheStore#increment, already incorporate the usage of a 'by' argument and an 'amount' argument, respectively.

To know more about this feature, please refer to this PR.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right