Using Ruby on Rails Counter Cache with Active Storage attachments

Suleyman Musayev
2 min readMay 2, 2023

Counter cache is a useful feature in Ruby on Rails that allows you to track the number of associated records using a cache column in the parent model. If explained in general terms, whenever a child record is created or destroyed the number (integer) contained in the cache columns of the parent model is increased or decreased by one respectively. Use of counter cache improves performance by reducing the number of database queries needed to retrieve the count of associated records.

Recently I worked on the task that included a feature for tracking user’s progress for completing an application by submitting required attachments. Since the progress depended largely on the number of `ActiveStorage::Attachments` uploaded by a user I thought that it would be a perfect use case for the counter cache.

`ActiveStorage` is a “behind the scenes” framework in the Ruby on Rails that magically enables uploading and managing files, and by default it does not support counter cache. But in theory counter cache may be enabled by updating a cache column in the parent model whenever a new attachment is added or removed.

I ended up adding the following monkey patch to enable counter cache in the `ActiveStorage::Attachment` class:

module UserApplication::Attachments
extend ActiveSupport::Concern

class ActiveStorage::Attachment
require 'active_record/counter_cache'

before_create :after_create_action, if: :record_user_application?
before_destroy :after_destroy_action, if: :record_user_application?

def after_create_action
UserApplication.increment_counter(:attachments_count, record_id, touch: true)
end

def after_destroy_action
UserApplication.decrement_counter(:attachments_count, record_id, touch: true)
end

def record_user_application?
record_type == ‘UserApplication’
end
end
end

The patch imports a counter cache library and adds two before action hooks that execute methods to update the cache column in the parent model whenever a new attachment is created or deleted. The `after_create_action` method uses the `increment_counter` to increment the `attachments_count` column in the `UserApplication` model, while the `after_destroy_action` uses the `decrement_counter` to decrement it respectively. The `record_user_application?` is used to check if the attachment belongs to a `UserApplication`. The patch was wrapped in the `UserApplication::Attachments` module and `record_user_application?` method was used as a precaution measure against risks of monkey patching `ActiveStorage::Attachment` class.

To sum up, the patch allowed me to use `counter_cache` for user’s attachments while improving performance at the very same time since it reduced the number of database queries otherwise needed to retrieve the count of associated attachments.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response