Rails Polymorphic Feed Caching
When building a social platform, we’re often required to build a feed of some sort, usually containing multiple types of content such as posts, photos, etc.
With this in mind, it’s usually best to have a FeedItem
model with a polymorphic association for the different types of feed items. Let’s say:
class FeedItem < ActiveRecord::Base
belongs_to :source, polymorphic: true
has_many :comments
end
class Post < ActiveRecord::Base
belongs_to :user
has_one :feed_item, as: :source, dependent: :destroy
end
class Photo < ActiveRecord::Base
belongs_to :user
has_one :feed_item, as: :source, dependent: :destroy
end
This is all well and good, but when you come to eager-load associations on the collection to improve performance, this technique becomes problematic. This is where Rails Fragment Caching comes into play.
Fragment caching is a really useful tool in Rails to cache partials and collections so they are pulled straight from memory without needing to access the database (besides checking if new records exist or existing records have been updated).
In the view that includes our feed partial we have the following:
- cache(cache_key_for_user_feed_items(resource, params[:page])) do
= render 'feed_items'
module FragmentCacheHelper
def cache_key_for_user_feed_items(resource, page = 1)
count = resource.feed_items.count
max_updated_at = resource.feed_items.maximum(:updated_at).try(:utc)
.try(:to_s, :number)
"user/#{resource.id}/feed_items_#{count}_#{max_updated_at}_#{page}"
end
end
The FragmentCacheHelper
generates a key for us to use. If the key differs when the next page is loaded, it will pull the records as usual and skip the cache. This works per page, so each page with will_paginate will also pull from the database if needs be.
This cuts down the database calls by only ever needing to check the count of the records for the user and the latest updated_at
value.