Skip to content

Schedule async creation of route indexes

Jan Provaznik requested to merge jp-route-idx into master

What does this MR do and why?

If we want to create routes for ProjectNamespaces with the same path is corresponding Project route, we first need to replace existing index_routes_on_path unique index with a couple of indexes so uniqueness is checked only for "not project namespace paths".

Because routes table is big, these indexes will be created asynchronously (https://docs.gitlab.com/ee/development/adding_database_indexes.html#schedule-the-index-to-be-created).

Additional info

There are two possible migration strategies discussed in &7184 (comment 755099066). This MR assumes that we would use strategy when project namespace routes are first backfilled for all projects, then project routes are deleted. If DB team suggests rather different strategy (in line replacement of existing project route with project namespace route, this MR wouldn't be needed. Pros/cons of each strategy is discussed in the linked discussion.

DB migration

$ rake db:migrate
== 20211208081833 ExcludeProjectNamespaceFromRoutePathUniqueIndex: migrating ==
== 20211208081833 ExcludeProjectNamespaceFromRoutePathUniqueIndex: migrated (0.0022s)

$ rake db:rollback
== 20211208081833 ExcludeProjectNamespaceFromRoutePathUniqueIndex: reverting ==
== 20211208081833 ExcludeProjectNamespaceFromRoutePathUniqueIndex: reverted (0.0015s)

The output above is not very helpful, if done synchronously with add/remove_concurrent_indexes, the output is:

$ rake db:migrate
== 20211208081833 ExcludeProjectNamespaceFromRoutePathUniqueIndex: migrating ==
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:routes, :path, {:where=>"source_type = 'Namespaces::ProjectNamespace'", :unique=>true, :name=>"tmp_index_routes_on_path_where_project_namespace", :algorithm=>:concurrently})
   -> 0.0030s
-- execute("SET statement_timeout TO 0")
   -> 0.0004s
-- add_index(:routes, :path, {:where=>"source_type = 'Namespaces::ProjectNamespace'", :unique=>true, :name=>"tmp_index_routes_on_path_where_project_namespace", :algorithm=>:concurrently})
   -> 0.0216s
-- execute("RESET statement_timeout")
   -> 0.0005s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:routes, :path, {:where=>"source_type != 'Namespaces::ProjectNamespace'", :unique=>true, :name=>"tmp_index_routes_on_path_where_not_project_namespace", :algorithm=>:concurrently})
   -> 0.0022s
-- add_index(:routes, :path, {:where=>"source_type != 'Namespaces::ProjectNamespace'", :unique=>true, :name=>"tmp_index_routes_on_path_where_not_project_namespace", :algorithm=>:concurrently})
   -> 1.2125s
== 20211208081833 ExcludeProjectNamespaceFromRoutePathUniqueIndex: migrated (1.2430s) 

$ rake db:rollback
== 20211208081833 ExcludeProjectNamespaceFromRoutePathUniqueIndex: reverting ==
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:routes, :path, {:name=>"tmp_index_routes_on_path_where_project_namespace", :algorithm=>:concurrently})
   -> 0.0036s
-- execute("SET statement_timeout TO 0")
   -> 0.0011s
-- remove_index(:routes, {:name=>"tmp_index_routes_on_path_where_project_namespace", :algorithm=>:concurrently, :column=>:path})
   -> 0.0207s
-- execute("RESET statement_timeout")
   -> 0.0004s
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:routes, :path, {:name=>"tmp_index_routes_on_path_where_not_project_namespace", :algorithm=>:concurrently})
   -> 0.0021s
-- remove_index(:routes, {:name=>"tmp_index_routes_on_path_where_not_project_namespace", :algorithm=>:concurrently, :column=>:path})
   -> 0.0077s
== 20211208081833 ExcludeProjectNamespaceFromRoutePathUniqueIndex: reverted (0.0396s) 

Estimated creation time for "other than project namespace" routes is ~5 minutes:

exec CREATE UNIQUE INDEX CONCURRENTLY "tmp_index_routes_on_path_where_not_project_namespace" ON "routes" ("path") WHERE source_type != 'Namespaces::ProjectNamespace'
Session: webui-i7534
The query has been executed. Duration: 5.759 min

Related to #346448 (closed)

Screenshots or screen recordings

These are strongly recommended to assist reviewers and reduce the time to merge your change.

How to set up and validate locally

Numbered steps to set up and validate the change are strongly suggested.

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Jan Provaznik

Merge request reports