Make generated cache_key customizable per-serializer
I just caught a bug in the app I'm working on that was caused by conflicting cache keys.
Related issues that would be solved with the proposed solution: https://github.com/Netflix/fast_jsonapi/issues/383 https://github.com/Netflix/fast_jsonapi/issues/300 https://github.com/Netflix/fast_jsonapi/issues/389
Backstory of the bug
FastJSONAPI uses Rails.cache.fetch(record.cache_key), and so was another part of the app Rails.cache.fetch(@product) { @product.to_json }. Although similar the two results are different, one is the hash resulting from serialization with fast_jsonapi, the other is a String containing JSON data.
That generated weird dynamics, dependent on which page was visited first and when the cache expired. 💆♂️
Proposed solution
- Allow to pass a proc that is used to customize the cache_key generation, e.g.
cache_options enabled: true, cache_key: -> product { [:product_serializer, product] } - Default to a cache_key namespaced to the serializer's class name, e.g.
default_cache_key = -> record { [self.class.name, record] } Rails.cache.fetch(cache_key.call(record) do …
Slightly OT, but might also make sense to allow to pass a different cache object instead of Rails.cache:
cache_options enabled: true, cache: Dalli::Client.new('localhost:11211', namespace: "app_v1", compress: true)
I'm not sure it would be required to make the cache_key customizable.
The problem is, that currently the models cache_key is used raw.
In contrast, Rails Fragment Caching automatically scopes the cache key
I currently have this problem where I have two different Serializers for one Model (a Summary and a Detail response for the same model) like #389
Example
For comparison, I have a Post model rendered to JSON via a FastJsonapi Serializer and to HTML via erb
PostSerializer:
class PostSerializer < BaseSerializer
include FastJsonapi::ObjectSerializer
cache_options enabled: true, cache_length: 12.hours
attributes :message, :created_at
end
_post.html.erb:
<% cache post do %>
<p>
<%= post.created_at %><br>
<%= post.message %><br>
</p>
<% end %>
I have not changed the default cache_key Method from ApplicationRecord in the Post model!
Here's the cache keys
-
posts/12: Cache Key for the JSON (PostSerializer) -
views/posts/_post:92256691d155da60f6a0e68498474d25/posts/12: Cache Key for the erb Fragment (_post.html.erb)
Conclusion
I think, record_hash should not simply take the models cache_key, it should at least scope to
- A constant unique for FastJson (
"fast_json"?) to avoid collision with other things that also use the rawcache_keyof a model (#394) - The specific Serializer used (
type?) to avoid collision between different Serializers for the same model (#389) - A version of the Model like Rails Fragments do (
cache_version?) to support Cache Versioning (#300)
This way, all related issues would be solved without the need for the user to manually generate a cache_key in each Serializer