Skip to content

Add Migration to Index members on user_id, source_id, and source_type

What does this MR do?

Adds a migration to add an index to the members table on the user_id, source_id, and source_type columns.

Also adds a migration to remove the index on the user_id column.

This improves the performance of SQL found in !58506 (merged).

See: !58506 (comment 548430730)

Also see: !58506 (comment 544046217)

Migration Output

$ bin/rails dbconsole
psql (11.11)
Type "help" for help.

gitlabhq_development=# \d members
                                            Table "public.members"
       Column       |            Type             | Collation | Nullable |               Default               
--------------------+-----------------------------+-----------+----------+-------------------------------------
 id                 | integer                     |           | not null | nextval('members_id_seq'::regclass)
 access_level       | integer                     |           | not null | 
 source_id          | integer                     |           | not null | 
 source_type        | character varying           |           | not null | 
 user_id            | integer                     |           |          | 
 notification_level | integer                     |           | not null | 
 type               | character varying           |           |          | 
 created_at         | timestamp without time zone |           |          | 
 updated_at         | timestamp without time zone |           |          | 
 created_by_id      | integer                     |           |          | 
 invite_email       | character varying           |           |          | 
 invite_token       | character varying           |           |          | 
 invite_accepted_at | timestamp without time zone |           |          | 
 requested_at       | timestamp without time zone |           |          | 
 expires_at         | date                        |           |          | 
 ldap               | boolean                     |           | not null | false
 override           | boolean                     |           | not null | false
Indexes:
    "members_pkey" PRIMARY KEY, btree (id)
    "index_members_on_invite_token" UNIQUE, btree (invite_token)
    "idx_members_created_at_user_id_invite_token" btree (created_at) WHERE invite_token IS NOT NULL AND user_id IS NULL
    "index_members_on_access_level" btree (access_level)
    "index_members_on_expires_at" btree (expires_at)
    "index_members_on_invite_email" btree (invite_email)
    "index_members_on_requested_at" btree (requested_at)
    "index_members_on_source_id_and_source_type" btree (source_id, source_type)
    "index_members_on_user_id" btree (user_id)
    "index_members_on_user_id_created_at" btree (user_id, created_at) WHERE ldap = true AND type::text = 'GroupMember'::text AND source_type::text = 'Namespace'::text
    "index_non_requested_project_members_on_source_id_and_type" btree (source_id, source_type) WHERE requested_at IS NULL AND type::text = 'ProjectMember'::text
Foreign-key constraints:
    "fk_2e88fb7ce9" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE

gitlabhq_development=# \q
$ bin/rails db:migrate
== 20210409185501 IndexMembersOnUserIdSourceIdSourceType: migrating ===========
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:members, [:user_id, :source_id, :source_type], {:name=>"index_members_on_user_id_source_id_source_type", :algorithm=>:concurrently})
   -> 0.0053s
-- execute("SET statement_timeout TO 0")
   -> 0.0006s
-- add_index(:members, [:user_id, :source_id, :source_type], {:name=>"index_members_on_user_id_source_id_source_type", :algorithm=>:concurrently})
   -> 0.0050s
-- execute("RESET ALL")
   -> 0.0007s
== 20210409185501 IndexMembersOnUserIdSourceIdSourceType: migrated (0.0125s) ==

== 20210409185531 RemoveMembersIndexOnUserId: migrating =======================
-- transaction_open?()
   -> 0.0000s
-- indexes(:members)
   -> 0.0043s
-- remove_index(:members, {:algorithm=>:concurrently, :name=>"index_members_on_user_id"})
   -> 0.0047s
== 20210409185531 RemoveMembersIndexOnUserId: migrated (0.0098s) ==============

$ bin/rails dbconsole
psql (11.11)
Type "help" for help.

gitlabhq_development=# \d members
                                            Table "public.members"
       Column       |            Type             | Collation | Nullable |               Default               
--------------------+-----------------------------+-----------+----------+-------------------------------------
 id                 | integer                     |           | not null | nextval('members_id_seq'::regclass)
 access_level       | integer                     |           | not null | 
 source_id          | integer                     |           | not null | 
 source_type        | character varying           |           | not null | 
 user_id            | integer                     |           |          | 
 notification_level | integer                     |           | not null | 
 type               | character varying           |           |          | 
 created_at         | timestamp without time zone |           |          | 
 updated_at         | timestamp without time zone |           |          | 
 created_by_id      | integer                     |           |          | 
 invite_email       | character varying           |           |          | 
 invite_token       | character varying           |           |          | 
 invite_accepted_at | timestamp without time zone |           |          | 
 requested_at       | timestamp without time zone |           |          | 
 expires_at         | date                        |           |          | 
 ldap               | boolean                     |           | not null | false
 override           | boolean                     |           | not null | false
