Only preload misses on multifetch cache#31250
Conversation
|
r? @eileencodes (@rails-bot has picked a reviewer for you, use r? to override) |
|
Wow, this is super neat! The tight coupling does seem less than ideal, but the basic principle of highlighting the subset of records that are "active" and only applying the preload to those is great! |
rafaelfranca
left a comment
There was a problem hiding this comment.
We need to fix a lot of rubocop violations
There was a problem hiding this comment.
can we make the arguments always required?
There was a problem hiding this comment.
collection_cache_association_loading.
|
I think the coupling is fine at this level. Of course, it would be better if we can find a way to avoid it, but if not we are using the right mechanisms and it is similar to what we are doing with the controller runtime logs. |
1309daa to
5f1e76f
Compare
|
@rafaelfranca The rubocop violations have been fixed and other suggestions implemented. |
5f1e76f to
d04a32f
Compare
|
Thanks! |
…ion-error-with-http-caching-and-collection-caching" This reverts commit 8b0cdbe. Reason: #31250 is not backported to 5-2-stable, and this change broke build. https://travis-ci.org/rails/rails/builds/365396137
…r-with-http-caching-and-collection-caching Fix ActiveRecord::ImmutableRelation is raised when collection caching and HTTP caching are used together
|
This PR creates a potential problem if you are using a custom cache key for the @view.render partial: "test/partial", collection: @post, cached: proc { |post| [post, post.author] }In this case it might not sense to use Russian-doll caching, as in, you may not want to update every single But if you do this you get an n+1 on I think this PR is great if you do |
|
@ghiculescu can you open a new issue with a reproduction? Or a PR with a repro that fixes this? Otherwise the notification from this will get lost and forgotten. |
|
@eileencodes I haven't tested it thoroughly but this is what I had in mind: #44616 |
rails#31250 optimised rendering a collection with `cached: true`, but also with `cached: proc {}`. The problem is the proc may reference associations that should be preloaded to avoid n+1s in generating the cache key. For example: ```ruby @posts = Post.preload(:author) @view.render partial: "test/partial", collection: @post, cached: proc { |post| [post, post.author] } ``` This will n+1 on `post.author`. To fix this, this PR will always preload the collection if there's a proc given for `cached`. `cached: true` will still not preload unless it renders, as it did in rails#31250.
Took a bit longer than I planned, but this is now a green build and should be a fix for the issue 👍 |
Summary
When rendering a collection with
cached: truefor anActiveRecord::Relationthat haspreloadvalues, the preload would normally only be required for rendering the partial when it misses the cache and nor required for generating the cache key.Currently, we have to chose between using preload, which loads unused
ActiveRecordinstances when the have a high cache hit rate, or not preloading the associations, which can result inN + 1queries when there are a large number of cache misses.This PR attempts to optimise this be only preloading the associations for the records that miss the cache.
An example application to demonstrate this behaviour is at https://github.com/lsylvester/multifetch-demo. An example from the log demonstrating this behaviour is:
Without this change, we would either so a query for each of the categories that miss the cache, or have to load products for every category.
Concerns and Further Work
There may be instances where we want to preload the associations for generation of cache keys, or we may want to have all of the associations loaded because the collection is used in other contexts other than the current rendering.
Currently there is no API to support this, but it is possible to acheive the load behaviour by calling
loadon the association before it is passed to therendercall.I am looking for feedback on:
loadon the relation before rendering a suffient API for opting out, or should an additional option to the render call be added.If it is ok to be default behaviour then I assume that I would need to add some application config to control this behavour to prevent breaking existing application and to provide deprication warnings.
I am also not sure about the structure of this code is it is very tightly connected to both ActionView and ActiveRecord, so any feedback in that area is welcome.