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/partition_manager.rb')
-rw-r--r--lib/gitlab/database/partitioning/partition_manager.rb64
1 files changed, 62 insertions, 2 deletions
diff --git a/lib/gitlab/database/partitioning/partition_manager.rb b/lib/gitlab/database/partitioning/partition_manager.rb
index 124fae582d3..cc5c49cc24a 100644
--- a/lib/gitlab/database/partitioning/partition_manager.rb
+++ b/lib/gitlab/database/partitioning/partition_manager.rb
@@ -4,9 +4,12 @@ module Gitlab
module Database
module Partitioning
class PartitionManager
+ include ::Gitlab::Utils::StrongMemoize
+
UnsafeToDetachPartitionError = Class.new(StandardError)
- LEASE_TIMEOUT = 1.minute
+ LEASE_TIMEOUT = 1.hour
+ STATEMENT_TIMEOUT = 1.hour
MANAGEMENT_LEASE_KEY = 'database_partition_management_%s'
RETAIN_DETACHED_PARTITIONS_FOR = 1.week
@@ -16,7 +19,7 @@ module Gitlab
@connection_name = @connection.pool.db_config.name
end
- def sync_partitions
+ def sync_partitions(analyze: true)
return skip_synching_partitions unless table_partitioned?
Gitlab::AppLogger.info(
@@ -33,6 +36,8 @@ module Gitlab
create(partitions_to_create) unless partitions_to_create.empty?
detach(partitions_to_detach) unless partitions_to_detach.empty?
+
+ run_analyze_on_partitioned_table if analyze
end
rescue ArgumentError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
@@ -146,6 +151,61 @@ module Gitlab
connection_name: @connection_name
)
end
+
+ def run_analyze_on_partitioned_table
+ return if Feature.disabled?(:database_analyze_on_partitioned_tables)
+ return if ineligible_for_analyzing?
+
+ primary_transaction(statement_timeout: STATEMENT_TIMEOUT) do
+ # Running ANALYZE on partitioned table will go through itself and its partitions
+ connection.execute("ANALYZE VERBOSE #{model.quoted_table_name}")
+ end
+ end
+
+ def ineligible_for_analyzing?
+ analyze_interval.blank? ||
+ first_model_partition.blank? ||
+ last_analyzed_at_within_interval?
+ end
+
+ def last_analyzed_at_within_interval?
+ table_to_query = first_model_partition.identifier
+
+ primary_transaction do
+ # We don't need to get the last_analyze_time from partitioned table,
+ # because it's not supported and always returns NULL for PG version below 14
+ # Therefore, we can always get the last_analyze_time from the first partition
+ last_analyzed_at = connection.select_value(
+ "SELECT pg_stat_get_last_analyze_time('#{table_to_query}'::regclass)"
+ )
+ last_analyzed_at.present? && last_analyzed_at >= Time.current - analyze_interval
+ end
+ end
+
+ def first_model_partition
+ Gitlab::Database::SharedModel.using_connection(connection) do
+ Gitlab::Database::PostgresPartition.for_parent_table(model.table_name).first
+ end
+ end
+ strong_memoize_attr :first_model_partition
+
+ def analyze_interval
+ model.partitioning_strategy.analyze_interval
+ end
+
+ def primary_transaction(statement_timeout: nil)
+ Gitlab::Database::LoadBalancing::Session.current.use_primary do
+ connection.transaction(requires_new: false) do
+ if statement_timeout.present?
+ connection.execute(
+ format("SET LOCAL statement_timeout TO '%ds'", statement_timeout)
+ )
+ end
+
+ yield
+ end
+ end
+ end
end
end
end