andrzej541
andrzej541

Reputation: 961

Rails: ActiveRecord perform pointless query multiple times over the same model

In my app I have overwritten current_user devise method a bit. The idea is that if certain cookie is present method check the organization by the id inside that cookie and returns owner of this organization instead of regular user:

  def current_user
    user = warden.authenticate(scope: :user)
    return nil if user.nil?

    if user.admin? && cookies.key?('mock_admin_login')
      organization = Organization.includes(:creator).find(cookies.encrypted[:mock_admin_login])
      return organization.creator
    end
    user
  end 

Everything works correct but when I take a look at my console I noticed that Organization query is performed multiple times:

CACHE Organization Load (0.5ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE Organization Load (0.9ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE Organization Load (0.7ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE Organization Load (0.3ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE Organization Load (0.4ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE Organization Load (2.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (4.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE Organization Load (0.4ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE Organization Load (42.8ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE Organization Load (4.5ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user' CACHE User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 10]] ↳ app/controllers/concerns/current_methods_overwritten.rb:11:in current_user'

Although It might seem like a not big deal but server spends additional 30-40ms to perform this action every time when current_user method is called. Why this query is called so many times instead of one and how can I fix it?

Upvotes: 0

Views: 694

Answers (1)

max
max

Reputation: 101811

You need to memoize the result so that its not reevaluated every time you call current_user.

If you look at the helper that devise generates you can see that it does just that:

def current_#{mapping}
  @current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end

If you want to fix your existing method you want to make sure to memoize the DB calls:

def current_user
  @current_user ||= warden.authenticate(scope: :#{mapping})
  if @current_user&.admin? && cookies.key?('mock_admin_login')
    @current_org || = Organization.includes(:creator)
                                    .find(cookies.encrypted[:mock_admin_login])
    @current_user = @current_org.creator
  end
  @current_user
end 

But you really should implement this as a custom Warden strategy instead.

Upvotes: 3

Related Questions