Skip to content

Optimize query for cherry picked merge requests

What does this MR do?

This MR fixes Query optimization for the cherry picked merge request search. Currently, the query takes some time on a project with a lot of notes, for example:

SELECT
    "merge_requests".*
FROM
    "merge_requests"
WHERE
    "merge_requests"."target_project_id" = 278964
    AND "merge_requests"."id" IN (
        SELECT
            "notes"."noteable_id"
        FROM
            "notes"
        WHERE
            "notes"."project_id" = 278964
            AND "notes"."noteable_type" = 'MergeRequest'
            AND "notes"."commit_id" IN (
                'c17848ad6b4cc5d5482bf085449ee9e8024a116e',
                'd967959a0ae7d69e6a3503fd25e8c41473a3b1a9',
                'e4339f5b10ab53d99c24597c3e18647d8714a7d5',
                'a4d05d06cbef35b62774eb08e60661b6b4e88d38',
                '44f055b8716a6443091887c4a5f60276c30b553c',
                'c2f25b2b250781a6517c246db71e87c7903e73d6',
                'fdce57e682a35a54d03cc5aa81ec3b5f2b4380d2',
                '35c2819f52cddd9c9f4a4a6832998255c66b883f',
                'ef5c42dd6e67d49f199ae7ae78e00edcf3b876a6',
                'ecdb5a383e6a73dee6fe62922c45160a647ef877',
                'ea44183c072fd087272c2df5713bf0c5a2a93bb1',
                'c77c0e915274fe2ba2baf9af25c8a8c8c35e223d',
                '4d4d28ec1379e8d79e293333d13622c387e822b7',
                '33d1ae4d27671df52f2611d1e4fa82f918c464fe',
                '1e74cc70e47c45616d1c60959441ade527bf4285',
                '74fce67aeacab475822ca43304a05fb88fa10a28',
                'dd1c00464ae96eb6d33f0d6db0708dd65c12dc8c',
                '9a7a66c356cee302a5e1fa78ebba0246089892c7',
                '4b950dc2f8b679e5dac08ba3cefa391855659a3f',
                'e68f6c96e808a77cde2b9a5832669deacbcfaf3a',
                '31cbf8dc37a03962667b9d9b9263ae401b6e6656',
                'ea08abd199a46b5f4f4a83f749640c130624b990',
                '0b90af665865e676fa5fdfc613c544e9411eff68',
                'ae271da6564a0a01fbea1c777859603be3a09262',
                '6abd28acf90c176903cabfd24fee965dff3d9148',
                '3c32f6d9d45b7022792f71ff9765bf3419004c6f',
                'db3ce6cd9e15ce2d37a349b25f8689fb928b9908',
                '38186b3c790e031f99ac1dad70996803a79f8f7d',
                'fd1ecb9d5929c834b5bd106f138f3fccf11a1ff8',
                'a2b544427866d4e59c55adea017c6ad5ed62fbb5',
                '9ef59ce58b6ae3835925b98f91033f11a2f37bac',
                '0bc379961f9634632d2a74a506604c2bdafeaa6d',
                '0e07d4db7d7f3557e82f777adbf948de8444e997',
                '4e6ccdb1477b9ed39ff774d476b286082628bcad',
                '7ee7ad9344c2d36c691773a480675904bd0f5748',
                '1030a56f2a4ed4d3b357a094c93cd643354993f1',
                '4eb4de9c0837d22839a55be7dc60bcad90d2211a',
                '523333da9a45159516f3b3a3d0c6bf7d97821ba4',
                '6af841bafd8c105f75b0eb3733174ea6c18d8983',
                '5c0d670b5ea5697c33a96c81459d430b82df3981',
                '23d95f17c24a3ec8113a65d5af6afd871b5036ba',
                '77b495f77200dede8e107049faf0618f4503a253',
                '87ff1bdd2fdb114ff193d7b062bc1662878b9f68',
                '95051adaf38fbbba4b8cdf062c66847a7f9b528d',
                '6abf99ae70285b2ff05a0dcce7a344439c225ec9',
                '47420c38ac1288c8741c27fd0a1cfdb7c7153e3a',
                '28471cf5e7d86e09d89afd3e1669ad3d294f0aad',
                '41303181ac2adc35b77f8e9cf56c1d992fc71d56',
                '6fde063f3a6df83ce582da170be86d5597d15506',
                '41e9595688f1531a36105f1a37d8a475e67b82ef',
                'dbd59feb10d5f6102f41b3d47ed3870a2f81b091',
                '3f5d0012ab463d240c944b6a395253c5645726c5',
                'e609f9594ca8940183a389dc3d94d3bbdbfa350b',
                'b58b53fccf77324ecec80283706738ffec07be77',
                '1a35f564e867a9746f3f0817b4568c4f38a8a8a8',
                '3a766b987aa6ec97c4a9566d921701dcbfb4b328',
                '4aa39fc5f1b48102bb3a24399b70f3ea91840ae6',
                'a6db63145b0acdb0306087de412c1eb00a213183',
                '2927d330cd0171cdc5c1ae791f65b8d22af688cc',
                'ecf38f38830fba5557bff858b91fa3d5a45b2c83',
                '6d72f39cc350b00c7a2ee7a804753f25241119ab',
                'a9b32f613c290ed4bbabda5f459023266833e8eb',
                '9c826440a9a161f0dbdeca18ec99036f87fee8e6',
                '499d436d7a8408d7cf1e0be7ab2f9cbb1703ae9d',
                'e9b0d74d9045a0a5e0806689bdb6b5b5d90ef654',
                'db3b102d41b463edd773916dcc07ccc36a5ce595',
                '473ccd7f5e159601b91ed0641e5139c2685fcdad',
                'bfeeecdf01cf2694b4a5af1a70b3bf45b9c9a609',
                'efd684241847291b1d2776f7aefef2035a67cfb9',
                '697459c14a4b61c1882a764b5bebd4d2e75cf1c3',
                'ae11d316c0f183f017d9df013dea15987aefb215',
                'c6137e874843fa4015c169091bb7ec855b918c58',
                'c899740a5eea05969ddd52614465df23ae237ba3',
                '96d02a33573233f9776a53e7487ebdb164e853db',
                'd290f0aa9f661cfc75fbcc00ee1230f95c861e09',
                'e2b12946c24ecd91358c4a155c5129b884d02ac6',
                'b5fd7238480df96e0ce1b5c307228843fec60f71',
                '08022572a2db1e4dde86fde623fe69777eeb2d2b',
                '7029cd7787ac107591417e7e21733d32fcf869fa',
                '563940baf3362ff835384a4979e7c61845c1d626',
                '2eb065b233646bacef9b58717742d4b02ee0300e',
                '42430d0619c11eeea7baeb033452acd0c479c7f8',
                '3e826e66186377759583e15b8fddce1b21dc3033',
                'e475734438a120375a4f487bfb46f4e05c02ab7c',
                '9c4c93ae5099eadb96e7a13ce18df361240e33a8',
                '9e50af23ae1b13c0b524322990e9fb17fb167112',
                '8f5e0fbaf89c4535ff84c39c2bff23b1e22d6b24',
                '556e642e5b3682e8a9e57b5dfb9b41ee0b79f79c',
                '939028e7634707dcde050560de02a56f3c3e4d06',
                '5bd754a6b396413ba86df2a4bcaf7b31dfdfa219',
                '32949cb92f007e0f68a8b796706fb4c492a69d1e',
                '602b3e44230d17359eba679da13608a40ff93f3f',
                '5d5cec6d7acc87cbc558b4be767d05d50d081a69',
                'dc3d354a9f917cb6f5b4913b1823d30324a98a43',
                '48edc67557e16f5f8447079ae87f4419fae20f55',
                '1b554254a8dbac0907df317a7606f445e7fe4ed9',
                'abae41e8e725915a4e07717a1c605798f75cbe40',
                'c8f244f1570f3f0b65f1762fb3a4eb30a5394686',
                '2fa64e76f60ff5a58ff85d0a977cf76729a4195d',
                '781df9fc8d0f1760b86fc6e088036fbf6f2cdb79',
                '1ff17bc2d1745fc8a4562303591e51f85d082b31',
                'd3f46313846e824fb48547ff99010a2dc0925df6',
                '9b9ba6b8a90a0e2fb8b7674fc6d7062a034eb456'
            )
    )

