diff options
author | Andreas Brandl <abrandl@gitlab.com> | 2018-03-20 17:50:07 +0300 |
---|---|---|
committer | Andreas Brandl <abrandl@gitlab.com> | 2018-03-20 20:44:00 +0300 |
commit | c914883a2b350bb53313df3eb97e6e0064d9f655 (patch) | |
tree | dd180c3287f0899e7a4953c36d4bb8060ed5a144 /lib/gitlab/database | |
parent | 1362d9fe1383a2fa2cca563435064e622ec8e043 (diff) |
Shortcut concurrent foreign key creation if already exists.
Closes #43887.
Diffstat (limited to 'lib/gitlab/database')
-rw-r--r-- | lib/gitlab/database/migration_helpers.rb | 55 |
1 files changed, 37 insertions, 18 deletions
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb index 55160ca8708..44ca434056f 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -155,6 +155,13 @@ module Gitlab # of PostgreSQL's "VALIDATE CONSTRAINT". As a result we'll just fall # back to the normal foreign key procedure. if Database.mysql? + if foreign_key_exists?(source, target, column: column) + Rails.logger.warn "Foreign key not created because it exists already " \ + "(this may be due to an aborted migration or similar): " \ + "source: #{source}, target: #{target}, column: #{column}" + return + end + return add_foreign_key(source, target, column: column, on_delete: on_delete) @@ -166,25 +173,43 @@ module Gitlab key_name = concurrent_foreign_key_name(source, column) - # Using NOT VALID allows us to create a key without immediately - # validating it. This means we keep the ALTER TABLE lock only for a - # short period of time. The key _is_ enforced for any newly created - # data. - execute <<-EOF.strip_heredoc - ALTER TABLE #{source} - ADD CONSTRAINT #{key_name} - FOREIGN KEY (#{column}) - REFERENCES #{target} (id) - #{on_delete ? "ON DELETE #{on_delete.upcase}" : ''} - NOT VALID; - EOF + unless foreign_key_exists?(source, target, column: column) + Rails.logger.warn "Foreign key not created because it exists already " \ + "(this may be due to an aborted migration or similar): " \ + "source: #{source}, target: #{target}, column: #{column}" + + # Using NOT VALID allows us to create a key without immediately + # validating it. This means we keep the ALTER TABLE lock only for a + # short period of time. The key _is_ enforced for any newly created + # data. + execute <<-EOF.strip_heredoc + ALTER TABLE #{source} + ADD CONSTRAINT #{key_name} + FOREIGN KEY (#{column}) + REFERENCES #{target} (id) + #{on_delete ? "ON DELETE #{on_delete.upcase}" : ''} + NOT VALID; + EOF + end # Validate the existing constraint. This can potentially take a very # long time to complete, but fortunately does not lock the source table # while running. + # + # Note this is a no-op in case the constraint is VALID already execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{key_name};") end + def foreign_key_exists?(source, target = nil, column: nil) + foreign_keys(source).any? do |key| + if column + key.options[:column].to_s == column.to_s + else + key.to_table.to_s == target.to_s + end + end + end + # Returns the name for a concurrent foreign key. # # PostgreSQL constraint names have a limit of 63 bytes. The logic used @@ -875,12 +900,6 @@ into similar problems in the future (e.g. when new tables are created). end end - def foreign_key_exists?(table, column) - foreign_keys(table).any? do |key| - key.options[:column] == column.to_s - end - end - # Rails' index_exists? doesn't work when you only give it a table and index # name. As such we have to use some extra code to check if an index exists for # a given name. |