You know that feeling when you open a page and it seems to be loading straight from the last decade? Well... that's exactly what we were experiencing on our product page. One of the bottlenecks was the stock verification for each product variant. This was being done synchronously, meaning the page would only load after checking the stock for each variant. Asynchronous Separation The first improvement was structural. I made this part of the page asynchronous, using turbo_frame_tag with src and loading: :lazy. This allowed the main content to render quickly, while the stock data and product selection form loaded separately, enhancing the user's perception of speed. Misleading Abstractions Even with asynchronous loading, the requests were still heavy. I investigated and found we were doing this: product_variant.total_available_quantity It seemed harmless... until I saw what was behind it: def total_available_quantity Factory.total_available_quantity(finished_sku: sku) end Digging deeper, I found this: class Factory # ... def self.total_available_quantity(finished_sku:) stock_skus = SkuMapper.where(finished_sku: finished_sku, enabled: true).pluck(:stock_sku) FactoryStock.where(stock_sku: stock_skus, enabled: true).sum(:available_quantity) end # ... end In other words, for each variant, we were making at least two queries—one to SkuMapper and another to FactoryStock—to sum up the stock. Imagine this running on a product with 15 or 25 variants... Anticipate and Map Instead of repeating these queries, I fetched all the finished_sku for the variants and then created two hashes with the anticipated data: The first maps the variant's finished_sku to the stock_sku from SkuMapper: { finished_sku: stock_sku } The second maps the stock_sku from FactoryStock to its available quantity: { stock_sku: available_quantity } With this, we consolidated the queries and transformed what were multiple where clauses into simple key lookups in the hash. Result On the product page, we saw a reduction of nearly 85% in response time, going from 983.7 ms to 141.7 ms. Additionally, we reduced the number of queries from 206 to 37. Before and after in NewRelic: And on the page now responsible for delivering all this information for users to choose their product, we achieved 149.2 ms with just 9 SQL queries. How the new page looks in NewRelic:

You know that feeling when you open a page and it seems to be loading straight from the last decade? Well... that's exactly what we were experiencing on our product page.
One of the bottlenecks was the stock verification for each product variant. This was being done synchronously, meaning the page would only load after checking the stock for each variant.
Asynchronous Separation
The first improvement was structural. I made this part of the page asynchronous, using turbo_frame_tag
with src and loading: :lazy
. This allowed the main content to render quickly, while the stock data and product selection form loaded separately, enhancing the user's perception of speed.
Misleading Abstractions
Even with asynchronous loading, the requests were still heavy. I investigated and found we were doing this:
product_variant.total_available_quantity
It seemed harmless... until I saw what was behind it:
def total_available_quantity
Factory.total_available_quantity(finished_sku: sku)
end
Digging deeper, I found this:
class Factory
# ...
def self.total_available_quantity(finished_sku:)
stock_skus = SkuMapper.where(finished_sku: finished_sku, enabled: true).pluck(:stock_sku)
FactoryStock.where(stock_sku: stock_skus, enabled: true).sum(:available_quantity)
end
# ...
end
In other words, for each variant, we were making at least two queries—one to SkuMapper
and another to FactoryStock
—to sum up the stock. Imagine this running on a product with 15 or 25 variants...
Anticipate and Map
Instead of repeating these queries, I fetched all the finished_sku
for the variants and then created two hashes with the anticipated data:
- The first maps the variant's
finished_sku
to thestock_sku
fromSkuMapper
:
{
finished_sku: stock_sku
}
- The second maps the
stock_sku
fromFactoryStock
to its available quantity:
{
stock_sku: available_quantity
}
With this, we consolidated the queries and transformed what were multiple where clauses into simple key lookups in the hash.
Result
On the product page, we saw a reduction of nearly 85% in response time, going from 983.7 ms to 141.7 ms. Additionally, we reduced the number of queries from 206 to 37.
Before and after in NewRelic:
And on the page now responsible for delivering all this information for users to choose their product, we achieved 149.2 ms with just 9 SQL queries.
How the new page looks in NewRelic: