Skip to content

Geo: Improve performance of package files queries

What does this MR do?

Removes a CTE which was written to improve performance of cross-database queries using FDW. Since we rearchitected the backend to no longer perform those particular cross-database queries, we should remove this CTE to:

  • give PostgreSQL the opportunity to optimize the whole query
  • improve readability and maintainability

Resolves #222617 (closed)

Queries

Summary:

  • Selective sync by shard query was 43s => now 0.1s
  • Selective sync by namespace query was 6s => now 0.3s

Before - selective sync by shard

EXPLAIN WITH "restricted_packages" AS (
  SELECT 
    "packages_packages"."id" 
  FROM 
    "packages_packages" 
  WHERE 
    "packages_packages"."project_id" IN (
      SELECT 
        "projects"."id" 
      FROM 
        "projects" 
      WHERE 
        "projects"."repository_storage" = 'nfs-file22'
    )
) 
SELECT 
  "packages_package_files"."id" 
FROM 
  "restricted_packages" 
  INNER JOIN "packages_package_files" ON "restricted_packages"."id" = "packages_package_files"."package_id" 
WHERE 
  "packages_package_files"."id" BETWEEN 1 
  AND 1000
Plan with execution:
  Hash Join  (cost=283226.65..283861.10 rows=34 width=8) (actual time=43948.639..43949.529 rows=35 loops=1)
   Hash Cond: (packages_package_files.package_id = restricted_packages.id)
   Buffers: shared hit=553909 read=102654 dirtied=8767
   I/O Timings: read=41617.001
   CTE restricted_packages
     ->  Merge Join  (cost=6292.38..282935.15 rows=8956 width=8) (actual time=10871.256..43942.639 rows=6477 loops=1)
           Merge Cond: (packages_packages.project_id = projects.id)
           Buffers: shared hit=553051 read=102654 dirtied=8767
           I/O Timings: read=41617.001
           ->  Index Scan using index_packages_packages_on_project_id_and_package_type on public.packages_packages  (cost=0.42..19149.50 rows=397217 width=12) (actual time=3.609..3331.170 rows=398536 loops=1)
                 Buffers: shared hit=259716 read=2269 dirtied=13
                 I/O Timings: read=2912.833
           ->  Index Only Scan using idx_projects_on_repository_storage_last_repository_updated_at on public.projects  (cost=0.56..262051.04 rows=343964 width=4) (actual time=6776.332..40465.224 rows=345688 loops=1)
                 Index Cond: (projects.repository_storage = 'nfs-file22'::text)
                 Heap Fetches: 26480
                 Buffers: shared hit=293335 read=100385 dirtied=8754
                 I/O Timings: read=38704.168
   ->  Index Scan using packages_package_files_pkey on public.packages_package_files  (cost=0.43..99.11 rows=477 width=16) (actual time=0.187..0.910 rows=946 loops=1)
         Index Cond: ((packages_package_files.id >= 1) AND (packages_package_files.id <= 1000))
         Buffers: shared hit=855
   ->  Hash  (cost=179.12..179.12 rows=8956 width=8) (actual time=43948.204..43948.204 rows=6477 loops=1)
         Buckets: 16384  Batches: 1  Memory Usage: 382kB
         Buffers: shared hit=553051 read=102654 dirtied=8767
         I/O Timings: read=41617.001
         ->  CTE Scan on restricted_packages  (cost=0.00..179.12 rows=8956 width=8) (actual time=10871.260..43945.740 rows=6477 loops=1)
               Buffers: shared hit=553051 read=102654 dirtied=8767
               I/O Timings: read=41617.001

Recommendations:
:exclamation: Query processes too much data to return a relatively small number of rows. – Reduce data cardinality as early as possible during the execution, using one or several of the following techniques: new indexes, partitioning, query rewriting, denormalization. See the visualization of the plan to understand which plan nodes are the main bottlenecks. Show details
:exclamation: VACUUM ANALYZE needed – Visibility map(s) for the table(s) involved in the query are outdated. For better performance: 1) run VACUUM ANALYZE on them as a one-time solution, 2) perform autovacuum tuning as a long-term permanent solution (tuning "vacuum" part of autovacuum). Show details

Summary:

Time: 43.951 s
  - planning: 1.590 ms
  - execution: 43.950 s
    - I/O read: 41.617 s
    - I/O write: 0.000 ms

Shared buffers:
  - hits: 553909 (~4.20 GiB) from the buffer pool
  - reads: 102654 (~802.00 MiB) from the OS file cache, including disk I/O
  - dirtied: 8767 (~68.50 MiB)
  - writes: 0

After - selective sync by shard

EXPLAIN 
SELECT 
  "packages_package_files"."id" 
FROM 
  "packages_package_files" 
  INNER JOIN "packages_packages" ON "packages_packages"."id" = "packages_package_files"."package_id" 
WHERE 
  (
    packages_packages.project_id IN (
      SELECT 
        "projects"."id" 
      FROM 
        "projects" 
      WHERE 
        "projects"."repository_storage" = 'nfs-file22'
    )
  ) 
  AND "packages_package_files"."id" BETWEEN 1 
  AND 1000;
Plan with execution:
 Nested Loop  (cost=1.41..2073.58 rows=11 width=8) (actual time=31.265..146.130 rows=35 loops=1)
   Buffers: shared hit=8323 read=101
   I/O Timings: read=135.642
   ->  Nested Loop  (cost=0.85..1618.49 rows=477 width=12) (actual time=10.467..120.458 rows=946 loops=1)
         Buffers: shared hit=4552 read=87
         I/O Timings: read=114.595
         ->  Index Scan using packages_package_files_pkey on public.packages_package_files  (cost=0.43..99.11 rows=477 width=16) (actual time=4.006..98.236 rows=946 loops=1)
               Index Cond: ((packages_package_files.id >= 1) AND (packages_package_files.id <= 1000))
               Buffers: shared hit=780 read=75
               I/O Timings: read=96.320
         ->  Index Scan using packages_packages_pkey on public.packages_packages  (cost=0.42..3.19 rows=1 width=12) (actual time=0.022..0.022 rows=1 loops=946)
               Index Cond: (packages_packages.id = packages_package_files.package_id)
               Buffers: shared hit=3772 read=12
               I/O Timings: read=18.275
   ->  Index Only Scan using idx_projects_on_repository_storage_last_repository_updated_at on public.projects  (cost=0.56..0.95 rows=1 width=4) (actual time=0.027..0.027 rows=0 loops=946)
         Index Cond: ((projects.id = packages_packages.project_id) AND (projects.repository_storage = 'nfs-file22'::text))
         Heap Fetches: 0
         Buffers: shared hit=3771 read=14
         I/O Timings: read=21.047

Recommendations:
:exclamation: Query processes too much data to return a relatively small number of rows. – Reduce data cardinality as early as possible during the execution, using one or several of the following techniques: new indexes, partitioning, query rewriting, denormalization. See the visualization of the plan to understand which plan nodes are the main bottlenecks. Show details

Summary:

Time: 148.733 ms
  - planning: 2.480 ms
  - execution: 146.253 ms
    - I/O read: 135.642 ms
    - I/O write: 0.000 ms

Shared buffers:
  - hits: 8323 (~65.00 MiB) from the buffer pool
  - reads: 101 (~808.00 KiB) from the OS file cache, including disk I/O
  - dirtied: 0
  - writes: 0

Before - selective sync by namespace

