Skip to content

Commit

Permalink
specs for MiqPreloader
Browse files Browse the repository at this point in the history
added docs around using a scope to populate the relation
This is only used in one place by rollups.

I had thought an array would work, but it does not.
  • Loading branch information
kbrock committed Jul 3, 2023
1 parent 6482c01 commit 386f572
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 14 deletions.
27 changes: 26 additions & 1 deletion lib/miq_preloader.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
module MiqPreloader
# If you want to preload an association on multiple records
# or want to only load a subset of an association
#
# @example Preloading vms on a set of emses
# vms_scope = Vm.where(:ems_id => emses.id)
# preload(emses, :vms, vms_scope)
# emses.map { |ems| ems.vms } # cached - no queries
# vms_scope.first.ems # cached - the reversed association is cached
#
# @example Programmatically determine the reverse association name
# Going from Ems#association(:vms) and going to Vm#association(:ems)
#
# reverse_association_name = record.class.reflect_on_association(association).inverse_of.name
# reverse_association = result.association(reverse_association_name)
#
# @param record [relation|ActiveRecord::Base|Array[ActiveRecord::Base]]
# @param association [Symbol|Hash|Array] name of the association(s)
# @param preload_scope [Nil|relation] Relation of the records to be use for preloading
# For all but one case, default behavior is to use the association
# Alternatively a scope can be used.
# Currently an array does not work
# @return [Array<ActiveRecord::Base>] records
def self.preload(records, associations, preload_scope = nil)
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(records, associations, preload_scope)
Expand All @@ -10,12 +32,15 @@ def self.preload(records, associations, preload_scope = nil)
# orchestration_stack.subtree.flat_map(&:direct_vms)
# use instead:
# preload_and_map(orchestration_stack.subtree, :direct_vms)
#
# @param records [ActiveRecord::Base, Array<ActiveRecord::Base>, Object, Array<Object>]
# @param association [Symbol] name of the association
def self.preload_and_map(records, association)
Array.wrap(records).tap { |recs| MiqPreloader.preload(recs, association) }.flat_map(&association)
end

# @param records [ActiveRecord::Base, Array<ActiveRecord::Base>, Object, Array<Object>]
# @param association [String] an association on records
# @param association_name [Symbol] Name of the association
def self.preload_and_scope(records, association_name)
records = Array.wrap(records) unless records.kind_of?(Enumerable)
active_record_klass = records.respond_to?(:klass) ? records.klass : records.first.class
Expand Down
39 changes: 26 additions & 13 deletions spec/lib/miq_preloader_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,35 @@
ems = FactoryBot.create(:ems_infra)
expect(ems.vms).not_to be_loaded
expect { preload(ems, :vms) }.to make_database_queries(:count => 1)

expect(ems.vms).to be_loaded
expect { preload(ems, :vms) }.to_not make_database_queries
expect { preload(ems, :vms) }.not_to make_database_queries
expect { ems.vms.size }.not_to make_database_queries
end

it "preloads from an array" do
it "preloads an array" do
emses = FactoryBot.create_list(:ems_infra, 2)
expect { preload(emses, :vms) }.to make_database_queries(:count => 1)
expect(emses[0].vms).to be_loaded

expect(emses.first.vms).to be_loaded
expect { emses.first.vms.size }.not_to make_database_queries
end

it "preloads from an association" do
it "preloads a relation (record is a relation)" do
FactoryBot.create_list(:vm, 2, :ext_management_system => FactoryBot.create(:ems_infra))
emses = ExtManagementSystem.all
expect { preload(emses, :vms) }.to make_database_queries(:count => 2)

expect { expect(emses.first.vms.size).to eq(2) }.not_to make_database_queries
end

it "preloads with a relation (records is a relation)" do
ems = FactoryBot.create(:ems_infra)
FactoryBot.create_list(:vm, 2, :ext_management_system => ems)

emses = ExtManagementSystem.all
expect { preload(emses, :vms) }.to make_database_queries(:count => 2)
emses = ExtManagementSystem.all.load
vmses = Vm.where(:ems_id => emses.select(:id))
expect { preload(emses, :vms, vmses) }.to make_database_queries(:count => 1)
end

def preload(*args)
Expand All @@ -34,7 +47,7 @@ def preload(*args)

vms = nil
expect { vms = preload_and_map(ems, :vms) }.to make_database_queries(:count => 1)
expect { expect(vms.size).to eq(2) }.to_not make_database_queries
expect { expect(vms.size).to eq(2) }.not_to make_database_queries
end

it "preloads from an association" do
Expand All @@ -56,7 +69,7 @@ def preload_and_map(*args)
FactoryBot.create_list(:vm, 2, :ext_management_system => ems)

vms = nil
expect { vms = preload_and_scope(ems, :vms) }.to_not make_database_queries
expect { vms = preload_and_scope(ems, :vms) }.not_to make_database_queries
expect { expect(vms.count).to eq(2) }.to make_database_queries(:count => 1)
end

Expand All @@ -65,7 +78,7 @@ def preload_and_map(*args)
FactoryBot.create(:template, :ext_management_system => FactoryBot.create(:ems_infra))

vms = nil
expect { vms = preload_and_scope(ExtManagementSystem.all, :vms_and_templates) }.to_not make_database_queries
expect { vms = preload_and_scope(ExtManagementSystem.all, :vms_and_templates) }.not_to make_database_queries
expect { expect(vms.count).to eq(3) }.to make_database_queries(:count => 1)
end

Expand All @@ -86,8 +99,8 @@ def preload_and_map(*args)

hosts = nil
vms = nil
expect { vms = preload_and_scope(ExtManagementSystem.all, :vms_and_templates) }.to_not make_database_queries
expect { hosts = preload_and_scope(vms, :host) }.to_not make_database_queries
expect { vms = preload_and_scope(ExtManagementSystem.all, :vms_and_templates) }.not_to make_database_queries
expect { hosts = preload_and_scope(vms, :host) }.not_to make_database_queries
expect { expect(hosts.count).to eq(2) }.to make_database_queries(:count => 1)
end

Expand All @@ -103,8 +116,8 @@ def preload_and_map(*args)

emses = nil
vms = nil
expect { emses = preload_and_scope(Host.all, :ext_management_system) }.to_not make_database_queries
expect { vms = preload_and_scope(emses, :vms) }.to_not make_database_queries
expect { emses = preload_and_scope(Host.all, :ext_management_system) }.not_to make_database_queries
expect { vms = preload_and_scope(emses, :vms) }.not_to make_database_queries
expect { expect(vms.count).to eq(3) }.to make_database_queries(:count => 1)
end

Expand Down

0 comments on commit 386f572

Please sign in to comment.