stackshareio/graphql-cache

Advanced custom cache keys

jeromedalbert opened this issue · 4 comments

I am not sure that this is a good idea. I will need to think more about that tomorrow or if I even actually need to to this. But here is my idea dump right now for future reference.

My use case is that my custom cache key value is potentially complicated and would need:

  • a lambda/proc on multiple lines (not super elegant, but already possible?)

    field :some_field, String, cache: { key: ->(obj) {
      "#{obj.id}123blah"
    } } do
      ...
    end
  • its own line, e.g.

    field :calculated_field, Int do
      cache <complicated code block goes here>
    end
  • or even its own method within the enclosing Type class.

    field :calculated_field, Int, cache: { key: :custom_cache_key }

    where :custom_cache_key is first looked up in the instance of the enclosing type class that is defining this field (with respond_to?) and only then calls the parent object if it's not defined.

  • or just use Rails.cache.fetch blocks manually inside the resolver

@jeromedalbert thanks for pointing this out. There seems to be a use case that we don't really cover with overridden keys. Looking through the code, I'm starting to think the whole key construct could be refactored to be a bit more readable and maintainable. I'm going to open a PR later today to start off that process.

I'm thinking maybe break key up somehow along the lines of type as evaluated here Identify the type of key based on metadata[:key] and instantiate the approriate class so that we can move features like context passing (#49) or more advanced usage of the object method approach (this issue) into their own classes. Suggestions are certainly welcome 😃

Starting up a refactor over on #50 that will fix that specific issue along with making it possible to provide a custom key generation class of some kind which will give you all the tools necessary for controlling the cache keys for specific fields.

I hope this issue is not too much of a hassle or waste of time, because I don't find myself often needing this. If it is a hassle, feel free to close!

Maybe needing complicated cache keys is a code smell telling me to instead cache upstream (Rails fragment/page caching), cache downstream (plain Rails.cache.fetch calls in the resolver), or avoid caching altogether.

Though it's not often used, I think the current implementation is a little stilted in it's flexibility. I think a feature like cache_if along with the ability to define a custom object to pass as the key generator is useful. The latter is just a matter of changing our checks from is_a? Proc to checking that it responds to call. This would allow you to create arbitrarily complex cache keys simply by providing an object that responds to call (this technique is used in graphql-ruby with several tracers, instead of using a long, difficult to read Proc, they instantiate a service object that responds to call). I'm going to leave this open for now and close it as part of a refactor I'm working on.