fast_jsonapi icon indicating copy to clipboard operation
fast_jsonapi copied to clipboard

Make generated cache_key customizable per-serializer

Open elia opened this issue 6 years ago • 1 comments

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)

elia avatar Feb 27 '19 14:02 elia

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 raw cache_key of 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

iv-mexx avatar Mar 12 '19 18:03 iv-mexx