Add support for caching renders in Graphiti, and better support using etags and stale? in the controller#424
Conversation
|
@richmolj This concludes my blast of PRs for a while 😝 #422 and #423 were just leading to this in order to keep things topical. There might be a few things still to add to this, but thought this was a good enough starting point to talk about it. This has made a huuuugge difference in my app, and I'm super stoked about this addition. json-api resources has a kinda-similar caching strategy, but with more configuration. I liked how they defined it on the Resource definition. I initially had it as a jsonapi-renderer which we're leveraging for rendering has a caching strategy that I'm specifically working around in this PR, because I couldn't get it to work at all. The idea of individually caching each resource response fragment sounds good in theory, but cutting it off at this point made sense until a reason to do otherwise presented itself |
Changes: - add sideload-respecting `cache_key`, `cache_key_with_version`, 'updated_at`, and `etag` methods to resource instances. - add `cache_resource` method to Resource definition - wrap rendering logic in `Rails.cache.fetch(@resource.cache_key, version: @resource.updated_at)` (for cache_resources) to re-use cache keys by default, and dramatically improve rendering response times (because you know, caching)
…y. Use Graphiti.cache= to configure cache store
stale? in the controller
# [1.7.0](v1.6.4...v1.7.0) (2024-03-27) ### Features * Add support for caching renders in Graphiti, and better support using etags and stale? in the controller ([#424](#424)) ([8bae50a](8bae50a))
|
🎉 This PR is included in version 1.7.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
This PR exposes some things that make supporting using rails built-in caching methods like
stale?with Graphiti a snap, and also allowing the option of caching the render of a graphiti resource (which is often the most time-consuming part of the process)Changes:
expose
cache_keymethod to resource instancethis generates a combined stable cache key based on resource identifiers specified by the resource class, the specified sideloads, and any specified
extra_fieldsorfields,pages, orlinkswhich will affect the response.expose
cache_key_with_versionmethod to resource instancesame as above, but with the last modified dates added in. If any included resource's
updated_atchanges, this key will change.expose
updated_atmethod to resource instancereturns the max
updated_atdate of the resource and any specified sideloadsexpose
etagmethod to resource instancegenerate a Weak Etag based on the
cache_key_with_versionresponse. Withetagandupdated_atmethods on a resource instance, usingstale?(@resource)will respect them.for cached resources, rendering logic in Graphiti is wrapped in a cache block
Graphiti.cache.fetch("graphiti:render/#{@resource.cache_key}", version: @resource.updated_at, expires_in: @resource.expires_in ) { [expensive rendering] }.(Using
cache_keyandversiontogether by default instead of usingcache_key_with_versionas the key better ensures we won’t flood a cache store with dead keys)Using Rails etags and
stale?You can now easily benefit from using rails etags without any additional logic by using the
stale?method and passing in your resource.Cache the rendered json
You can also cache the json rendering-step of the resource, which in the case of json-api can sometimes be expensive. In order to cache a resource set up a cache store, enable
cache_rendering, and then add acache_resourcedirective to the resource you want to cache. For complex resources with many sideloads, this can improve your response time dramatically.Debugging
With the debug flag enabled in Graphiti config extra information will be logged. This logic tries to make caching the render loop of graphiti dead simple, but sometimes a query with an argument that changes often (a relative time, like
Time.nowfor example) will create an unstable cache key, negating any potential benefits. This problems are a little tricky to spot, but the debugging code below should help.// stable key
// volatile key
The above illustrates that there have been 11 requests but only 1 of those was pulled from the cache, which in the above query's case is because the query was using
Time.nowas an argument.