Query performance (in db-lab)

Query plan

https://explain.depesz.com/s/NTia

Timing

Time: 1.071 min
  - planning: 12.405 ms
  - execution: 1.071 min (estimated* for prod: 3.292...58.719 s)
    - I/O read: 1.020 min
    - I/O write: N/A

Shared buffers:
  - hits: 167 (~1.30 MiB) from the buffer pool
  - reads: 31241 (~244.10 MiB) from the OS file cache, including disk I/O
  - dirtied: 0
  - writes: 0

Given that the query searching the notes with index_notes_on_commit_id and index_notes_on_project_id_and_noteable_type but merging the results sequentially, it can be expensive if the project has too many notes and cherry-picked commits.

Approach

We should create an index on notes table for optimizing the query, something like:

exec CREATE INDEX test_index ON notes USING btree (project_id, commit_id) WHERE ((noteable_type)::text = 'MergeRequest'::text);

Query plan

https://explain.depesz.com/s/66Dy

Timing

Time: 67.225 ms
  - planning: 9.920 ms
  - execution: 57.305 ms
    - I/O read: 56.003 ms
    - I/O write: N/A

Shared buffers:
  - hits: 391 (~3.10 MiB) from the buffer pool
  - reads: 26 (~208.00 KiB) from the OS file cache, including disk I/O
  - dirtied: 0
  - writes: 0