EXPLAIN WITH "restricted_packages" AS (
  SELECT 
    "packages_packages"."id" 
  FROM 
    "packages_packages" 
  WHERE 
    "packages_packages"."project_id" IN (
      SELECT 
        "projects"."id" 
      FROM 
        "projects" 
      WHERE 
        "projects"."namespace_id" IN (
          WITH RECURSIVE "base_and_descendants" AS (
            (
              SELECT 
                9970 AS id
            ) 
            UNION 
              (
                SELECT 
                  "namespaces"."id" 
                FROM 
                  "namespaces", 
                  "base_and_descendants" 
                WHERE 
                  "namespaces"."parent_id" = "base_and_descendants"."id"
              )
          ) 
          SELECT 
            "id" 
          FROM 
            "base_and_descendants" AS "namespaces"
        )
    )
) 
SELECT 
  "packages_package_files"."id" 
FROM 
  "restricted_packages" 
  INNER JOIN "packages_package_files" ON "restricted_packages"."id" = "packages_package_files"."package_id" 
WHERE 
  "packages_package_files"."id" BETWEEN 1 
  AND 1000
Plan with execution:
 Hash Join  (cost=391404.09..415203.39 rows=1518 width=8) (actual time=6057.775..6059.786 rows=32 loops=1)
   Hash Cond: (packages_package_files.package_id = restricted_packages.id)
   Buffers: shared hit=260470 read=5080 dirtied=312
   I/O Timings: read=5071.090
   CTE restricted_packages
     ->  Hash Semi Join  (cost=215699.65..378494.11 rows=397217 width=8) (actual time=5439.020..6053.534 rows=61 loops=1)
           Hash Cond: (packages_packages.project_id = projects.id)
           Buffers: shared hit=259612 read=5080 dirtied=312
           I/O Timings: read=5071.090
           ->  Index Scan using index_packages_packages_on_project_id_and_package_type on public.packages_packages  (cost=0.42..19149.50 rows=397217 width=12) (actual time=0.038..5112.849 rows=398635 loops=1)
                 Buffers: shared hit=257641 read=4685 dirtied=234
                 I/O Timings: read=4498.981
           ->  Hash  (cost=1163.33..1163.33 rows=7627832 width=4) (actual time=589.815..589.816 rows=1073 loops=1)
                 Buckets: 4194304  Batches: 8  Memory Usage: 32825kB
                 Buffers: shared hit=1971 read=395 dirtied=78
                 I/O Timings: read=572.109
                 ->  Nested Loop  (cost=364.62..1163.33 rows=7627832 width=4) (actual time=13.289..587.259 rows=1073 loops=1)
                       Buffers: shared hit=1971 read=395 dirtied=78
                       I/O Timings: read=572.109
                       ->  HashAggregate  (cost=364.19..366.19 rows=200 width=4) (actual time=3.245..3.583 rows=178 loops=1)
                             Group Key: namespaces_1.id
                             Buffers: shared hit=678
                             ->  CTE Scan on base_and_descendants namespaces_1  (cost=338.16..354.18 rows=801 width=4) (actual time=0.008..3.141 rows=178 loops=1)
                                   Buffers: shared hit=678
                                   CTE base_and_descendants
                                     ->  Recursive Union  (cost=0.00..338.16 rows=801 width=4) (actual time=0.006..3.058 rows=178 loops=1)
                                           Buffers: shared hit=678
                                           ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.002..0.002 rows=1 loops=1)
                                           ->  Nested Loop  (cost=0.43..32.21 rows=80 width=4) (actual time=0.105..0.585 rows=35 loops=5)
                                                 Buffers: shared hit=678
                                                 ->  WorkTable Scan on base_and_descendants  (cost=0.00..0.20 rows=10 width=4) (actual time=0.000..0.006 rows=36 loops=5)
                                                 ->  Index Only Scan using index_namespaces_on_parent_id_and_id on public.namespaces  (cost=0.43..3.12 rows=8 width=8) (actual time=0.014..0.016 rows=1 loops=178)
                                                       Index Cond: (namespaces.parent_id = base_and_descendants.id)
                                                       Heap Fetches: 16
                                                       Buffers: shared hit=678
                       ->  Index Only Scan using index_projects_on_namespace_id_and_id on public.projects  (cost=0.43..3.81 rows=18 width=8) (actual time=2.168..3.271 rows=6 loops=178)
                             Index Cond: (projects.namespace_id = namespaces_1.id)
                             Heap Fetches: 197
                             Buffers: shared hit=1293 read=395 dirtied=78
                             I/O Timings: read=572.109
   ->  Index Scan using packages_package_files_pkey on public.packages_package_files  (cost=0.43..99.11 rows=477 width=16) (actual time=0.066..2.028 rows=946 loops=1)
         Index Cond: ((packages_package_files.id >= 1) AND (packages_package_files.id <= 1000))
         Buffers: shared hit=855
   ->  Hash  (cost=7944.34..7944.34 rows=397217 width=8) (actual time=6053.678..6053.678 rows=61 loops=1)
         Buckets: 524288  Batches: 1  Memory Usage: 4099kB
         Buffers: shared hit=259612 read=5080 dirtied=312
         I/O Timings: read=5071.090
         ->  CTE Scan on restricted_packages  (cost=0.00..7944.34 rows=397217 width=8) (actual time=5439.025..6053.595 rows=61 loops=1)
               Buffers: shared hit=259612 read=5080 dirtied=312
               I/O Timings: read=5071.090

