Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch-loader or preloading with destroy and dependent: :destroy associations N+1 problems. #10

Open
ghost opened this issue Dec 28, 2017 · 0 comments

Comments

@ghost
Copy link

ghost commented Dec 28, 2017

In Rails there is the model.destroy(id) command to delete (with callbacks) a model. Good.

But when you use dependent: :destroy with has_many or has_one it has fun never ending with N+1 queries!!! :(

Especially if I use polymorhpic associations.

See this:

Started POST "/api/v1" for 172.18.0.1 at 2017-12-28 00:05:38 +0000
Processing by GraphqlController#execute as */*
  Parameters: {"query"=>"mutation deleteProductMutation {\n  deleteProduct(id: 1) {\n    id\n  }\n}\n", "graphql"=>{"query"=>"mutation deleteProductMutation {\n  deleteProduct(id: 1) {\n    id\n  }\n}\n"}}
  User Load (1.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Product Load (0.4ms)  SELECT  "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (0.3ms)  BEGIN
  Team Load (0.4ms)  SELECT  "teams".* FROM "teams" WHERE "teams"."product_id" = $1 LIMIT $2  [["product_id", 1], ["LIMIT", 1]]
  MotherItem Load (0.5ms)  SELECT "game_items".* FROM "game_items" WHERE "game_items"."team_id" = $1  [["team_id", 1]]
  PlayerProfile Load (1.3ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 1], ["profileable_type", "MotherItem"], ["LIMIT", 1]]
  SQL (0.5ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 1]]
  SQL (0.3ms)  DELETE FROM "game_items" WHERE "game_items"."id" = $1  [["id", 1]]
  PlayerProfile Load (0.5ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 2], ["profileable_type", "MotherItem"], ["LIMIT", 1]]
  SQL (0.6ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 2]]
  SQL (0.8ms)  DELETE FROM "game_items" WHERE "game_items"."id" = $1  [["id", 2]]
  PlayerProfile Load (0.6ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 3], ["profileable_type", "MotherItem"], ["LIMIT", 1]]
  SQL (0.5ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 3]]
  SQL (0.3ms)  DELETE FROM "game_items" WHERE "game_items"."id" = $1  [["id", 3]]
  PlayerItem Load (0.5ms)  SELECT "sister_items".* FROM "sister_items" WHERE "sister_items"."team_id" = $1  [["team_id", 1]]
  PlayerProfile Load (0.5ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 1], ["profileable_type", "PlayerItem"], ["LIMIT", 1]]
  SQL (0.4ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 4]]
  SQL (0.4ms)  DELETE FROM "sister_items" WHERE "sister_items"."id" = $1  [["id", 1]]
  PlayerProfile Load (0.6ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 2], ["profileable_type", "PlayerItem"], ["LIMIT", 1]]
  SQL (0.5ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 5]]
  SQL (0.7ms)  DELETE FROM "sister_items" WHERE "sister_items"."id" = $1  [["id", 2]]
  PlayerProfile Load (0.4ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 3], ["profileable_type", "PlayerItem"], ["LIMIT", 1]]
  SQL (0.3ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 6]]
  SQL (0.3ms)  DELETE FROM "sister_items" WHERE "sister_items"."id" = $1  [["id", 3]]
  PlayerProfile Load (0.4ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 4], ["profileable_type", "PlayerItem"], ["LIMIT", 1]]
  SQL (1.4ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 7]]
  SQL (0.5ms)  DELETE FROM "sister_items" WHERE "sister_items"."id" = $1  [["id", 4]]
  PlayerProfile Load (0.5ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 5], ["profileable_type", "PlayerItem"], ["LIMIT", 1]]
  SQL (0.5ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 8]]
  SQL (0.5ms)  DELETE FROM "sister_items" WHERE "sister_items"."id" = $1  [["id", 5]]
  PlayerProfile Load (0.4ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 6], ["profileable_type", "PlayerItem"], ["LIMIT", 1]]
  SQL (1.3ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 9]]
  SQL (1.9ms)  DELETE FROM "sister_items" WHERE "sister_items"."id" = $1  [["id", 6]]
  FootballItem Load (1.4ms)  SELECT "football_items".* FROM "football_items" WHERE "football_items"."team_id" = $1  [["team_id", 1]]
  PlayerProfile Load (1.0ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 1], ["profileable_type", "FootballItem"], ["LIMIT", 1]]
  SQL (0.6ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 10]]
  SQL (1.8ms)  DELETE FROM "football_items" WHERE "football_items"."id" = $1  [["id", 1]]
  PlayerProfile Load (0.3ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 2], ["profileable_type", "FootballItem"], ["LIMIT", 1]]
  SQL (1.0ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 11]]
  SQL (0.4ms)  DELETE FROM "football_items" WHERE "football_items"."id" = $1  [["id", 2]]
  PlayerProfile Load (0.4ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 3], ["profileable_type", "FootballItem"], ["LIMIT", 1]]
  SQL (0.6ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 12]]
  SQL (0.6ms)  DELETE FROM "football_items" WHERE "football_items"."id" = $1  [["id", 3]]
  PlayerProfile Load (0.9ms)  SELECT  "player_profiles".* FROM "player_profiles" WHERE "player_profiles"."profileable_id" = $1 AND "player_profiles"."profileable_type" = $2 LIMIT $3  [["profileable_id", 4], ["profileable_type", "FootballItem"], ["LIMIT", 1]]
  SQL (0.6ms)  DELETE FROM "player_profiles" WHERE "player_profiles"."id" = $1  [["id", 13]]
  SQL (0.3ms)  DELETE FROM "football_items" WHERE "football_items"."id" = $1  [["id", 4]]
  SQL (2.7ms)  DELETE FROM "teams" WHERE "teams"."id" = $1  [["id", 1]]
  SQL (2.6ms)  DELETE FROM "products" WHERE "products"."id" = $1  [["id", 1]]
   (3.3ms)  COMMIT
Completed 200 OK in 522ms (Views: 0.4ms | ActiveRecord: 80.5ms)

user: root
POST /api/v1
USE eager loading detected
  MotherItem => [:player_profile]
  Add to your finder: :includes => [:player_profile]
Call stack
  /app/graphql/delete.rb:15:in `call'
  /app/controllers/graphql_controller.rb:11:in `execute'

user: root
POST /api/v1
USE eager loading detected
  PlayerItem => [:player_profile]
  Add to your finder: :includes => [:player_profile]
Call stack
  /app/graphql/delete.rb:15:in `call'
  /app/controllers/graphql_controller.rb:11:in `execute'

user: root
POST /api/v1
USE eager loading detected
  FootballItem => [:player_profile]
  Add to your finder: :includes => [:player_profile]
Call stack
  /app/graphql/delete.rb:15:in `call'
  /app/controllers/graphql_controller.rb:11:in `execute'

As you can see gem Bullet (https://github.com/flyerhzm/bullet) detects N+1 problems...

Is there a way to handle this? Maybe with the same gem for batching and preloader? (https://github.com/Shopify/graphql-batch) - (https://github.com/ConsultingMD/graphql-preload) ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

0 participants