-
Notifications
You must be signed in to change notification settings - Fork 405
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
Significantly optimize #without_role #500
Conversation
since RolifyCommunity@522c0d7 Rails < 4 is no longer supported
Imagine a big production application with millions of users. The previous version generates 3 queries for `User.without_role(:admin)`: 1) For `user.all` in [`#all_except`](https://github.com/RolifyCommunity/rolify/blob/522c0d7d84a70dd3576b569edf19f9f6cb118a61/lib/rolify/adapters/active_record/role_adapter.rb#L83): ```sql SELECT "users".* FROM "users" ``` 2) For `self.with_role(role_name, resource)` in [`#without_role`](https://github.com/RolifyCommunity/rolify/blob/522c0d7d84a70dd3576b569edf19f9f6cb118a61/lib/rolify/finders.rb#L8): ```sql SELECT "users".* FROM "users" INNER JOIN "users_roles" ON "users_roles"."user_id" = "users"."id" INNER JOIN "roles" ON "roles"."id" = "users_roles"."role_id" WHERE (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) ``` 3) For `user.where(prime_key => (user.all - excluded_obj).map(&prime_key))` in [`#all_except`](https://github.com/RolifyCommunity/rolify/blob/522c0d7d84a70dd3576b569edf19f9f6cb118a61/lib/rolify/adapters/active_record/role_adapter.rb#L83): ```sql SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 2, ..., N) ``` where `N` is a number of users without the `admin` role. While a number of values in the `IN` clause for the third query can be huge there is even a worse problem. First, `user.all - excluded_obj` creates an `Array` of users and then `(user.all - excluded_obj).map(&prime_key)` calls a primary key method on every object in this array. From tests --- Before (3 queries): ```sql SELECT "customers".* FROM "customers" SELECT "customers".* FROM "customers" INNER JOIN "customers_privileges" ON "customers_privileges"."customer_id" = "customers"."id" INNER JOIN "privileges" ON "privileges"."id" = "customers_privileges"."privilege_id" WHERE (((privileges.name = 'admin') AND (privileges.resource_type IS NULL) AND (privileges.resource_id IS NULL))) SELECT "customers".* FROM "customers" WHERE "customers"."id" IN (2, 3, 4) ``` After (1 query with a subquery): ```sql SELECT "customers".* FROM "customers" WHERE ("customers"."id" NOT IN (SELECT "customers"."id" FROM "customers" INNER JOIN "customers_privileges" ON "customers_privileges"."customer_id" = "customers"."id" INNER JOIN "privileges" ON "privileges"."id" = "customers_privileges"."privilege_id" WHERE (((privileges.name = 'admin') AND (privileges.resource_type IS NULL) AND (privileges.resource_id IS NULL))))) ```
5 similar comments
@EppO some jobs in the Travis CI build mistakenly failed with The command "gem update --system" failed and exited with 1 during . Can you restart those? BTW any reason for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
No there is no reason and I agree it's confusing. Any PR regarding that matter would be greatly appreciated |
Thanks for your PR! |
Imagine a big production application with millions of users.
The previous version generates 3 queries for
User.without_role(:admin)
:user.all
in#all_except
:self.with_role(role_name, resource)
in#without_role
:user.where(prime_key => (user.all - excluded_obj).map(&prime_key))
in#all_except
:where
N
is a number of users without theadmin
role.While a number of values in the
IN
clause for the third query can be huge there is even a worse problem. First,user.all - excluded_obj
creates anArray
of users and then(user.all - excluded_obj).map(&prime_key)
calls a primary key method on every object in this array.From tests
Before (3 queries):
After (1 query with a subquery):