Recommendations:
:exclamation: Query processes too much data to return a relatively small number of rows. – Reduce data cardinality as early as possible during the execution, using one or several of the following techniques: new indexes, partitioning, query rewriting, denormalization. See the visualization of the plan to understand which plan nodes are the main bottlenecks. Show details
:exclamation: Temporary buffers written – Raise work_mem (currently, the recipe is: exec alter system set work_mem to '100MB', then exec select pg_reload_conf()) Show details
:exclamation: VACUUM ANALYZE needed – Visibility map(s) for the table(s) involved in the query are outdated. For better performance: 1) run VACUUM ANALYZE on them as a one-time solution, 2) perform autovacuum tuning as a long-term permanent solution (tuning "vacuum" part of autovacuum). Show details

Summary:

Time: 6.065 s
  - planning: 2.190 ms
  - execution: 6.063 s
    - I/O read: 5.071 s
    - I/O write: 0.000 ms

Shared buffers:
  - hits: 260470 (~2.00 GiB) from the buffer pool
  - reads: 5080 (~39.70 MiB) from the OS file cache, including disk I/O
  - dirtied: 312 (~2.40 MiB)
  - writes: 0

Temp buffers:
  - reads: 351 (~2.70 MiB)
  - writes: 351 (~2.70 MiB)

After - selective sync by namespace

EXPLAIN 
SELECT 
  "packages_package_files"."id" 
FROM 
  "packages_package_files" 
  INNER JOIN "packages_packages" ON "packages_packages"."id" = "packages_package_files"."package_id" 
