Commit a0ba6c6c authored by Yorick Peterse's avatar Yorick Peterse

Merge branch 'optimize-user-find-by-any-email' into 'master'

Improve performance of User.find_by_any_email



See merge request !1698
parents 49a73b6e 6d3068be
...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.2.0 (unreleased) v 8.2.0 (unreleased)
- Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
- Improved performance of finding users by one of their Email addresses
- Improved performance of replacing references in comments - Improved performance of replacing references in comments
- Show last project commit to default branch on project home page - Show last project commit to default branch on project home page
- Highlight comment based on anchor in URL - Highlight comment based on anchor in URL
......
...@@ -235,21 +235,16 @@ class User < ActiveRecord::Base ...@@ -235,21 +235,16 @@ class User < ActiveRecord::Base
# Find a User by their primary email or any associated secondary email # Find a User by their primary email or any associated secondary email
def find_by_any_email(email) def find_by_any_email(email)
user_table = arel_table sql = 'SELECT *
email_table = Email.arel_table FROM users
WHERE id IN (
# Use ARel to build a query: SELECT id FROM users WHERE email = :email
query = user_table. UNION
# SELECT "users".* FROM "users" SELECT emails.user_id FROM emails WHERE email = :email
project(user_table[Arel.star]). )
# LEFT OUTER JOIN "emails" LIMIT 1;'
join(email_table, Arel::Nodes::OuterJoin).
# ON "users"."id" = "emails"."user_id" User.find_by_sql([sql, { email: email }]).first
on(user_table[:id].eq(email_table[:user_id])).
# WHERE ("user"."email" = '<email>' OR "emails"."email" = '<email>')
where(user_table[:email].eq(email).or(email_table[:email].eq(email)))
find_by_sql(query.to_sql).first
end end
def filter(filter_name) def filter(filter_name)
......
...@@ -39,4 +39,30 @@ describe User, benchmark: true do ...@@ -39,4 +39,30 @@ describe User, benchmark: true do
it { is_expected.to iterate_per_second(iterations) } it { is_expected.to iterate_per_second(iterations) }
end end
end end
describe '.find_by_any_email' do
let(:user) { create(:user) }
describe 'using a user with only a single Email address' do
let(:email) { user.email }
benchmark_subject { User.find_by_any_email(email) }
it { is_expected.to iterate_per_second(1000) }
end
describe 'using a user with multiple Email addresses' do
let(:email) { user.emails.first.email }
benchmark_subject { User.find_by_any_email(email) }
before do
10.times do
user.emails.create(email: FFaker::Internet.email)
end
end
it { is_expected.to iterate_per_second(1000) }
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment