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?)
@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
# load example pet types for the colors that don't have paint brushes.
@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)]
end.to_h
# Finish loading the NC trade values.
trade_values_task.wait
render layout: "application"
end

View file

@ -1,3 +1,6 @@
require "async"
require "async/barrier"
class Item < ApplicationRecord
include PrettyParam
@ -114,15 +117,25 @@ class Item < ApplicationRecord
def nc_trade_value
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)
rescue OwlsValueGuide::NotFound => error
Rails.logger.debug("No NC trade value listed for #{name} (#{id})")
return nil
nil
rescue OwlsValueGuide::NetworkError => error
Rails.logger.error("Couldn't load nc_trade_value: #{error.full_message}")
return nil
nil
end
@nc_trade_value_loaded = true
@nc_trade_value
end
# Return an OrderedHash mapping users to the number of times they
@ -599,6 +612,27 @@ class Item < ApplicationRecord
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)
# bear in mind that registries are arrays with many nil elements,
# due to how the parser works