Index details (in db-lab)

Size

List of relations
 Schema |    Name    | Type  |   Owner    | Table |  Size   | Description 
--------+------------+-------+------------+-------+---------+-------------
 public | test_index | index | joe_shinya | notes | 9036 MB | 
(1 row)

Timing

% time      seconds wait_event
------ ------------ -----------------------------
91.49  20253.170848 IO.DataFileRead
3.75     830.045994 Running
2.50     554.073752 IO.DataFileExtend
1.10     244.349545 IO.BufFileWrite
0.74     164.107568 LWLock.WALWriteLock
0.20      43.834928 IO.DataFileWrite
0.18      40.465748 IO.BufFileRead
0.01       2.997461 LWLock.buffer_mapping
0.01       2.918001 IO.SLRURead
0.00       0.809943 IO.WALWrite
0.00       0.336951 LWLock.WALBufMappingLock
0.00       0.011034 LWLock.clog
------ ------------ -----------------------------
100.00 22137.121773

Local migration log

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ tre bin/rails db:migrate:redo VERSION=20210409084242
INFO: This script is a predefined script in devkitkat.
== 20210409084242 CreateIndexOnNotesForCherryPickedMergeRequests: reverting ===
-- transaction_open?()
   -> 0.0000s
-- indexes(:notes)
   -> 0.0066s
-- remove_index(:notes, {:algorithm=>:concurrently, :name=>"index_notes_for_cherry_picked_merge_requests"})
   -> 0.0024s
== 20210409084242 CreateIndexOnNotesForCherryPickedMergeRequests: reverted (0.0112s) 

== 20210409084242 CreateIndexOnNotesForCherryPickedMergeRequests: migrating ===
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:notes, [:project_id, :commit_id], {:where=>"((noteable_type)::text = 'MergeRequest'::text)", :name=>"index_notes_for_cherry_picked_merge_requests", :algorithm=>:concurrently})
   -> 0.0039s
-- add_index(:notes, [:project_id, :commit_id], {:where=>"((noteable_type)::text = 'MergeRequest'::text)", :name=>"index_notes_for_cherry_picked_merge_requests", :algorithm=>:concurrently})
   -> 0.0052s
== 20210409084242 CreateIndexOnNotesForCherryPickedMergeRequests: migrated (0.0097s) 

Screenshots (strongly suggested)

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • Label as security and @ mention @gitlab-com/gl-security/appsec
  • The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • Security reports checked/validated by a reviewer from the AppSec team
Edited by Shinya Maeda

Merge request reports

Loading