Skip to content

Fix duplicates in create_table_with_constraints

What does this MR do?

Fix the create_table_with_constraints helper to not attempt to create duplicate constraints when the with_lock_retries wrapping the method body executes the internal create_table more than once.

For example, migration specs fail with a message like (see #324416 (closed)):

  PG::DuplicateObject: ERROR:  constraint "check_b634ca168d" for relation "external_approval_rules" already exists
# ./lib/gitlab/database/migration_helpers.rb:121:in `block in create_table_with_constraints'

This can be recreated locally when running a migration which creates a foreign key.

  1. In a database session, start a transaction, and lock the reference tabled in the below migration with a lock table namespaces in share mode
  2. Run the migration rails db:migrate:up VERSION=20201228110136
  3. Watch for the with_lock_retries to retry the migration block multiple times
  4. Rollback the transaction with the lock on namespaces
rails db:migrate:up VERSION=20201228110136
== 20201228110136 CreateIterationsCadence: migrating ==========================
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
   -> 0.0078s
-- quote_table_name("check_fedff82d3b")
   -> 0.0000s
-- quote_table_name("check_fedff82d3b")
   -> 0.0000s
-- quote_table_name("check_fedff82d3b")
   -> 0.0000s
-- quote_table_name("check_fedff82d3b")
   -> 0.0000s
-- quote_table_name("check_fedff82d3b")
   -> 0.0000s
-- quote_table_name("check_fedff82d3b")
   -> 0.0000s
-- quote_table_name("check_fedff82d3b")
   -> 0.0000s
-- quote_table_name(:iterations_cadences)
   -> 0.0000s
-- execute("ALTER TABLE \"iterations_cadences\"\nADD CONSTRAINT \"check_fedff82d3b\" CHECK (char_length(\"title\") <= 255),\nADD CONSTRAINT \"check_fedff82d3b\" CHECK (char_length(\"title\") <= 255),\nADD CONSTRAINT \"check_fedff82d3b\" CHECK (char_length(\"title\") <= 255),\nADD CONSTRAINT \"check_fedff82d3b\" CHECK (char_length(\"title\") <= 255),\nADD CONSTRAINT \"check_fedff82d3b\" CHECK (char_length(\"title\") <= 255),\nADD CONSTRAINT \"check_fedff82d3b\" CHECK (char_length(\"title\") <= 255),\nADD CONSTRAINT \"check_fedff82d3b\" CHECK (char_length(\"title\") <= 255)\n")

There is one definition of the check constraint for each time with_lock_retries executed the inner block. After the behavior has been fixed, the output looks like:

rails db:migrate:up VERSION=20201228110136
== 20201228110136 CreateIterationsCadence: migrating ==========================
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
-- create_table(:iterations_cadences, {})
-- quote_column_name(:title)
   -> 0.0000s
   -> 0.4345s
-- quote_table_name("check_fedff82d3b")
   -> 0.0000s
-- quote_table_name(:iterations_cadences)
   -> 0.0000s
-- execute("ALTER TABLE \"iterations_cadences\"\nADD CONSTRAINT \"check_fedff82d3b\" CHECK (char_length(\"title\") <= 255)\n")
   -> 0.0009s
== 20201228110136 CreateIterationsCadence: migrated (2.0057s) =================

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 Patrick Bair

Merge request reports