summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorick Peterse <yorickpeterse@gmail.com>2015-10-30 17:28:30 +0000
committerYorick Peterse <yorickpeterse@gmail.com>2015-10-30 17:28:30 +0000
commita0ba6c6c190c2d556166f78cde06b53df6d1b15e (patch)
tree0243525307126bab15c6e18633871fefd5fa87f9
parent49a73b6e70e740c0f9a336fb5f0ec0154f5a4168 (diff)
parent6d3068bec3a926d17f4f2d0da895856489bfcb7a (diff)
downloadgitlab-ce-a0ba6c6c190c2d556166f78cde06b53df6d1b15e.tar.gz
Merge branch 'optimize-user-find-by-any-email' into 'master'
Improve performance of User.find_by_any_email See merge request !1698
-rw-r--r--CHANGELOG1
-rw-r--r--app/models/user.rb25
-rw-r--r--spec/benchmarks/models/user_spec.rb26
3 files changed, 37 insertions, 15 deletions
diff --git a/CHANGELOG b/CHANGELOG
index cf44a5c7c82..6bec4f606e7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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)
- 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
- Show last project commit to default branch on project home page
- Highlight comment based on anchor in URL
diff --git a/app/models/user.rb b/app/models/user.rb
index c72beacbf0f..67fef1c1e6a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -235,21 +235,16 @@ class User < ActiveRecord::Base
# Find a User by their primary email or any associated secondary email
def find_by_any_email(email)
- user_table = arel_table
- email_table = Email.arel_table
-
- # Use ARel to build a query:
- query = user_table.
- # SELECT "users".* FROM "users"
- project(user_table[Arel.star]).
- # LEFT OUTER JOIN "emails"
- join(email_table, Arel::Nodes::OuterJoin).
- # ON "users"."id" = "emails"."user_id"
- 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
+ sql = 'SELECT *
+ FROM users
+ WHERE id IN (
+ SELECT id FROM users WHERE email = :email
+ UNION
+ SELECT emails.user_id FROM emails WHERE email = :email
+ )
+ LIMIT 1;'
+
+ User.find_by_sql([sql, { email: email }]).first
end
def filter(filter_name)
diff --git a/spec/benchmarks/models/user_spec.rb b/spec/benchmarks/models/user_spec.rb
index cc5c3904193..4cdba66939b 100644
--- a/spec/benchmarks/models/user_spec.rb
+++ b/spec/benchmarks/models/user_spec.rb
@@ -39,4 +39,30 @@ describe User, benchmark: true do
it { is_expected.to iterate_per_second(iterations) }
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