Improve performance of Owls values in Item Getting Guide

Now we preload them all concurrently, instead of in sequence when the
template gets around to asking for them!
This commit is contained in:
Emi Matchu 2024-05-27 16:21:22 -07:00
parent 551e8941f3
commit 758b62e7d5
2 changed files with 43 additions and 3 deletions

View file

@ -128,6 +128,9 @@ class ItemsController < ApplicationController
@np_items = @items.select(&:np?) @np_items = @items.select(&:np?)
@pb_items = @items.select(&:pb?) @pb_items = @items.select(&:pb?)
# Start loading the NC trade values for the non-Mall NC items.
trade_values_task = Async { Item.preload_nc_trade_values(@other_nc_items) }
# Also, PB items have some special handling: we group them by color, then # Also, PB items have some special handling: we group them by color, then
# load example pet types for the colors that don't have paint brushes. # load example pet types for the colors that don't have paint brushes.
@pb_items_by_color = @pb_items.group_by(&:pb_color). @pb_items_by_color = @pb_items.group_by(&:pb_color).
@ -144,6 +147,9 @@ class ItemsController < ApplicationController
[color, color.example_pet_type(preferred_species: species)] [color, color.example_pet_type(preferred_species: species)]
end.to_h end.to_h
# Finish loading the NC trade values.
trade_values_task.wait
render layout: "application" render layout: "application"
end end

View file

@ -1,3 +1,6 @@
require "async"
require "async/barrier"
class Item < ApplicationRecord class Item < ApplicationRecord
include PrettyParam include PrettyParam
@ -114,15 +117,25 @@ class Item < ApplicationRecord
def nc_trade_value def nc_trade_value
return nil unless nc? return nil unless nc?
begin
# Load the trade value, if we haven't already. Note that, because the trade
# value may be nil, we also save an explicit boolean for whether we've
# already looked it up, rather than checking if the saved value is empty.
return @nc_trade_value if @nc_trade_value_loaded
@nc_trade_value = begin
Rails.logger.debug "Item #{id} (#{name}) <lookup>"
OwlsValueGuide.find_by_name(name) OwlsValueGuide.find_by_name(name)
rescue OwlsValueGuide::NotFound => error rescue OwlsValueGuide::NotFound => error
Rails.logger.debug("No NC trade value listed for #{name} (#{id})") Rails.logger.debug("No NC trade value listed for #{name} (#{id})")
return nil nil
rescue OwlsValueGuide::NetworkError => error rescue OwlsValueGuide::NetworkError => error
Rails.logger.error("Couldn't load nc_trade_value: #{error.full_message}") Rails.logger.error("Couldn't load nc_trade_value: #{error.full_message}")
return nil nil
end end
@nc_trade_value_loaded = true
@nc_trade_value
end end
# Return an OrderedHash mapping users to the number of times they # Return an OrderedHash mapping users to the number of times they
@ -599,6 +612,27 @@ class Item < ApplicationRecord
end end
end end
def self.preload_nc_trade_values(items)
# Only allow 10 trade values to be loaded at a time.
barrier = Async::Barrier.new
semaphore = Async::Semaphore.new(10, parent: barrier)
Sync do
# Load all the trade values in concurrent async tasks. (The
# `nc_trade_value` caches the value in the Item object.)
items.each do |item|
semaphore.async { item.nc_trade_value }
end
# Wait until all tasks are done.
barrier.wait
ensure
barrier.stop # If something goes wrong, clean up all tasks.
end
items
end
def self.collection_from_pet_type_and_registries(pet_type, info_registry, asset_registry, scope=Item.all) def self.collection_from_pet_type_and_registries(pet_type, info_registry, asset_registry, scope=Item.all)
# bear in mind that registries are arrays with many nil elements, # bear in mind that registries are arrays with many nil elements,
# due to how the parser works # due to how the parser works