Re-organise "issues" indexes for faster ordering
This MR adds a variety of composite indexes to the "issues" table to reduce time spent getting issue lists. See the following resources for more information:
- https://gitlab.com/gitlab-org/gitlab-ce/issues/27165
- https://gitlab.com/gitlab-org/gitlab-ce/issues/27165#note_35373285
- https://gitlab.com/gitlab-org/gitlab-ce/issues/27165#note_35376294
Database Checklist
When adding migrations:
-
Updated db/schema.rb
-
Added a down
method so the migration can be reverted -
Added the output of the migration(s) to the MR body -
Added the execution time of the migration(s) to the MR body -
Made sure the migration won't interfere with a running GitLab cluster, for example by disabling transactions for long running migrations
When adding indexes:
-
Described the need for these indexes in the MR body -
Made sure existing indexes can not be reused instead - We are removing some existing indexes (that are no longer necessary) to balance things out
When removing columns, tables, indexes or other structures:
-
Removed these in a post-deployment migration - The indexes are removed in a regular migration since we first add the new indexes, thus this is safe to do
Migration Output
== ReorganiseIssuesIndexesForFasterSorting: migrating ========================
-- index_exists?(:issues, [:project_id, :created_at, :id, :state])
-> 0.0154s
-- transaction_open?()
-> 0.0000s
-- execute("SET statement_timeout TO 0")
-> 0.0011s
-- add_index(:issues, [:project_id, :created_at, :id, :state], {:algorithm=>:concurrently})
-> 33.6328s
-- index_exists?(:issues, [:project_id, :due_date, :id, :state])
-> 0.0120s
-- transaction_open?()
-> 0.0000s
-- execute("SET statement_timeout TO 0")
-> 0.0010s
-- add_index(:issues, [:project_id, :due_date, :id, :state], {:algorithm=>:concurrently})
-> 16.6955s
-- index_exists?(:issues, [:project_id, :updated_at, :id, :state])
-> 0.0121s
-- transaction_open?()
-> 0.0000s
-- execute("SET statement_timeout TO 0")
-> 0.0005s
-- add_index(:issues, [:project_id, :updated_at, :id, :state], {:algorithm=>:concurrently})
-> 16.7783s
-- index_exists?(:issues, :project_id)
-> 0.0130s
-- index_exists?(:issues, :created_at)
-> 0.0121s
-- transaction_open?()
-> 0.0000s
-- select_one("SELECT current_setting('server_version_num') AS v")
-> 0.0016s
-- execute("SET statement_timeout TO 0")
-> 0.0007s
-- remove_index(:issues, {:algorithm=>:concurrently, :column=>:created_at})
-> 0.0360s
-- index_exists?(:issues, :due_date)
-> 0.0144s
-- transaction_open?()
-> 0.0000s
-- select_one("SELECT current_setting('server_version_num') AS v")
-> 0.0007s
-- execute("SET statement_timeout TO 0")
-> 0.0006s
-- remove_index(:issues, {:algorithm=>:concurrently, :column=>:due_date})
-> 0.0234s
-- index_exists?(:issues, :updated_at)
-> 0.0118s
== ReorganiseIssuesIndexesForFasterSorting: migrated (67.2663s) ==============
Migration Timings
Migration | Time | Environment |
---|---|---|
ReorganiseIssuesIndexesForFasterSorting | 67.26 sec | staging |
Performance Impact
I used the following simple query to demonstrate the impact:
SELECT "issues".*
FROM "issues"
WHERE "issues"."deleted_at" IS NULL
AND "issues"."project_id" = 13083
AND "issues"."state" = 'opened'
ORDER BY "issues"."created_at" DESC, "issues"."id" DESC
The query plan before this migration:
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------
Sort (cost=50710.24..50730.65 rows=8166 width=1291) (actual time=62.352..65.565 rows=7914 loops=1)
Sort Key: created_at DESC, id DESC
Sort Method: quicksort Memory: 13784kB
-> Index Scan using index_issues_on_project_id on issues (cost=0.43..50179.63 rows=8166 width=1291) (actual time=0.067..45.894 rows=7914 loops=1)
Index Cond: (project_id = 13083)
Filter: ((deleted_at IS NULL) AND ((state)::text = 'opened'::text))
Rows Removed by Filter: 19251
Planning time: 0.243 ms
Execution time: 67.294 ms
And the plan after the index changes are applied:
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Index Scan Backward using index_issues_on_project_id_and_created_at_and_id_and_state on issues (cost=0.56..16401.37 rows=8166 width=1291) (actual time=0.034..20.491 rows=7914 loops=1)
Index Cond: ((project_id = 13083) AND ((state)::text = 'opened'::text))
Filter: (deleted_at IS NULL)
Rows Removed by Filter: 1522
Planning time: 0.203 ms
Execution time: 22.048 ms
General Checklist
-
Changelog entry added, if necessary -
Conform by the merge request performance guides -
Conform by the style guides -
Squashed related commits together
Edited by yorickpeterse-staging