Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/database/partitioning_migration_helpers/foreign_key_helpers.rb')
-rw-r--r--lib/gitlab/database/partitioning_migration_helpers/foreign_key_helpers.rb74
1 files changed, 74 insertions, 0 deletions
diff --git a/lib/gitlab/database/partitioning_migration_helpers/foreign_key_helpers.rb b/lib/gitlab/database/partitioning_migration_helpers/foreign_key_helpers.rb
index 2def3a4d3a9..4402c42b136 100644
--- a/lib/gitlab/database/partitioning_migration_helpers/foreign_key_helpers.rb
+++ b/lib/gitlab/database/partitioning_migration_helpers/foreign_key_helpers.rb
@@ -6,6 +6,80 @@ module Gitlab
module ForeignKeyHelpers
include ::Gitlab::Database::SchemaHelpers
+ # Adds a foreign key with only minimal locking on the tables involved.
+ #
+ # In concept it works similarly to add_concurrent_foreign_key, but we have
+ # to add a special helper for partitioned tables for the following reasons:
+ # - add_concurrent_foreign_key sets the constraint to `NOT VALID`
+ # before validating it
+ # - Setting an FK to NOT VALID is not supported currently in Postgres (up to PG13)
+ # - Also, PostgreSQL will currently ignore NOT VALID constraints on partitions
+ # when adding a valid FK to the partitioned table, so they have to
+ # also be validated before we can add the final FK.
+ # Solution:
+ # - Add the foreign key first to each partition by using
+ # add_concurrent_foreign_key and validating it
+ # - Once all partitions have a foreign key, add it also to the partitioned
+ # table (there will be no need for a validation at that level)
+ # For those reasons, this method does not include an option to delay the
+ # validation, we have to force validate: true.
+ #
+ # source - The source (partitioned) table containing the foreign key.
+ # target - The target table the key points to.
+ # column - The name of the column to create the foreign key on.
+ # on_delete - The action to perform when associated data is removed,
+ # defaults to "CASCADE".
+ # name - The name of the foreign key.
+ #
+ def add_concurrent_partitioned_foreign_key(source, target, column:, on_delete: :cascade, name: nil)
+ partition_options = {
+ column: column,
+ on_delete: on_delete,
+
+ # We'll use the same FK name for all partitions and match it to
+ # the name used for the partitioned table to follow the convention
+ # used by PostgreSQL when adding FKs to new partitions
+ name: name.presence || concurrent_partitioned_foreign_key_name(source, column),
+
+ # Force the FK validation to true for partitions (and the partitioned table)
+ validate: true
+ }
+
+ if foreign_key_exists?(source, target, **partition_options)
+ warning_message = "Foreign key not created because it exists already " \
+ "(this may be due to an aborted migration or similar): " \
+ "source: #{source}, target: #{target}, column: #{partition_options[:column]}, "\
+ "name: #{partition_options[:name]}, on_delete: #{partition_options[:on_delete]}"
+
+ Gitlab::AppLogger.warn warning_message
+
+ return
+ end
+
+ partitioned_table = find_partitioned_table(source)
+
+ partitioned_table.postgres_partitions.order(:name).each do |partition|
+ add_concurrent_foreign_key(partition.identifier, target, **partition_options)
+ end
+
+ with_lock_retries do
+ add_foreign_key(source, target, **partition_options)
+ end
+ end
+
+ # Returns the name for a concurrent partitioned foreign key.
+ #
+ # Similar to concurrent_foreign_key_name (Gitlab::Database::MigrationHelpers)
+ # we just keep a separate method in case we want a different behavior
+ # for partitioned tables
+ #
+ def concurrent_partitioned_foreign_key_name(table, column, prefix: 'fk_rails_')
+ identifier = "#{table}_#{column}_fk"
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
+
+ "#{prefix}#{hashed_identifier}"
+ end
+
# Creates a "foreign key" that references a partitioned table. Because foreign keys referencing partitioned
# tables are not supported in PG11, this does not create a true database foreign key, but instead implements the
# same functionality at the database level by using triggers.