Skip to content

Commit

Permalink
Preload associations :inverse_of for VimPerformanceState
Browse files Browse the repository at this point in the history
For Metrics rollup, we fetch the `container_image` from each of the `container` objects.
This change preloads the parent `container_image` value into the `containers` so we avoid the N+1.

before
======

```
container_image = ContainerImage.first
containers = container_image.vim_performance_state(timestamp).containers
containers.map(&:container_image) # N+1
```

after
=====

```
container_image = ContainerImage.first
containers = container_image.vim_performance_state(timestamp).containers
containers.map(&:container_image) # ==> array of container_image object
```
  • Loading branch information
kbrock committed Jun 29, 2023
1 parent 86356ad commit 1b2e2f9
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 1 deletion.
2 changes: 1 addition & 1 deletion app/models/metric/ci_mixin/state_finders.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ def vim_performance_state_association(ts, assoc)
return respond_to?(:miq_regions) ? miq_regions : []
end

vim_performance_state_for_ts(ts).public_send(assoc)
vim_performance_state_for_ts(ts).public_send(assoc).tap { |ret| MiqPreloader.preload_reverse(self, assoc, ret) }
end
end
17 changes: 17 additions & 0 deletions lib/miq_preloader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ def self.preload(records, associations, preload_scope = nil)
preloader.preload(records, associations, preload_scope)
end

# If you don't use an association to load records, this method can be used
# to assign the parent record to the association
#
# example:
# vms = Vm.where(:ems_id => ems.id)
# preload_reverse(ems, :ems, vms)
# ==>
# vms.ems is cached
#
# also of interest:
# reverse_association = record.class.reflect_on_association(association).inverse_of.name
# ra = value.association(reverse_association) # vms.ems
def self.preload_reverse(record, association, values)
fa = record.association(association.to_sym)
values.each { |value| fa.set_inverse_instance(value) } # do we want to use _from_query?
end

# it will load records and their associations, and return the children
#
# instead of N+1:
Expand Down
16 changes: 16 additions & 0 deletions spec/lib/miq_preloader_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ def preload(*args)
end
end

describe ".preload_reverse" do
it "loads belongs_to in a reverse" do
ems = FactoryBot.create(:ems_infra)
vms = FactoryBot.create_list(:vm, 2, :ext_management_system => ems)

vms.each(&:reload)
expect(vms.first.association(:ext_management_system)).not_to be_loaded
expect { vms.map(&:ext_management_system) }.to make_database_queries

vms.each(&:reload)
expect { MiqPreloader.preload_reverse(ems, :vms, vms) }.not_to make_database_queries
expect(vms.first.association(:ext_management_system)).to be_loaded
expect { vms.map(&:ext_management_system) }.not_to make_database_queries
end
end

describe ".preload_and_map" do
it "preloads from an object" do
ems = FactoryBot.create(:ems_infra)
Expand Down

0 comments on commit 1b2e2f9

Please sign in to comment.