Indexes:
    "members_pkey" PRIMARY KEY, btree (id)
    "index_members_on_invite_token" UNIQUE, btree (invite_token)
    "idx_members_created_at_user_id_invite_token" btree (created_at) WHERE invite_token IS NOT NULL AND user_id IS NULL
    "index_members_on_access_level" btree (access_level)
    "index_members_on_expires_at" btree (expires_at)
    "index_members_on_invite_email" btree (invite_email)
    "index_members_on_requested_at" btree (requested_at)
    "index_members_on_source_id_and_source_type" btree (source_id, source_type)
    "index_members_on_user_id_created_at" btree (user_id, created_at) WHERE ldap = true AND type::text = 'GroupMember'::text AND source_type::text = 'Namespace'::text
    "index_members_on_user_id_source_id_source_type" btree (user_id, source_id, source_type)
    "index_non_requested_project_members_on_source_id_and_type" btree (source_id, source_type) WHERE requested_at IS NULL AND type::text = 'ProjectMember'::text
Foreign-key constraints:
    "fk_2e88fb7ce9" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE

gitlabhq_development=# \q
$ bin/rails db:rollback STEP=2
== 20210409185531 RemoveMembersIndexOnUserId: reverting =======================
-- transaction_open?()
   -> 0.0000s
-- index_exists?(:members, :user_id, {:name=>"index_members_on_user_id", :algorithm=>:concurrently})
   -> 0.0040s
-- execute("SET statement_timeout TO 0")
   -> 0.0006s
-- add_index(:members, :user_id, {:name=>"index_members_on_user_id", :algorithm=>:concurrently})
   -> 0.0032s
-- execute("RESET ALL")
   -> 0.0005s
== 20210409185531 RemoveMembersIndexOnUserId: reverted (0.0091s) ==============

== 20210409185501 IndexMembersOnUserIdSourceIdSourceType: reverting ===========
-- transaction_open?()
   -> 0.0000s
-- indexes(:members)
   -> 0.0039s
-- remove_index(:members, {:algorithm=>:concurrently, :name=>"index_members_on_user_id_source_id_source_type"})
   -> 0.0030s
== 20210409185501 IndexMembersOnUserIdSourceIdSourceType: reverted (0.0078s) ==

$ bin/rails dbconsole
psql (11.11)
Type "help" for help.

gitlabhq_development=# \d members
                                            Table "public.members"
       Column       |            Type             | Collation | Nullable |               Default               
--------------------+-----------------------------+-----------+----------+-------------------------------------
 id                 | integer                     |           | not null | nextval('members_id_seq'::regclass)
 access_level       | integer                     |           | not null | 
 source_id          | integer                     |           | not null | 
 source_type        | character varying           |           | not null | 
 user_id            | integer                     |           |          | 
 notification_level | integer                     |           | not null | 
 type               | character varying           |           |          | 
 created_at         | timestamp without time zone |           |          | 
 updated_at         | timestamp without time zone |           |          | 
 created_by_id      | integer                     |           |          | 
 invite_email       | character varying           |           |          | 
 invite_token       | character varying           |           |          | 
 invite_accepted_at | timestamp without time zone |           |          | 
 requested_at       | timestamp without time zone |           |          | 
 expires_at         | date                        |           |          | 
 ldap               | boolean                     |           | not null | false
 override           | boolean                     |           | not null | false
Indexes:
    "members_pkey" PRIMARY KEY, btree (id)
    "index_members_on_invite_token" UNIQUE, btree (invite_token)
    "idx_members_created_at_user_id_invite_token" btree (created_at) WHERE invite_token IS NOT NULL AND user_id IS NULL
    "index_members_on_access_level" btree (access_level)
    "index_members_on_expires_at" btree (expires_at)
    "index_members_on_invite_email" btree (invite_email)
    "index_members_on_requested_at" btree (requested_at)
    "index_members_on_source_id_and_source_type" btree (source_id, source_type)
    "index_members_on_user_id" btree (user_id)
    "index_members_on_user_id_created_at" btree (user_id, created_at) WHERE ldap = true AND type::text = 'GroupMember'::text AND source_type::text = 'Namespace'::text
    "index_non_requested_project_members_on_source_id_and_type" btree (source_id, source_type) WHERE requested_at IS NULL AND type::text = 'ProjectMember'::text
Foreign-key constraints:
    "fk_2e88fb7ce9" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE

gitlabhq_development=# \q
$ 

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 Jason Goodman

Merge request reports