openfoodfoundation / openfoodnetwork

Connect suppliers, distributors and consumers to trade local produce.
https://www.openfoodnetwork.org
GNU Affero General Public License v3.0
1.11k stars 719 forks source link

N+1 issues in Admin::SubscriptionsController#index #8694

Open Matt-Yorkley opened 2 years ago

Matt-Yorkley commented 2 years ago

What we should change and why (this is tech debt)

We've got 4 different N+1 issues in the Admin::Subscriptions#index action. For users that only manage few/small enterprises this isn't a big deal, but for users which manage many/large enterprises it can become a serious issue (for the user and for the server).

Context

Partly contributed to some UK downtime (Jan 10th).

Impact and timeline

Needs some improvement ASAP!

Matt-Yorkley commented 2 years ago

Notes and backtraces

High-impact N+1 on loading tags:

Backtrace:
app/serializers/api/admin/payment_method/base_serializer.rb:10:in `tag_list'
app/serializers/api/admin/payment_method_serializer.rb:6:in `serializable_hash'
app/helpers/admin/injection_helper.rb:209:in `admin_inject_json_ams_array'
app/views/admin/subscriptions/_data.html.haml:5 

Query example(s):
SELECT "taggings".* FROM "taggings" WHERE "taggings"."taggable_id" = $1 AND "taggings"."taggable_type" = $2;
SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = $1 AND "taggings"."taggable_type" = $2 AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL);

High-impact N+1 on payment method calculators:

Backtrace:
app/models/spree/payment_method.rb:106:in `init'
app/helpers/admin/injection_helper.rb:209:in `admin_inject_json_ams_array'
app/views/admin/subscriptions/_data.html.haml:5

Query example:
SELECT "spree_calculators".* FROM "spree_calculators" WHERE "spree_calculators"."calculable_id" = $1 AND "spree_calculators"."calculable_type" = $2 LIMIT $3;

Small-medium impact N+1 on enterprises / exhanges:

Backtrace:
app/helpers/admin/injection_helper.rb:209:in `admin_inject_json_ams_array'
app/views/admin/subscriptions/_data.html.haml:7

Query example: 
SELECT DISTINCT "enterprises".* FROM "enterprises" INNER JOIN "exchanges" ON "enterprises"."id" = "exchanges"."receiver_id" WHERE "exchanges"."order_cycle_id" = $1 AND "exchanges"."incoming" = $2; 

Small-medium impact N+1 around checks in #subscriptions_setup_complete?:

Backtrace:
app/helpers/admin/subscriptions_helper.rb:8:in `block in subscriptions_setup_complete?'
app/helpers/admin/subscriptions_helper.rb:8:in `subscriptions_setup_complete?'
app/controllers/admin/subscriptions_controller.rb:18:in `block (2 levels) in index'

Query example(s):
SELECT 1 AS one FROM "spree_shipping_methods" INNER JOIN "distributors_shipping_methods" ON "spree_shipping_methods"."id" = "distributors_shipping_methods"."shipping_method_id" WHERE "spree_shipping_methods"."deleted_at" IS NULL AND "distributors_shipping_methods"."distributor_id" = $1 LIMIT $2; 
SELECT 1 AS one FROM "spree_payment_methods" INNER JOIN "distributors_payment_methods" ON "spree_payment_methods"."id" = "distributors_payment_methods"."payment_method_id" WHERE "spree_payment_methods"."deleted_at" IS NULL AND "distributors_payment_methods"."distributor_id" = $1 AND "spree_payment_methods"."type" IN ($2, $3) LIMIT $4; 
Matt-Yorkley commented 2 years ago

Note on N+1 issues with loading tags specifically; see this previous issue for more detail (and potential solution).