mirror of
https://github.com/we-promise/sure.git
synced 2026-07-25 19:22:14 +00:00
perf(exchange_rate): batch-fetch rates to fix N+1 query (#2676)
Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com>
This commit is contained in:
@@ -56,14 +56,44 @@ module ExchangeRate::Provided
|
||||
# Batch-fetches exchange rates for multiple source currencies.
|
||||
# Returns a hash mapping each currency to its numeric rate, defaulting to 1 when unavailable.
|
||||
def rates_for(currencies, to:, date: Date.current)
|
||||
currencies.uniq.each_with_object({}) do |currency, map|
|
||||
unique_currencies = currencies.uniq
|
||||
return {} if unique_currencies.empty?
|
||||
|
||||
# Batch-load exact-date matches in a single query
|
||||
exact_rates = where(from_currency: unique_currencies, to_currency: to, date: date)
|
||||
.index_by(&:from_currency)
|
||||
|
||||
missing = unique_currencies - exact_rates.keys
|
||||
|
||||
# For currencies without an exact match, batch-load the nearest recent rate
|
||||
nearest_rates = if missing.any?
|
||||
where(from_currency: missing, to_currency: to)
|
||||
.where(date: (date - NEAREST_RATE_LOOKBACK_DAYS)..date)
|
||||
.order(date: :desc)
|
||||
.to_a
|
||||
.each_with_object({}) do |r, map|
|
||||
map[r.from_currency] ||= r # keep most-recent (first due to ORDER BY date DESC)
|
||||
end
|
||||
else
|
||||
{}
|
||||
end
|
||||
|
||||
still_missing = missing - nearest_rates.keys
|
||||
|
||||
# Only hit the provider for currencies with no cached rate at all
|
||||
fetched_rates = still_missing.each_with_object({}) do |currency, map|
|
||||
rate = find_or_fetch_rate(from: currency, to: to, date: date)
|
||||
map[currency] = rate if rate
|
||||
end
|
||||
|
||||
unique_currencies.each_with_object({}) do |currency, result|
|
||||
rate = exact_rates[currency] || nearest_rates[currency] || fetched_rates[currency]
|
||||
if rate.nil?
|
||||
Rails.logger.warn("No exchange rate found for #{currency}/#{to} on #{date}, using 1")
|
||||
elsif rate.date != date
|
||||
Rails.logger.debug("FX rate #{currency}/#{to}: using #{rate.date} for #{date} (gap=#{(date - rate.date).to_i}d)")
|
||||
end
|
||||
map[currency] = rate&.rate || 1
|
||||
result[currency] = rate&.rate || 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user