Skip to content

Commit dab997f

Browse files
ikraamgmamhoff
authored andcommitted
Fixed N+1 adjustment loading in promotion recalculation
Recalculating promotions loaded each line item's and shipment's adjustments with a separate query, so the adjustment query count grew with the number of items in the order. This runs on every order recalculation. Preloading the adjustments for all items in one batch keeps the query count constant regardless of order size. The same fix applies to both the legacy and the new promotion adjusters.
1 parent b6fbbe7 commit dab997f

4 files changed

Lines changed: 30 additions & 0 deletions

File tree

legacy_promotions/app/models/spree/promotion/order_adjustments_recalculator.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def initialize(order)
1515

1616
def call(persist: true)
1717
all_items = line_items + shipments
18+
ActiveRecord::Associations::Preloader.new(records: all_items, associations: :adjustments).call
1819
all_items.each do |item|
1920
promotion_adjustments = item.adjustments.select(&:promotion?)
2021

legacy_promotions/spec/models/spree/promotion/order_adjustments_recalculator_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ def initialize(_adjustments)
1818
let(:order) { create(:order_with_line_items, line_items_count: 1, line_items_price: 10) }
1919
let(:line_item) { order.line_items[0] }
2020

21+
context "with multiple line items" do
22+
let(:order) { create(:order_with_line_items, line_items_count: 3) }
23+
24+
before { order.reload }
25+
26+
it "loads line item adjustments in a single query" do
27+
expect { subject }.to make_database_queries(matching: /from .spree_adjustments..*adjustable_id. IN \(/im, count: 1)
28+
end
29+
end
30+
2131
context "when the quantity changes with a CreateQuantityAdjustments promotion" do
2232
let(:promotion) { create(:promotion, promotion_actions: [promotion_action]) }
2333
let(:promotion_action) { Spree::Promotion::Actions::CreateQuantityAdjustments.new(calculator:, preferred_group_size: 2) }

promotions/app/models/solidus_promotions/order_adjuster.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def initialize(order, dry_run_promotion: nil)
2222
def call(persist: true) # rubocop:disable Lint/UnusedMethodArgument
2323
return order unless SolidusPromotions::Promotion.order_activatable?(order)
2424

25+
ActiveRecord::Associations::Preloader.new(records: order.line_items + order.shipments, associations: :adjustments).call
26+
2527
SetDiscountsToZero.call(order)
2628

2729
DiscountOrder.new(order, promotions, dry_run: dry_run).call

promotions/spec/models/solidus_promotions/order_adjuster_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,21 @@
237237
end
238238
end
239239
end
240+
241+
context "with multiple line items" do
242+
let(:order) { create(:order_with_line_items, line_items_count: 3) }
243+
let!(:benefit) do
244+
SolidusPromotions::Benefits::AdjustLineItem.create(promotion: promotion, calculator: calculator)
245+
end
246+
247+
before do
248+
order_adjuster.call
249+
order.save!
250+
order.reload
251+
end
252+
253+
it "loads line item adjustments in a single query" do
254+
expect { order_adjuster.call }.to make_database_queries(matching: /from .spree_adjustments..*adjustable_id. IN \(/im, count: 1)
255+
end
256+
end
240257
end

0 commit comments

Comments
 (0)