Skip to content

Improve snippets search SQL

This is a follow up to gitlab-foss!23952 (merged). We removed redundant counts there to improve the request time but the query itself might need improvements too.

  • Current query on staging (same in master):

    SQL
    SELECT COUNT(*)
    FROM
      (SELECT "snippets".*
      FROM "snippets"
      WHERE (snippets.visibility_level IN (10,
                                            20)
              OR snippets.author_id = 1675774)
        AND "snippets"."project_id" IS NULL
      UNION SELECT "snippets".*
      FROM "snippets"
      INNER JOIN "projects" ON "projects"."id" = "snippets"."project_id"
      INNER JOIN "project_features" ON "project_features"."project_id" = "projects"."id"
      WHERE (snippets.visibility_level IN (10,
                                            20)
              OR snippets.author_id = 1675774)
        AND (projects.visibility_level IN (10,
                                            20))
        AND "project_features"."snippets_access_level" IN (20,
                                                            30)
      UNION SELECT "snippets".*
      FROM "snippets"
      INNER JOIN "projects" ON "projects"."id" = "snippets"."project_id"
      INNER JOIN "project_features" ON "project_features"."project_id" = "projects"."id"
      WHERE "project_features"."snippets_access_level" IN (20,
                                                            30,
                                                            10)
        AND (EXISTS
                (SELECT 1
                FROM "project_authorizations"
                WHERE (project_id = snippets.project_id)
                  AND "project_authorizations"."user_id" = 1675774))) snippets
    WHERE "snippets"."content" ILIKE '%foo%'
    EXPLAIN
    Aggregate  (cost=13494.71..13494.72 rows=1 width=8) (actual time=9136.346..9136.346 rows=1 loops=1)
      Buffers: shared hit=34431 read=20888
      I/O Timings: read=962.676
      ->  HashAggregate  (cost=13492.51..13493.49 rows=98 width=1744) (actual time=9135.890..9136.298 rows=494 loops=1)
            Group Key: snippets.id, snippets.title, snippets.content, snippets.author_id, snippets.project_id, snippets.created_at, snippets.updated_at, snippets.
    file_name, snippets.type, snippets.visibility_level, snippets.title_html, snippets.content_html, snippets.cached_markdown_version, snippets.description, snippe
    ts.description_html
            Buffers: shared hit=34431 read=20888
            I/O Timings: read=962.676
            ->  Append  (cost=605.78..13488.83 rows=98 width=1744) (actual time=30.786..8364.451 rows=509 loops=1)
                  Buffers: shared hit=25958 read=16632
                  I/O Timings: read=856.209
                  ->  Bitmap Heap Scan on snippets  (cost=605.78..6538.49 rows=92 width=1134) (actual time=30.784..4049.599 rows=451 loops=1)
                        Recheck Cond: (((visibility_level = ANY ('{10,20}'::integer[])) OR (author_id = 1675774)) AND (project_id IS NULL))
                        Filter: (content ~~* '%foo%'::text)
                        Rows Removed by Filter: 11029
                        Heap Blocks: exact=5012
                        Buffers: shared hit=3894 read=14249
                        I/O Timings: read=615.799
                        ->  BitmapAnd  (cost=605.78..605.78 rows=10235 width=0) (actual time=19.574..19.575 rows=0 loops=1)
                              Buffers: shared hit=51 read=107
                              I/O Timings: read=12.004
                              ->  BitmapOr  (cost=162.75..162.75 rows=13978 width=0) (actual time=2.236..2.236 rows=0 loops=1)
                                    Buffers: shared hit=49
                                    ->  Bitmap Index Scan on index_snippets_on_visibility_level  (cost=0.00..160.90 rows=13976 width=0) (actual time=2.220..2.220
    rows=14132 loops=1)
                                          Index Cond: (visibility_level = ANY ('{10,20}'::integer[]))
                                          Buffers: shared hit=47
                                    ->  Bitmap Index Scan on index_snippets_on_author_id  (cost=0.00..1.80 rows=2 width=0) (actual time=0.015..0.015 rows=0 loops=
    1)
                                          Index Cond: (author_id = 1675774)
                                          Buffers: shared hit=2
                              ->  Bitmap Index Scan on index_snippets_on_project_id  (cost=0.00..442.75 rows=37195 width=0) (actual time=16.478..16.478 rows=37500
    loops=1)
                                    Index Cond: (project_id IS NULL)
                                    Buffers: shared hit=2 read=107
                                    I/O Timings: read=12.004
                  ->  Nested Loop  (cost=163.62..6881.02 rows=5 width=1134) (actual time=464.825..4168.857 rows=39 loops=1)
                        Buffers: shared hit=19866 read=2292
                        I/O Timings: read=210.612
                        ->  Nested Loop  (cost=163.19..6871.58 rows=15 width=1138) (actual time=461.839..4158.875 rows=41 loops=1)
                              Buffers: shared hit=19740 read=2254
                              I/O Timings: read=202.338
                              ->  Bitmap Heap Scan on snippets snippets_1  (cost=162.77..6624.38 rows=126 width=1134) (actual time=5.848..4150.055 rows=563 loops=
    1)
                                    Recheck Cond: ((visibility_level = ANY ('{10,20}'::integer[])) OR (author_id = 1675774))
                                    Filter: (content ~~* '%foo%'::text)
                                    Rows Removed by Filter: 13411
                                    Heap Blocks: exact=5436
                                    Buffers: shared hit=19239 read=2208
                                    I/O Timings: read=199.190
                                    ->  BitmapOr  (cost=162.77..162.77 rows=13978 width=0) (actual time=1.477..1.477 rows=0 loops=1)
                                          Buffers: shared hit=49
                                          ->  Bitmap Index Scan on index_snippets_on_visibility_level  (cost=0.00..160.90 rows=13976 width=0) (actual time=1.465..
    1.465 rows=14132 loops=1)
                                                Index Cond: (visibility_level = ANY ('{10,20}'::integer[]))
                                                Buffers: shared hit=47
                                          ->  Bitmap Index Scan on index_snippets_on_author_id  (cost=0.00..1.80 rows=2 width=0) (actual time=0.008..0.008 rows=0
    loops=1)
                                                Index Cond: (author_id = 1675774)
                                                Buffers: shared hit=2
                              ->  Index Only Scan using index_projects_on_id_partial_for_visibility on projects  (cost=0.42..1.95 rows=1 width=4) (actual time=0.0
    11..0.012 rows=0 loops=563)
                                    Index Cond: (id = snippets_1.project_id)
                                    Heap Fetches: 191
                                    Buffers: shared hit=501 read=46
                                    I/O Timings: read=3.148
                        ->  Index Scan using index_project_features_on_project_id on project_features  (cost=0.43..0.62 rows=1 width=4) (actual time=0.237..0.238
    rows=1 loops=41)
                              Index Cond: (project_id = projects.id)
                              Filter: (snippets_access_level = ANY ('{20,30}'::integer[]))
                              Rows Removed by Filter: 0
                              Buffers: shared hit=126 read=38
                              I/O Timings: read=8.274
    EXPLAIN for subquery 1
    gitlabhq_production=> explain (analyze, buffers) SELECT "snippets".*
    gitlabhq_production->     FROM "snippets"
    gitlabhq_production->     WHERE (snippets.visibility_level IN (10,
    gitlabhq_production(>                                           20)
    gitlabhq_production(>             OR snippets.author_id = 1675774)
    gitlabhq_production->       AND "snippets"."project_id" IS NULL;
    
    Bitmap Heap Scan on snippets  (cost=613.38..6520.51 rows=10235 width=1134) (actual time=6.284..21.256 rows=11480 loops=1)
      Recheck Cond: (((visibility_level = ANY ('{10,20}'::integer[])) OR (author_id = 1675774)) AND (project_id IS NULL))
      Heap Blocks: exact=5012
      Buffers: shared hit=5170
      ->  BitmapAnd  (cost=613.38..613.38 rows=10235 width=0) (actual time=5.545..5.545 rows=0 loops=1)
            Buffers: shared hit=158
            ->  BitmapOr  (cost=167.82..167.82 rows=13978 width=0) (actual time=1.746..1.746 rows=0 loops=1)
                  Buffers: shared hit=49
                  ->  Bitmap Index Scan on index_snippets_on_visibility_level  (cost=0.00..160.90 rows=13976 width=0) (actual time=1.733..1.733 rows=14132 loops=1
    )
                        Index Cond: (visibility_level = ANY ('{10,20}'::integer[]))
                        Buffers: shared hit=47
                  ->  Bitmap Index Scan on index_snippets_on_author_id  (cost=0.00..1.80 rows=2 width=0) (actual time=0.010..0.010 rows=0 loops=1)
                        Index Cond: (author_id = 1675774)
                        Buffers: shared hit=2
            ->  Bitmap Index Scan on index_snippets_on_project_id  (cost=0.00..442.75 rows=37195 width=0) (actual time=2.992..2.992 rows=37500 loops=1)
                  Index Cond: (project_id IS NULL)
                  Buffers: shared hit=109
    Planning time: 0.239 ms
    Execution time: 21.987 ms
    EXPLAIN for subquery 2
    gitlabhq_production=> explain (analyze, buffers) SELECT "snippets".*
    gitlabhq_production->     FROM "snippets"
    gitlabhq_production->     INNER JOIN "projects" ON "projects"."id" = "snippets"."project_id"
    gitlabhq_production->     INNER JOIN "project_features" ON "project_features"."project_id" = "projects"."id"
    gitlabhq_production->     WHERE (snippets.visibility_level IN (10,
    gitlabhq_production(>                                           20)
    gitlabhq_production(>             OR snippets.author_id = 1675774)
    gitlabhq_production->       AND (projects.visibility_level IN (10,
    gitlabhq_production(>                                           20))
    gitlabhq_production->       AND "project_features"."snippets_access_level" IN (20,
    gitlabhq_production(>                                                           30);
    
    Nested Loop  (cost=170.55..17809.22 rows=605 width=1134) (actual time=3.175..187.270 rows=810 loops=1)
      Buffers: shared hit=17790 read=1240
      I/O Timings: read=136.371
      ->  Nested Loop  (cost=170.12..16768.46 rows=1654 width=1138) (actual time=3.070..64.243 rows=860 loops=1)
            Buffers: shared hit=14998 read=589
            I/O Timings: read=22.263
            ->  Bitmap Heap Scan on snippets  (cost=169.69..6596.36 rows=13978 width=1134) (actual time=2.330..22.674 rows=13974 loops=1)
                  Recheck Cond: ((visibility_level = ANY ('{10,20}'::integer[])) OR (author_id = 1675774))
                  Heap Blocks: exact=5436
                  Buffers: shared hit=5485
                  ->  BitmapOr  (cost=169.69..169.69 rows=13978 width=0) (actual time=1.526..1.526 rows=0 loops=1)
                        Buffers: shared hit=49
                        ->  Bitmap Index Scan on index_snippets_on_visibility_level  (cost=0.00..160.90 rows=13976 width=0) (actual time=1.518..1.518 rows=14132 l
    oops=1)
                              Index Cond: (visibility_level = ANY ('{10,20}'::integer[]))
                              Buffers: shared hit=47
                        ->  Bitmap Index Scan on index_snippets_on_author_id  (cost=0.00..1.80 rows=2 width=0) (actual time=0.007..0.007 rows=0 loops=1)
                              Index Cond: (author_id = 1675774)
                              Buffers: shared hit=2
            ->  Index Only Scan using index_projects_on_id_partial_for_visibility on projects  (cost=0.42..0.72 rows=1 width=4) (actual time=0.003..0.003 rows=0 l
    oops=13974)
                  Index Cond: (id = snippets.project_id)
                  Heap Fetches: 2095
                  Buffers: shared hit=9513 read=589
                  I/O Timings: read=22.263
      ->  Index Scan using index_project_features_on_project_id on project_features  (cost=0.43..0.62 rows=1 width=4) (actual time=0.142..0.142 rows=1 loops=860)
            Index Cond: (project_id = projects.id)
            Filter: (snippets_access_level = ANY ('{20,30}'::integer[]))
            Rows Removed by Filter: 0
            Buffers: shared hit=2792 read=651
            I/O Timings: read=114.108
    Planning time: 1.521 ms
    Execution time: 187.459 ms
    EXPLAIN for subquery 3
    gitlabhq_production=> explain (analyze, buffers) SELECT "snippets".*
    gitlabhq_production->     FROM "snippets"
    gitlabhq_production->     INNER JOIN "projects" ON "projects"."id" = "snippets"."project_id"
    gitlabhq_production->     INNER JOIN "project_features" ON "project_features"."project_id" = "projects"."id"
    gitlabhq_production->     WHERE "project_features"."snippets_access_level" IN (20,
    gitlabhq_production(>                                                           30,
    gitlabhq_production(>                                                           10)
    gitlabhq_production->       AND (EXISTS
    gitlabhq_production(>               (SELECT 1
    gitlabhq_production(>               FROM "project_authorizations"
    gitlabhq_production(>               WHERE (project_id = snippets.project_id)
    gitlabhq_production(>                 AND "project_authorizations"."user_id" = 1675774));
    
    Nested Loop  (cost=5.12..68.48 rows=1 width=1134) (actual time=0.561..9.657 rows=255 loops=1)
      Buffers: shared hit=2827 read=5
      I/O Timings: read=4.310
      ->  Nested Loop  (cost=4.69..67.99 rows=1 width=1142) (actual time=0.540..4.641 rows=255 loops=1)
            Buffers: shared hit=1812
            ->  Nested Loop  (cost=4.40..58.59 rows=27 width=8) (actual time=0.158..3.224 rows=235 loops=1)
                  Buffers: shared hit=1100
                  ->  HashAggregate  (cost=3.97..4.24 rows=27 width=4) (actual time=0.132..0.210 rows=235 loops=1)
                        Group Key: project_authorizations.project_id
                        Buffers: shared hit=8
                        ->  Index Only Scan using index_project_authorizations_on_user_id_project_id_access_level on project_authorizations  (cost=0.43..3.90 rows
    =27 width=4) (actual time=0.035..0.073 rows=235 loops=1)
                              Index Cond: (user_id = 1675774)
                              Heap Fetches: 2
                              Buffers: shared hit=8
                  ->  Index Only Scan using idx_projects_on_repository_storage_last_repository_updated_at on projects  (cost=0.43..2.00 rows=1 width=4) (actual ti
    me=0.012..0.012 rows=1 loops=235)
                        Index Cond: (id = project_authorizations.project_id)
                        Heap Fetches: 184
                        Buffers: shared hit=1092
            ->  Index Scan using index_snippets_on_project_id on snippets  (cost=0.29..0.33 rows=2 width=1134) (actual time=0.003..0.005 rows=1 loops=235)
                  Index Cond: (project_id = projects.id)
                  Buffers: shared hit=712
      ->  Index Scan using index_project_features_on_project_id on project_features  (cost=0.43..0.47 rows=1 width=4) (actual time=0.019..0.019 rows=1 loops=255)
            Index Cond: (project_id = projects.id)
            Filter: (snippets_access_level = ANY ('{20,30,10}'::integer[]))
            Buffers: shared hit=1015 read=5
            I/O Timings: read=4.310
    Planning time: 11.190 ms
    Execution time: 9.803 ms

  • Suggested query from the comment of @redbaron1 https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23952#note_128840089:

    just curious does it get better if we merge first 2 selects from snippets? Idea is to get rid of duplicated heap scan in non-indexed case, which does more or less same job twice

    SQL
    SELECT COUNT(*)
    FROM
    (SELECT "snippets".*
      FROM "snippets"
      LEFT OUTER JOIN (
          "projects"
          INNER JOIN "project_features" ON "project_features"."project_id" = "projects"."id"
                                        AND "projects"."visibility_level" IN (10, 20)
                                        AND "project_features"."snippets_access_level" IN (20, 30)
      ) ON "projects"."id" = "snippets"."project_id"
      WHERE (snippets.visibility_level IN (10,
                                          20)
            OR snippets.author_id = 443319)
            AND ("snippets"."project_id" IS NULL OR "projects"."id" IS NOT NULL)
      UNION SELECT "snippets".*
      FROM "snippets"
      INNER JOIN "projects" ON "projects"."id" = "snippets"."project_id"
      INNER JOIN "project_features" ON "project_features"."project_id" = "projects"."id"
      WHERE "project_features"."snippets_access_level" IN (20,
                                                          30,
                                                          10)
        AND (EXISTS
              (SELECT 1
                FROM "project_authorizations"
                WHERE (project_id = snippets.project_id)
                  AND "project_authorizations"."user_id" = 443319))) snippets
    WHERE ("snippets"."content" ILIKE '%foo%')
    EXPLAIN (from staging)
    Aggregate  (cost=8898.23..8898.24 rows=1 width=8) (actual time=5211.605..5211.606 rows=1 loops=1)
      Buffers: shared hit=38571 read=2970
      I/O Timings: read=528.957
      ->  HashAggregate  (cost=8895.37..8896.64 rows=127 width=1744) (actual time=5211.230..5211.565 rows=494 loops=1)
            Group Key: snippets.id, snippets.title, snippets.content, snippets.author_id, snippets.project_id, snippets.created_at, snippets.updated_at, snippets.
    file_name, snippets.type, snippets.visibility_level, snippets.title_html, snippets.content_html, snippets.cached_markdown_version, snippets.description, snippe
    ts.description_html
            Buffers: shared hit=38571 read=2970
            I/O Timings: read=528.957
            ->  Append  (cost=163.82..8890.61 rows=127 width=1744) (actual time=24.537..4530.016 rows=510 loops=1)
                  Buffers: shared hit=25932 read=2880
                  I/O Timings: read=517.138
                  ->  Nested Loop Left Join  (cost=163.82..6947.22 rows=126 width=1134) (actual time=24.537..4332.577 rows=491 loops=1)
                        Filter: ((snippets.project_id IS NULL) OR (projects.id IS NOT NULL))
                        Rows Removed by Filter: 73
                        Buffers: shared hit=19437 read=2761
                        I/O Timings: read=452.625
                        ->  Bitmap Heap Scan on snippets  (cost=162.96..6625.03 rows=126 width=1134) (actual time=24.520..4313.814 rows=564 loops=1)
                              Recheck Cond: ((visibility_level = ANY ('{10,20}'::integer[])) OR (author_id = 443319))
                              Filter: (content ~~* '%foo%'::text)
                              Rows Removed by Filter: 13421
                              Heap Blocks: exact=5441
                              Buffers: shared hit=18747 read=2750
                              I/O Timings: read=443.573
                              ->  BitmapOr  (cost=162.96..162.96 rows=14004 width=0) (actual time=6.450..6.450 rows=0 loops=1)
                                    Buffers: shared hit=48 read=2
                                    I/O Timings: read=3.773
                                    ->  Bitmap Index Scan on index_snippets_on_visibility_level  (cost=0.00..160.90 rows=13976 width=0) (actual time=2.516..2.516
    rows=14125 loops=1)
                                          Index Cond: (visibility_level = ANY ('{10,20}'::integer[]))
                                          Buffers: shared hit=47
                                    ->  Bitmap Index Scan on index_snippets_on_author_id  (cost=0.00..2.00 rows=28 width=0) (actual time=3.931..3.931 rows=28 loop
    s=1)
                                          Index Cond: (author_id = 443319)
                                          Buffers: shared hit=1 read=2
                                          I/O Timings: read=3.773
                        ->  Nested Loop  (cost=0.85..2.55 rows=1 width=4) (actual time=0.029..0.029 rows=0 loops=564)
                              Buffers: shared hit=690 read=11
                              I/O Timings: read=9.052
                              ->  Index Only Scan using index_projects_on_id_partial_for_visibility on projects  (cost=0.42..1.92 rows=1 width=4) (actual time=0.0
    20..0.020 rows=0 loops=564)
                                    Index Cond: (id = snippets.project_id)
                                    Heap Fetches: 174
                                    Buffers: shared hit=525 read=8
                                    I/O Timings: read=7.047
                              ->  Index Scan using index_project_features_on_project_id on project_features  (cost=0.43..0.62 rows=1 width=4) (actual time=0.090..
    0.091 rows=1 loops=42)
                                    Index Cond: (project_id = projects.id)
                                    Filter: (snippets_access_level = ANY ('{20,30}'::integer[]))
                                    Rows Removed by Filter: 0
                                    Buffers: shared hit=165 read=3
                                    I/O Timings: read=2.005
                  ->  Nested Loop  (cost=59.73..1942.12 rows=1 width=1134) (actual time=70.415..197.145 rows=19 loops=1)
                        Buffers: shared hit=6495 read=119
                        I/O Timings: read=64.513
                        ->  Nested Loop  (cost=59.30..1941.64 rows=1 width=1142) (actual time=70.329..196.806 rows=19 loops=1)
                              Buffers: shared hit=6419 read=119
                              I/O Timings: read=64.513
                              ->  Nested Loop  (cost=59.01..1658.23 rows=826 width=8) (actual time=2.689..41.227 rows=954 loops=1)
                                    Buffers: shared hit=3962 read=19
                                    I/O Timings: read=26.518
                                    ->  HashAggregate  (cost=58.58..66.84 rows=826 width=4) (actual time=1.068..1.583 rows=954 loops=1)
                                          Group Key: project_authorizations.project_id
                                          Buffers: shared hit=135
                                          ->  Index Only Scan using index_project_authorizations_on_user_id_project_id_access_level on project_authorizations  (co
    st=0.43..56.51 rows=826 width=4) (actual time=0.104..0.852 rows=954 loops=1)
                                                Index Cond: (user_id = 443319)
                                                Heap Fetches: 36
                                                Buffers: shared hit=135
                                    ->  Index Only Scan using projects_pkey on projects projects_1  (cost=0.43..1.92 rows=1 width=4) (actual time=0.041..0.041 row
    s=1 loops=954)
                                          Index Cond: (id = project_authorizations.project_id)
                                          Heap Fetches: 227
                                          Buffers: shared hit=3827 read=19
                                          I/O Timings: read=26.518
                              ->  Index Scan using index_snippets_on_project_id on snippets snippets_1  (cost=0.29..0.33 rows=1 width=1134) (actual time=0.057..0.
    163 rows=0 loops=954)
                                    Index Cond: (project_id = projects_1.id)
                                    Filter: (content ~~* '%foo%'::text)
                                    Rows Removed by Filter: 0
                                    Buffers: shared hit=2457 read=100
                                    I/O Timings: read=37.995
                        ->  Index Scan using index_project_features_on_project_id on project_features project_features_1  (cost=0.43..0.47 rows=1 width=4) (actual
    time=0.013..0.014 rows=1 loops=19)
                              Index Cond: (project_id = projects_1.id)
                              Filter: (snippets_access_level = ANY ('{20,30,10}'::integer[]))
                              Buffers: shared hit=76
    Planning time: 41.530 ms
    Execution time: 5212.074 ms
    EXPLAIN for subquery 1
    gitlabhq_production=> explain (analyze, buffers) SELECT "snippets".*
    gitlabhq_production->     FROM "snippets"
    gitlabhq_production->     LEFT OUTER JOIN (
    gitlabhq_production(>         "projects"
    gitlabhq_production(>         INNER JOIN "project_features" ON "project_features"."project_id" = "projects"."id"
    gitlabhq_production(>                                       AND "projects"."visibility_level" IN (10, 20)
    gitlabhq_production(>                                       AND "project_features"."snippets_access_level" IN (20, 30)
    gitlabhq_production(>     ) ON "projects"."id" = "snippets"."project_id"
    gitlabhq_production->     WHERE (snippets.visibility_level IN (10,
    gitlabhq_production(>                                         20)
    gitlabhq_production(>           OR snippets.author_id = 443319)
    gitlabhq_production->           AND ("snippets"."project_id" IS NULL OR "projects"."id" IS NOT NULL);
    
    Nested Loop Left Join  (cost=170.75..25586.51 rows=13997 width=1134) (actual time=4.929..51.256 rows=12301 loops=1)
      Filter: ((snippets.project_id IS NULL) OR (projects.id IS NOT NULL))
      Rows Removed by Filter: 1684
      Buffers: shared hit=19236 read=2
      I/O Timings: read=2.040
      ->  Bitmap Heap Scan on snippets  (cost=169.90..6596.96 rows=13997 width=1134) (actual time=4.881..22.648 rows=13985 loops=1)
            Recheck Cond: ((visibility_level = ANY ('{10,20}'::integer[])) OR (author_id = 443319))
            Heap Blocks: exact=5441
            Buffers: shared hit=5489 read=2
            I/O Timings: read=2.040
            ->  BitmapOr  (cost=169.90..169.90 rows=14004 width=0) (actual time=3.661..3.662 rows=0 loops=1)
                  Buffers: shared hit=48 read=2
                  I/O Timings: read=2.040
                  ->  Bitmap Index Scan on index_snippets_on_visibility_level  (cost=0.00..160.90 rows=13976 width=0) (actual time=1.522..1.522 rows=14132 loops=1
    )
                        Index Cond: (visibility_level = ANY ('{10,20}'::integer[]))
                        Buffers: shared hit=47
                  ->  Bitmap Index Scan on index_snippets_on_author_id  (cost=0.00..2.00 rows=28 width=0) (actual time=2.137..2.138 rows=28 loops=1)
                        Index Cond: (author_id = 443319)
                        Buffers: shared hit=1 read=2
                        I/O Timings: read=2.040
      ->  Nested Loop  (cost=0.85..1.35 rows=1 width=4) (actual time=0.001..0.001 rows=0 loops=13985)
            Buffers: shared hit=13747
            ->  Index Only Scan using index_projects_on_id_partial_for_visibility on projects  (cost=0.42..0.72 rows=1 width=4) (actual time=0.001..0.001 rows=0 l
    oops=13985)
                  Index Cond: (id = snippets.project_id)
                  Heap Fetches: 2225
                  Buffers: shared hit=10264
            ->  Index Scan using index_project_features_on_project_id on project_features  (cost=0.43..0.62 rows=1 width=4) (actual time=0.006..0.006 rows=1 loops
    =870)
                  Index Cond: (project_id = projects.id)
                  Filter: (snippets_access_level = ANY ('{20,30}'::integer[]))
                  Rows Removed by Filter: 0
                  Buffers: shared hit=3483
    Planning time: 1.264 ms
    Execution time: 52.029 ms
    EXPLAIN for subquery 2
    gitlabhq_production=> explain (analyze, buffers) SELECT "snippets".*
    gitlabhq_production->     FROM "snippets"
    gitlabhq_production->     INNER JOIN "projects" ON "projects"."id" = "snippets"."project_id"
    gitlabhq_production->     INNER JOIN "project_features" ON "project_features"."project_id" = "projects"."id"
    gitlabhq_production->     WHERE "project_features"."snippets_access_level" IN (20,
    gitlabhq_production(>                                                         30,
    gitlabhq_production(>                                                         10)
    gitlabhq_production->       AND (EXISTS
    gitlabhq_production(>             (SELECT 1
    gitlabhq_production(>               FROM "project_authorizations"
    gitlabhq_production(>               WHERE (project_id = snippets.project_id)
    gitlabhq_production(>                 AND "project_authorizations"."user_id" = 443319));
    
    Nested Loop  (cost=59.73..1985.51 rows=5 width=1134) (actual time=6.588..43.190 rows=260 loops=1)
      Buffers: shared hit=6545 read=643
      I/O Timings: read=25.879
      ->  Nested Loop  (cost=59.30..1978.77 rows=14 width=1142) (actual time=6.539..40.283 rows=260 loops=1)
            Buffers: shared hit=5507 read=641
            I/O Timings: read=23.839
            ->  Nested Loop  (cost=59.01..1691.23 rows=826 width=8) (actual time=2.162..36.626 rows=954 loops=1)
                  Buffers: shared hit=3354 read=638
                  I/O Timings: read=23.683
                  ->  HashAggregate  (cost=58.58..66.84 rows=826 width=4) (actual time=1.962..2.508 rows=954 loops=1)
                        Group Key: project_authorizations.project_id
                        Buffers: shared hit=119 read=16
                        I/O Timings: read=0.842
                        ->  Index Only Scan using index_project_authorizations_on_user_id_project_id_access_level on project_authorizations  (cost=0.43..56.51 row
    s=826 width=4) (actual time=0.172..1.753 rows=954 loops=1)
                              Index Cond: (user_id = 443319)
                              Heap Fetches: 36
                              Buffers: shared hit=119 read=16
                              I/O Timings: read=0.842
                  ->  Index Only Scan using projects_pkey on projects  (cost=0.43..1.96 rows=1 width=4) (actual time=0.035..0.035 rows=1 loops=954)
                        Index Cond: (id = project_authorizations.project_id)
                        Heap Fetches: 239
                        Buffers: shared hit=3235 read=622
                        I/O Timings: read=22.841
            ->  Index Scan using index_snippets_on_project_id on snippets  (cost=0.29..0.33 rows=2 width=1134) (actual time=0.003..0.003 rows=0 loops=954)
                  Index Cond: (project_id = projects.id)
                  Buffers: shared hit=2153 read=3
                  I/O Timings: read=0.156
      ->  Index Scan using index_project_features_on_project_id on project_features  (cost=0.43..0.47 rows=1 width=4) (actual time=0.011..0.011 rows=1 loops=260)
            Index Cond: (project_id = projects.id)
            Filter: (snippets_access_level = ANY ('{20,30,10}'::integer[]))
            Buffers: shared hit=1038 read=2
            I/O Timings: read=2.040
    Planning time: 17.046 ms
    Execution time: 43.345 ms
Edited by Markus Koller