WHERE 
  (
    packages_packages.project_id IN (
      SELECT 
        "projects"."id" 
      FROM 
        "projects" 
      WHERE 
        "projects"."namespace_id" IN (
          WITH RECURSIVE "base_and_descendants" AS (
            (
              SELECT 
                9970 AS id
            ) 
            UNION 
              (
                SELECT 
                  "namespaces"."id" 
                FROM 
                  "namespaces", 
                  "base_and_descendants" 
                WHERE 
                  "namespaces"."parent_id" = "base_and_descendants"."id"
              )
          ) 
          SELECT 
            "id" 
          FROM 
            "base_and_descendants" AS "namespaces"
        )
    )
  ) 
  AND "packages_package_files"."id" BETWEEN 1 
  AND 1000;
 Nested Loop Semi Join  (cost=339.44..179982.93 rows=477 width=8) (actual time=277.546..342.000 rows=32 loops=1)
   Buffers: shared hit=8846 read=255 dirtied=6
   I/O Timings: read=273.810
   ->  Nested Loop  (cost=0.85..1618.49 rows=477 width=12) (actual time=0.639..10.740 rows=946 loops=1)
         Buffers: shared hit=4552 read=87
         I/O Timings: read=4.436
         ->  Index Scan using packages_package_files_pkey on public.packages_package_files  (cost=0.43..99.11 rows=477 width=16) (actual time=0.232..5.583 rows=946 loops=1)
               Index Cond: ((packages_package_files.id >= 1) AND (packages_package_files.id <= 1000))
               Buffers: shared hit=780 read=75
               I/O Timings: read=3.622
         ->  Index Scan using packages_packages_pkey on public.packages_packages  (cost=0.42..3.19 rows=1 width=12) (actual time=0.004..0.004 rows=1 loops=946)
               Index Cond: (packages_packages.id = packages_package_files.package_id)
               Buffers: shared hit=3772 read=12
               I/O Timings: read=0.813
   ->  Nested Loop Semi Join  (cost=338.59..373.92 rows=1 width=4) (actual time=0.349..0.349 rows=0 loops=946)
         Buffers: shared hit=4294 read=168 dirtied=6
         I/O Timings: read=269.374
         ->  Index Scan using projects_pkey on public.projects  (cost=0.43..1.72 rows=1 width=8) (actual time=0.049..0.050 rows=1 loops=946)
               Index Cond: (projects.id = packages_packages.project_id)
               Buffers: shared hit=3765 read=19 dirtied=1
               I/O Timings: read=43.353
         ->  CTE Scan on base_and_descendants namespaces  (cost=338.16..354.18 rows=801 width=4) (actual time=0.000..0.273 rows=172 loops=946)
               Buffers: shared hit=529 read=149 dirtied=5
               I/O Timings: read=226.021
               CTE base_and_descendants
                 ->  Recursive Union  (cost=0.00..338.16 rows=801 width=4) (actual time=0.005..232.100 rows=178 loops=1)
                       Buffers: shared hit=529 read=149 dirtied=5
                       I/O Timings: read=226.021
                       ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
                       ->  Nested Loop  (cost=0.43..32.21 rows=80 width=4) (actual time=5.344..46.357 rows=35 loops=5)
                             Buffers: shared hit=529 read=149 dirtied=5
                             I/O Timings: read=226.021
                             ->  WorkTable Scan on base_and_descendants  (cost=0.00..0.20 rows=10 width=4) (actual time=0.001..0.019 rows=36 loops=5)
                             ->  Index Only Scan using index_namespaces_on_parent_id_and_id on public.namespaces namespaces_1  (cost=0.43..3.12 rows=8 width=8) (actual time=1.048..1.299 rows=1 loops=178)
                                   Index Cond: (namespaces_1.parent_id = base_and_descendants.id)
                                   Heap Fetches: 16
                                   Buffers: shared hit=529 read=149 dirtied=5
                                   I/O Timings: read=226.021

Recommendations:
:exclamation: Query processes too much data to return a relatively small number of rows. – Reduce data cardinality as early as possible during the execution, using one or several of the following techniques: new indexes, partitioning, query rewriting, denormalization. See the visualization of the plan to understand which plan nodes are the main bottlenecks. Show details
:exclamation: VACUUM ANALYZE needed – Visibility map(s) for the table(s) involved in the query are outdated. For better performance: 1) run VACUUM ANALYZE on them as a one-time solution, 2) perform autovacuum tuning as a long-term permanent solution (tuning "vacuum" part of autovacuum). Show details

Summary:

Time: 344.654 ms
  - planning: 2.469 ms
  - execution: 342.185 ms
    - I/O read: 273.810 ms
    - I/O write: 0.000 ms

Shared buffers:
  - hits: 8846 (~69.10 MiB) from the buffer pool
  - reads: 255 (~2.00 MiB) from the OS file cache, including disk I/O
  - dirtied: 6 (~48.00 KiB)
  - writes: 0

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Edited by Michael Kozono

Merge request reports

Loading