gitlab-org--gitlab-foss/lib/tasks
Yorick Peterse 41bfe82b7a
Optimise searching for users using short queries
This optimises searching for users when using queries consisting out of
one or two characters such as "ab". We optimise such cases by searching
for `LOWER(name)` and `LOWER(username)` instead of using `ILIKE`. Using
`LOWER` produces a _much_ better performing query.

For example, when searching for all users matching the term "a" we'd
produce the following plan:

     Limit  (cost=637.69..637.74 rows=20 width=805) (actual time=41.983..41.995 rows=20 loops=1)
       Buffers: shared hit=8330
       ->  Sort  (cost=637.69..638.61 rows=368 width=805) (actual time=41.982..41.990 rows=20 loops=1)
             Sort Key: (CASE WHEN ((name)::text = 'a'::text) THEN 0 WHEN ((username)::text = 'a'::text) THEN 1 WHEN ((email)::text = 'a'::text) THEN 2 ELSE 3 END), name
             Sort Method: top-N heapsort  Memory: 35kB
             Buffers: shared hit=8330
             ->  Bitmap Heap Scan on users  (cost=75.47..627.89 rows=368 width=805) (actual time=9.452..41.305 rows=277 loops=1)
                   Recheck Cond: (((name)::text ~~* 'a'::text) OR ((username)::text ~~* 'a'::text) OR ((email)::text = 'a'::text))
                   Rows Removed by Index Recheck: 7601
                   Heap Blocks: exact=7636
                   Buffers: shared hit=8327
                   ->  BitmapOr  (cost=75.47..75.47 rows=368 width=0) (actual time=8.290..8.290 rows=0 loops=1)
                         Buffers: shared hit=691
                         ->  Bitmap Index Scan on index_users_on_name_trigram  (cost=0.00..38.85 rows=180 width=0) (actual time=4.369..4.369 rows=4071 loops=1)
                               Index Cond: ((name)::text ~~* 'a'::text)
                               Buffers: shared hit=360
                         ->  Bitmap Index Scan on index_users_on_username_trigram  (cost=0.00..34.41 rows=188 width=0) (actual time=3.896..3.896 rows=4140 loops=1)
                               Index Cond: ((username)::text ~~* 'a'::text)
                               Buffers: shared hit=328
                         ->  Bitmap Index Scan on users_email_key  (cost=0.00..1.94 rows=1 width=0) (actual time=0.022..0.022 rows=0 loops=1)
                               Index Cond: ((email)::text = 'a'::text)
                               Buffers: shared hit=3
     Planning time: 3.912 ms
     Execution time: 42.171 ms

With the changes in this commit we now produce the following plan
instead:

     Limit  (cost=13257.48..13257.53 rows=20 width=805) (actual time=1.567..1.579 rows=20 loops=1)
       Buffers: shared hit=287
       ->  Sort  (cost=13257.48..13280.93 rows=9379 width=805) (actual time=1.567..1.572 rows=20 loops=1)
             Sort Key: (CASE WHEN ((name)::text = 'a'::text) THEN 0 WHEN ((username)::text = 'a'::text) THEN 1 WHEN ((email)::text = 'a'::text) THEN 2 ELSE 3 END), name
             Sort Method: top-N heapsort  Memory: 35kB
             Buffers: shared hit=287
             ->  Bitmap Heap Scan on users  (cost=135.66..13007.91 rows=9379 width=805) (actual time=0.194..1.107 rows=277 loops=1)
                   Recheck Cond: ((lower((name)::text) = 'a'::text) OR (lower((username)::text) = 'a'::text) OR ((email)::text = 'a'::text))
                   Heap Blocks: exact=277
                   Buffers: shared hit=287
                   ->  BitmapOr  (cost=135.66..135.66 rows=9379 width=0) (actual time=0.152..0.152 rows=0 loops=1)
                         Buffers: shared hit=10
                         ->  Bitmap Index Scan on yorick_test_users  (cost=0.00..124.75 rows=9377 width=0) (actual time=0.101..0.101 rows=277 loops=1)
                               Index Cond: (lower((name)::text) = 'a'::text)
                               Buffers: shared hit=4
                         ->  Bitmap Index Scan on index_on_users_lower_username  (cost=0.00..1.94 rows=1 width=0) (actual time=0.035..0.035 rows=1 loops=1)
                               Index Cond: (lower((username)::text) = 'a'::text)
                               Buffers: shared hit=3
                         ->  Bitmap Index Scan on users_email_key  (cost=0.00..1.94 rows=1 width=0) (actual time=0.014..0.014 rows=0 loops=1)
                               Index Cond: ((email)::text = 'a'::text)
                               Buffers: shared hit=3
     Planning time: 0.303 ms
     Execution time: 1.687 ms

Here we can see the new query is 25 times faster compared to the old
query.
2018-02-22 18:55:36 +01:00
..
ci
gitlab 36847 - update toml-rb to 1.0.0 2018-02-21 22:09:15 +10:30
migrate Optimise searching for users using short queries 2018-02-22 18:55:36 +01:00
brakeman.rake Speed up Unicorn specs by using a dummy Rack application instead of GitLab 2017-11-22 19:51:57 +00:00
cache.rake Support multiple Redis instances based on queue type 2017-07-11 03:35:47 +00:00
config_lint.rake CI runs lint on shell scripts in lib/support 2017-02-07 22:19:16 +00:00
dev.rake Use `Gitlab::Utils::Override` over defined?(super) 2017-12-26 17:18:10 +08:00
downtime_check.rake Don’t exclude some file in lib from rubocop 2017-02-23 09:32:42 -06:00
ee_compat_check.rake
eslint.rake simplify eslint rake task 2017-02-15 23:46:29 -06:00
flay.rake Fix flay not detecting identical code 2018-01-31 14:07:47 +01:00
gemojione.rake Rename .scss files to use snake_case 2018-02-08 23:07:34 -06:00
gettext.rake Use StrongMemoize and enable/disable cops properly 2017-11-18 01:01:53 +08:00
grape.rake added missed commit in rebase 2017-02-07 11:00:47 +01:00
haml-lint.rake Convert parser warnings to stdout in haml_lint 2018-01-26 19:42:48 +08:00
import.rake Restore GH enterprise support in the Rake task 2017-11-08 21:20:46 +01:00
karma.rake Reset container width when switching to pipelines MR tab 2017-03-28 10:59:54 -05:00
lint.rake Run lint:all tasks in forks 2018-02-14 17:34:34 +01:00
rubocop.rake
scss-lint.rake
services.rake Don’t exclude some file in lib from rubocop 2017-02-23 09:32:42 -06:00
setup.rake
sidekiq.rake Don’t exclude some file in lib from rubocop 2017-02-23 09:32:42 -06:00
spec.rake Enable the Style/TrailingCommaInLiteral cop 2017-05-10 18:25:45 +02:00
spinach.rake Don’t exclude some file in lib from rubocop 2017-02-23 09:32:42 -06:00
test.rake remove remaining vestiges of teaspoon test runner 2017-01-10 12:30:41 -06:00
tokens.rake Remove tokens:reset_all_auth rake task 2017-11-02 11:39:02 +01:00
yarn.rake do not use --force command argument for yarn 2017-02-16 01:04:08 -06:00