Just doing some research on the best way to cache a paginated collection of items. Currently using jbuilder to output JSON and have been playing with various cache_key options.
The best example I've seen is by using the latest record's updated_at plus the amount of items in the collection.
def cache_key
pluck("COUNT(*)", "MAX(updated_at)").flatten.map(&:to_i).join("-")
end
defined here: https://gist.github.com/aaronjensen/6062912
However this won't work for paginated items, where I always have 10 items in my collection.
Are there any workarounds for this?
With a paginated collection, you're just getting an array. Any attempt to monkey patch Array to include a cache key would be a bit convoluted. Your best bet it just to use the cache method to generate a key on a collection-to-collection basis.
You can pass plenty of things to the cache method to generate a key. If you always have 10 items per page, I don't think the count is very valuable. However, the page number, and the last updated item would be.
cache ["v1/items_list/page-#{params[:page]}", @items.maximum('updated_at')] do
would generate a cache key like
v1/items_list/page-3/20140124164356774568000
With russian doll caching you should also cache each item in the list
# index.html.erb
<%= cache ["v1/items_list/page-#{params[:page]}", @items.maximum('updated_at')] do %>
<!-- v1/items_list/page-3/20140124164356774568000 -->
<%= render @items %>
<% end %>
# _item.html.erb
<%= cache ['v1', item] do %>
<!-- v1/items/15-20140124164356774568000 -->
<!-- render item -->
<% end %>
Caching pagination collections is tricky. The usual trick of using the collection count and max updated_at
does mostly not apply!
As you said, the collection count is a given so kind of useless, unless you allow dynamic per_page
values.
The latest updated_at
is totally dependent on the sorting of your collection.
Imagine than a new record is added and ends up in page one. This means that one record, previously page 1, now enters page 2. One previous page 2 record now becomes page 3. If the new page 2 record is not updated more recently than the previous max, the cache key stays the same but the collection is not! The same happens when a record is deleted.
Only if you can guarantee that new records always end up on the last page and no records will ever be deleted, using the max updated_at
is a solid way to go.
As a solution, you could include the total record count and the total max updated_at
in the cache key, in addition to page number and the per page
value. This will require extra queries, but can be worth it, depending on your database configuration and record count.
Another solution is using a key that takes into account some reduced form of the actual collection content. For example, also taking into account all record id's.
If you are using postgres as a database, this gem might help you, though I've never used it myself. https://github.com/cmer/scope_cache_key
And the rails 4 fork: https://github.com/joshblour/scope_cache_key
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With