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')
-rw-r--r--lib/gitlab/database/partitioning/detached_partition_dropper.rb96
-rw-r--r--lib/gitlab/database/partitioning/monthly_strategy.rb4
-rw-r--r--lib/gitlab/database/partitioning/multi_database_partition_dropper.rb35
-rw-r--r--lib/gitlab/database/partitioning/multi_database_partition_manager.rb37
-rw-r--r--lib/gitlab/database/partitioning/partition_monitoring.rb18
-rw-r--r--lib/gitlab/database/partitioning/replace_table.rb7
-rw-r--r--lib/gitlab/database/partitioning/time_partition.rb2
7 files changed, 90 insertions, 109 deletions
diff --git a/lib/gitlab/database/partitioning/detached_partition_dropper.rb b/lib/gitlab/database/partitioning/detached_partition_dropper.rb
index 3e7ddece20b..593824384b5 100644
--- a/lib/gitlab/database/partitioning/detached_partition_dropper.rb
+++ b/lib/gitlab/database/partitioning/detached_partition_dropper.rb
@@ -9,13 +9,10 @@ module Gitlab
Gitlab::AppLogger.info(message: "Checking for previously detached partitions to drop")
Postgresql::DetachedPartition.ready_to_drop.find_each do |detached_partition|
- connection.transaction do
- # Another process may have already dropped the table and deleted this entry
- next unless (detached_partition = Postgresql::DetachedPartition.lock.find_by(id: detached_partition.id))
-
- drop_detached_partition(detached_partition.table_name)
-
- detached_partition.destroy!
+ if partition_attached?(qualify_partition_name(detached_partition.table_name))
+ unmark_partition(detached_partition)
+ else
+ drop_partition(detached_partition)
end
rescue StandardError => e
Gitlab::AppLogger.error(message: "Failed to drop previously detached partition",
@@ -27,31 +24,100 @@ module Gitlab
private
+ def unmark_partition(detached_partition)
+ connection.transaction do
+ # Another process may have already encountered this case and deleted this entry
+ next unless try_lock_detached_partition(detached_partition.id)
+
+ # The current partition was scheduled for deletion incorrectly
+ # Dropping it now could delete in-use data and take locks that interrupt other database activity
+ Gitlab::AppLogger.error(message: "Prevented an attempt to drop an attached database partition", partition_name: detached_partition.table_name)
+ detached_partition.destroy!
+ end
+ end
+
+ def drop_partition(detached_partition)
+ remove_foreign_keys(detached_partition)
+
+ connection.transaction do
+ # Another process may have already dropped the table and deleted this entry
+ next unless try_lock_detached_partition(detached_partition.id)
+
+ drop_detached_partition(detached_partition.table_name)
+
+ detached_partition.destroy!
+ end
+ end
+
+ def remove_foreign_keys(detached_partition)
+ partition_identifier = qualify_partition_name(detached_partition.table_name)
+
+ # We want to load all of these into memory at once to get a consistent view to loop over,
+ # since we'll be deleting from this list as we go
+ fks_to_drop = PostgresForeignKey.by_constrained_table_identifier(partition_identifier).to_a
+ fks_to_drop.each do |foreign_key|
+ drop_foreign_key_if_present(detached_partition, foreign_key)
+ end
+ end
+
+ # Drops the given foreign key for the given detached partition, but only if another process has not already
+ # detached the partition first. This method must be safe to call even if the associated partition table has already
+ # been detached, as it could be called by multiple processes at once.
+ def drop_foreign_key_if_present(detached_partition, foreign_key)
+ # It is important to only drop one foreign key per transaction.
+ # Dropping a foreign key takes an ACCESS EXCLUSIVE lock on both tables participating in the foreign key.
+
+ partition_identifier = qualify_partition_name(detached_partition.table_name)
+ with_lock_retries do
+ connection.transaction(requires_new: false) do
+ next unless try_lock_detached_partition(detached_partition.id)
+
+ # Another process may have already dropped this foreign key
+ next unless PostgresForeignKey.by_constrained_table_identifier(partition_identifier).where(name: foreign_key.name).exists?
+
+ connection.execute("ALTER TABLE #{connection.quote_table_name(partition_identifier)} DROP CONSTRAINT #{connection.quote_table_name(foreign_key.name)}")
+
+ Gitlab::AppLogger.info(message: "Dropped foreign key for previously detached partition",
+ partition_name: detached_partition.table_name,
+ referenced_table_name: foreign_key.referenced_table_identifier,
+ foreign_key_name: foreign_key.name)
+ end
+ end
+ end
+
def drop_detached_partition(partition_name)
partition_identifier = qualify_partition_name(partition_name)
- if partition_detached?(partition_identifier)
- connection.drop_table(partition_identifier, if_exists: true)
+ connection.drop_table(partition_identifier, if_exists: true)
- Gitlab::AppLogger.info(message: "Dropped previously detached partition", partition_name: partition_name)
- else
- Gitlab::AppLogger.error(message: "Attempt to drop attached database partition", partition_name: partition_name)
- end
+ Gitlab::AppLogger.info(message: "Dropped previously detached partition", partition_name: partition_name)
end
def qualify_partition_name(table_name)
"#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{table_name}"
end
- def partition_detached?(partition_identifier)
+ def partition_attached?(partition_identifier)
# PostgresPartition checks the pg_inherits view, so our partition will only show here if it's still attached
# and thus should not be dropped
- !Gitlab::Database::PostgresPartition.for_identifier(partition_identifier).exists?
+ Gitlab::Database::PostgresPartition.for_identifier(partition_identifier).exists?
+ end
+
+ def try_lock_detached_partition(id)
+ Postgresql::DetachedPartition.lock.find_by(id: id).present?
end
def connection
Postgresql::DetachedPartition.connection
end
+
+ def with_lock_retries(&block)
+ Gitlab::Database::WithLockRetries.new(
+ klass: self.class,
+ logger: Gitlab::AppLogger,
+ connection: connection
+ ).run(raise_on_exhaustion: true, &block)
+ end
end
end
end
diff --git a/lib/gitlab/database/partitioning/monthly_strategy.rb b/lib/gitlab/database/partitioning/monthly_strategy.rb
index 4cdde5bf2f1..c93e775d7ed 100644
--- a/lib/gitlab/database/partitioning/monthly_strategy.rb
+++ b/lib/gitlab/database/partitioning/monthly_strategy.rb
@@ -96,10 +96,6 @@ module Gitlab
def oldest_active_date
(Date.today - retain_for).beginning_of_month
end
-
- def connection
- ActiveRecord::Base.connection
- end
end
end
end
diff --git a/lib/gitlab/database/partitioning/multi_database_partition_dropper.rb b/lib/gitlab/database/partitioning/multi_database_partition_dropper.rb
deleted file mode 100644
index 769b658bae4..00000000000
--- a/lib/gitlab/database/partitioning/multi_database_partition_dropper.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Database
- module Partitioning
- class MultiDatabasePartitionDropper
- def drop_detached_partitions
- Gitlab::AppLogger.info(message: "Dropping detached postgres partitions")
-
- each_database_connection do |name, connection|
- Gitlab::Database::SharedModel.using_connection(connection) do
- Gitlab::AppLogger.debug(message: "Switched database connection", connection_name: name)
-
- DetachedPartitionDropper.new.perform
- end
- end
-
- Gitlab::AppLogger.info(message: "Finished dropping detached postgres partitions")
- end
-
- private
-
- def each_database_connection
- databases.each_pair do |name, connection_wrapper|
- yield name, connection_wrapper.scope.connection
- end
- end
-
- def databases
- Gitlab::Database.databases
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/database/partitioning/multi_database_partition_manager.rb b/lib/gitlab/database/partitioning/multi_database_partition_manager.rb
deleted file mode 100644
index 5a93e3fb1fb..00000000000
--- a/lib/gitlab/database/partitioning/multi_database_partition_manager.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Database
- module Partitioning
- class MultiDatabasePartitionManager
- def initialize(models)
- @models = models
- end
-
- def sync_partitions
- Gitlab::AppLogger.info(message: "Syncing dynamic postgres partitions")
-
- models.each do |model|
- Gitlab::Database::SharedModel.using_connection(model.connection) do
- Gitlab::AppLogger.debug(message: "Switched database connection",
- connection_name: connection_name,
- table_name: model.table_name)
-
- PartitionManager.new(model).sync_partitions
- end
- end
-
- Gitlab::AppLogger.info(message: "Finished sync of dynamic postgres partitions")
- end
-
- private
-
- attr_reader :models
-
- def connection_name
- Gitlab::Database::SharedModel.connection.pool.db_config.name
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/database/partitioning/partition_monitoring.rb b/lib/gitlab/database/partitioning/partition_monitoring.rb
index e5b561fc447..1a23f58285d 100644
--- a/lib/gitlab/database/partitioning/partition_monitoring.rb
+++ b/lib/gitlab/database/partitioning/partition_monitoring.rb
@@ -4,20 +4,12 @@ module Gitlab
module Database
module Partitioning
class PartitionMonitoring
- attr_reader :models
+ def report_metrics_for_model(model)
+ strategy = model.partitioning_strategy
- def initialize(models = Gitlab::Database::Partitioning.registered_models)
- @models = models
- end
-
- def report_metrics
- models.each do |model|
- strategy = model.partitioning_strategy
-
- gauge_present.set({ table: model.table_name }, strategy.current_partitions.size)
- gauge_missing.set({ table: model.table_name }, strategy.missing_partitions.size)
- gauge_extra.set({ table: model.table_name }, strategy.extra_partitions.size)
- end
+ gauge_present.set({ table: model.table_name }, strategy.current_partitions.size)
+ gauge_missing.set({ table: model.table_name }, strategy.missing_partitions.size)
+ gauge_extra.set({ table: model.table_name }, strategy.extra_partitions.size)
end
private
diff --git a/lib/gitlab/database/partitioning/replace_table.rb b/lib/gitlab/database/partitioning/replace_table.rb
index 6f6af223fa2..a7686e97553 100644
--- a/lib/gitlab/database/partitioning/replace_table.rb
+++ b/lib/gitlab/database/partitioning/replace_table.rb
@@ -9,7 +9,8 @@ module Gitlab
attr_reader :original_table, :replacement_table, :replaced_table, :primary_key_column,
:sequence, :original_primary_key, :replacement_primary_key, :replaced_primary_key
- def initialize(original_table, replacement_table, replaced_table, primary_key_column)
+ def initialize(connection, original_table, replacement_table, replaced_table, primary_key_column)
+ @connection = connection
@original_table = original_table
@replacement_table = replacement_table
@replaced_table = replaced_table
@@ -29,10 +30,8 @@ module Gitlab
private
+ attr_reader :connection
delegate :execute, :quote_table_name, :quote_column_name, to: :connection
- def connection
- @connection ||= ActiveRecord::Base.connection
- end
def default_sequence(table, column)
"#{table}_#{column}_seq"
diff --git a/lib/gitlab/database/partitioning/time_partition.rb b/lib/gitlab/database/partitioning/time_partition.rb
index e09ca483549..649687bdd12 100644
--- a/lib/gitlab/database/partitioning/time_partition.rb
+++ b/lib/gitlab/database/partitioning/time_partition.rb
@@ -87,7 +87,7 @@ module Gitlab
end
def conn
- @conn ||= ActiveRecord::Base.connection
+ @conn ||= Gitlab::Database::SharedModel.connection
end
end
end