1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Only preload misses on multifetch cache

This commit is contained in:
Lachlan Sylvester 2017-11-21 15:38:21 +11:00
parent a3b7aa66bc
commit d04a32fe67
6 changed files with 101 additions and 9 deletions

View file

@ -0,0 +1,35 @@
# frozen_string_literal: true
require "active_record_unit"
require "active_record/railties/collection_cache_association_loading"
ActionView::PartialRenderer.prepend(ActiveRecord::Railties::CollectionCacheAssociationLoading)
class MultifetchCacheTest < ActiveRecordTestCase
fixtures :topics, :replies
def setup
view_paths = ActionController::Base.view_paths
@view = Class.new(ActionView::Base) do
def view_cache_dependencies
[]
end
def combined_fragment_cache_key(key)
[ :views, key ]
end
end.new(view_paths, {})
end
def test_only_preloading_for_records_that_miss_the_cache
@view.render partial: "test/partial", collection: [topics(:rails)], cached: true
@topics = Topic.preload(:replies)
@view.render partial: "test/partial", collection: @topics, cached: true
assert_not @topics.detect { |topic| topic.id == topics(:rails).id }.replies.loaded?
assert @topics.detect { |topic| topic.id != topics(:rails).id }.replies.loaded?
end
end

View file

@ -157,6 +157,14 @@ end_warning
end end
end end
initializer "active_record.collection_cache_association_loading" do
require "active_record/railties/collection_cache_association_loading"
ActiveSupport.on_load(:action_view) do
ActionView::PartialRenderer.prepend(ActiveRecord::Railties::CollectionCacheAssociationLoading)
end
end
initializer "active_record.set_reloader_hooks" do initializer "active_record.set_reloader_hooks" do
ActiveSupport.on_load(:active_record) do ActiveSupport.on_load(:active_record) do
ActiveSupport::Reloader.before_class_unload do ActiveSupport::Reloader.before_class_unload do

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
module ActiveRecord
module Railties # :nodoc:
module CollectionCacheAssociationLoading #:nodoc:
def setup(context, options, block)
@relation = relation_from_options(options)
super
end
def relation_from_options(cached: nil, partial: nil, collection: nil, **_)
return unless cached
relation = partial if partial.is_a?(ActiveRecord::Relation)
relation ||= collection if collection.is_a?(ActiveRecord::Relation)
if relation && !relation.loaded?
relation.skip_preloading!
end
end
def collection_without_template
@relation.preload_associations(@collection) if @relation
super
end
def collection_with_template
@relation.preload_associations(@collection) if @relation
super
end
end
end
end

View file

@ -8,7 +8,8 @@ module ActiveRecord
:extending, :unscope] :extending, :unscope]
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
:reverse_order, :distinct, :create_with, :skip_query_cache] :reverse_order, :distinct, :create_with, :skip_query_cache,
:skip_preloading]
CLAUSE_METHODS = [:where, :having, :from] CLAUSE_METHODS = [:where, :having, :from]
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having] INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
@ -546,6 +547,16 @@ module ActiveRecord
ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins) ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
end end
def preload_associations(records)
preload = preload_values
preload += includes_values unless eager_loading?
preloader = nil
preload.each do |associations|
preloader ||= build_preloader
preloader.preload records, associations
end
end
protected protected
def load_records(records) def load_records(records)
@ -575,13 +586,7 @@ module ActiveRecord
klass.find_by_sql(arel, &block).freeze klass.find_by_sql(arel, &block).freeze
end end
preload = preload_values preload_associations(@records) unless skip_preloading_value
preload += includes_values unless eager_loading?
preloader = nil
preload.each do |associations|
preloader ||= build_preloader
preloader.preload @records, associations
end
@records.each(&:readonly!) if readonly_value @records.each(&:readonly!) if readonly_value

View file

@ -899,6 +899,11 @@ module ActiveRecord
self self
end end
def skip_preloading! # :nodoc:
self.skip_preloading_value = true
self
end
# Returns the Arel object associated with the relation. # Returns the Arel object associated with the relation.
def arel(aliases = nil) # :nodoc: def arel(aliases = nil) # :nodoc:
@arel ||= build_arel(aliases) @arel ||= build_arel(aliases)

View file

@ -59,7 +59,7 @@ module ActiveRecord
assert_equal [], relation.extending_values assert_equal [], relation.extending_values
end end
(Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :skip_query_cache]).each do |method| (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :skip_query_cache, :skip_preloading]).each do |method|
test "##{method}!" do test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation) assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal :foo, relation.public_send("#{method}_value") assert_equal :foo, relation.public_send("#{method}_value")
@ -137,6 +137,11 @@ module ActiveRecord
assert relation.skip_query_cache_value assert relation.skip_query_cache_value
end end
test "skip_preloading!" do
relation.skip_preloading!
assert relation.skip_preloading_value
end
private private
def relation def relation
@relation ||= Relation.new(FakeKlass) @relation ||= Relation.new(FakeKlass)