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:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-06-20 14:10:13 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-06-20 14:10:13 +0300
commit0ea3fcec397b69815975647f5e2aa5fe944a8486 (patch)
tree7979381b89d26011bcf9bdc989a40fcc2f1ed4ff /lib/gitlab/database
parent72123183a20411a36d607d70b12d57c484394c8e (diff)
Add latest changes from gitlab-org/gitlab@15-1-stable-eev15.1.0-rc42
Diffstat (limited to 'lib/gitlab/database')
-rw-r--r--lib/gitlab/database/background_migration/batched_migration.rb27
-rw-r--r--lib/gitlab/database/background_migration/batched_migration_runner.rb5
-rw-r--r--lib/gitlab/database/batch_count.rb12
-rw-r--r--lib/gitlab/database/batch_counter.rb3
-rw-r--r--lib/gitlab/database/consistency_checker.rb6
-rw-r--r--lib/gitlab/database/gitlab_schema.rb8
-rw-r--r--lib/gitlab/database/gitlab_schemas.yml14
-rw-r--r--lib/gitlab/database/load_balancing/configuration.rb59
-rw-r--r--lib/gitlab/database/load_balancing/connection_proxy.rb1
-rw-r--r--lib/gitlab/database/load_balancing/load_balancer.rb20
-rw-r--r--lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb15
-rw-r--r--lib/gitlab/database/migration.rb1
-rw-r--r--lib/gitlab/database/migration_helpers.rb25
-rw-r--r--lib/gitlab/database/migration_helpers/announce_database.rb23
-rw-r--r--lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb7
-rw-r--r--lib/gitlab/database/migrations/batched_background_migration_helpers.rb83
-rw-r--r--lib/gitlab/database/migrations/reestablished_connection_stack.rb6
-rw-r--r--lib/gitlab/database/partitioning/monthly_strategy.rb4
-rw-r--r--lib/gitlab/database/partitioning/partition_manager.rb13
-rw-r--r--lib/gitlab/database/partitioning/sliding_list_strategy.rb53
-rw-r--r--lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb8
-rw-r--r--lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb2
-rw-r--r--lib/gitlab/database/shared_model.rb9
23 files changed, 290 insertions, 114 deletions
diff --git a/lib/gitlab/database/background_migration/batched_migration.rb b/lib/gitlab/database/background_migration/batched_migration.rb
index a90cae7aea2..d052d5adc4c 100644
--- a/lib/gitlab/database/background_migration/batched_migration.rb
+++ b/lib/gitlab/database/background_migration/batched_migration.rb
@@ -30,9 +30,23 @@ module Gitlab
scope :created_after, ->(time) { where('created_at > ?', time) }
- scope :for_configuration, ->(job_class_name, table_name, column_name, job_arguments) do
- where(job_class_name: job_class_name, table_name: table_name, column_name: column_name)
+ scope :for_configuration, ->(gitlab_schema, job_class_name, table_name, column_name, job_arguments) do
+ relation = where(job_class_name: job_class_name, table_name: table_name, column_name: column_name)
.where("job_arguments = ?", job_arguments.to_json) # rubocop:disable Rails/WhereEquals
+
+ # This method is called from migrations older than the gitlab_schema column,
+ # check and add this filter only if the column exists.
+ relation = relation.for_gitlab_schema(gitlab_schema) if gitlab_schema_column_exists?
+
+ relation
+ end
+
+ def self.gitlab_schema_column_exists?
+ column_names.include?('gitlab_schema')
+ end
+
+ scope :for_gitlab_schema, ->(gitlab_schema) do
+ where(gitlab_schema: gitlab_schema)
end
state_machine :status, initial: :paused do
@@ -73,12 +87,13 @@ module Gitlab
state_machine.states.map(&:name)
end
- def self.find_for_configuration(job_class_name, table_name, column_name, job_arguments)
- for_configuration(job_class_name, table_name, column_name, job_arguments).first
+ def self.find_for_configuration(gitlab_schema, job_class_name, table_name, column_name, job_arguments)
+ for_configuration(gitlab_schema, job_class_name, table_name, column_name, job_arguments).first
end
- def self.active_migration
- executable.queue_order.first
+ def self.active_migration(connection:)
+ for_gitlab_schema(Gitlab::Database.gitlab_schemas_for_connection(connection))
+ .executable.queue_order.first
end
def self.successful_rows_counts(migrations)
diff --git a/lib/gitlab/database/background_migration/batched_migration_runner.rb b/lib/gitlab/database/background_migration/batched_migration_runner.rb
index 59ff9a9744f..388eb596ce2 100644
--- a/lib/gitlab/database/background_migration/batched_migration_runner.rb
+++ b/lib/gitlab/database/background_migration/batched_migration_runner.rb
@@ -54,7 +54,10 @@ module Gitlab
# in order to prevent it being picked up by the background worker. Perform all pending jobs,
# then keep running until migration is finished.
def finalize(job_class_name, table_name, column_name, job_arguments)
- migration = BatchedMigration.find_for_configuration(job_class_name, table_name, column_name, job_arguments)
+ migration = BatchedMigration.find_for_configuration(
+ Gitlab::Database.gitlab_schemas_for_connection(connection),
+ job_class_name, table_name, column_name, job_arguments
+ )
configuration = {
job_class_name: job_class_name,
diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb
index 49f56b5be97..92a41bb36ee 100644
--- a/lib/gitlab/database/batch_count.rb
+++ b/lib/gitlab/database/batch_count.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
# For large tables, PostgreSQL can take a long time to count rows due to MVCC.
-# Implements a distinct and ordinary batch counter
+# Implements:
+# - distinct batch counter
+# - ordinary batch counter
+# - sum batch counter
+# - average batch counter
# Needs indexes on the column below to calculate max, min and range queries
# For larger tables just set use higher batch_size with index optimization
#
@@ -22,6 +26,8 @@
# batch_distinct_count(Project.group(:visibility_level), :creator_id)
# batch_sum(User, :sign_in_count)
# batch_sum(Issue.group(:state_id), :weight))
+# batch_average(Ci::Pipeline, :duration)
+# batch_average(MergeTrain.group(:status), :duration)
module Gitlab
module Database
module BatchCount
@@ -37,6 +43,10 @@ module Gitlab
BatchCounter.new(relation, column: nil, operation: :sum, operation_args: [column]).count(batch_size: batch_size, start: start, finish: finish)
end
+ def batch_average(relation, column, batch_size: nil, start: nil, finish: nil)
+ BatchCounter.new(relation, column: nil, operation: :average, operation_args: [column]).count(batch_size: batch_size, start: start, finish: finish)
+ end
+
class << self
include BatchCount
end
diff --git a/lib/gitlab/database/batch_counter.rb b/lib/gitlab/database/batch_counter.rb
index 417511618e4..522b598cd9d 100644
--- a/lib/gitlab/database/batch_counter.rb
+++ b/lib/gitlab/database/batch_counter.rb
@@ -6,6 +6,7 @@ module Gitlab
FALLBACK = -1
MIN_REQUIRED_BATCH_SIZE = 1_250
DEFAULT_SUM_BATCH_SIZE = 1_000
+ DEFAULT_AVERAGE_BATCH_SIZE = 1_000
MAX_ALLOWED_LOOPS = 10_000
SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep
ALLOWED_MODES = [:itself, :distinct].freeze
@@ -26,6 +27,7 @@ module Gitlab
def unwanted_configuration?(finish, batch_size, start)
(@operation == :count && batch_size <= MIN_REQUIRED_BATCH_SIZE) ||
(@operation == :sum && batch_size < DEFAULT_SUM_BATCH_SIZE) ||
+ (@operation == :average && batch_size < DEFAULT_AVERAGE_BATCH_SIZE) ||
(finish - start) / batch_size >= MAX_ALLOWED_LOOPS ||
start >= finish
end
@@ -92,6 +94,7 @@ module Gitlab
def batch_size_for_mode_and_operation(mode, operation)
return DEFAULT_SUM_BATCH_SIZE if operation == :sum
+ return DEFAULT_AVERAGE_BATCH_SIZE if operation == :average
mode == :distinct ? DEFAULT_DISTINCT_BATCH_SIZE : DEFAULT_BATCH_SIZE
end
diff --git a/lib/gitlab/database/consistency_checker.rb b/lib/gitlab/database/consistency_checker.rb
index e398fef744c..bf60c76a085 100644
--- a/lib/gitlab/database/consistency_checker.rb
+++ b/lib/gitlab/database/consistency_checker.rb
@@ -3,9 +3,9 @@
module Gitlab
module Database
class ConsistencyChecker
- BATCH_SIZE = 1000
- MAX_BATCHES = 25
- MAX_RUNTIME = 30.seconds # must be less than the scheduling frequency of the ConsistencyCheck jobs
+ BATCH_SIZE = 500
+ MAX_BATCHES = 20
+ MAX_RUNTIME = 5.seconds # must be less than the scheduling frequency of the ConsistencyCheck jobs
delegate :monotonic_time, to: :'Gitlab::Metrics::System'
diff --git a/lib/gitlab/database/gitlab_schema.rb b/lib/gitlab/database/gitlab_schema.rb
index d7ea614e2af..baf4cc48424 100644
--- a/lib/gitlab/database/gitlab_schema.rb
+++ b/lib/gitlab/database/gitlab_schema.rb
@@ -75,8 +75,8 @@ module Gitlab
return gitlab_schema
end
- # All tables from `information_schema.` are `:gitlab_shared`
- return :gitlab_shared if schema_name == 'information_schema'
+ # All tables from `information_schema.` are marked as `internal`
+ return :gitlab_internal if schema_name == 'information_schema'
return :gitlab_main if table_name.start_with?('_test_gitlab_main_')
@@ -85,8 +85,8 @@ module Gitlab
# All tables that start with `_test_` without a following schema are shared and ignored
return :gitlab_shared if table_name.start_with?('_test_')
- # All `pg_` tables are marked as `shared`
- return :gitlab_shared if table_name.start_with?('pg_')
+ # All `pg_` tables are marked as `internal`
+ return :gitlab_internal if table_name.start_with?('pg_')
# When undefined it's best to return a unique name so that we don't incorrectly assume that 2 undefined schemas belong on the same database
:"undefined_#{table_name}"
diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml
index 036ce7d7631..71c323cb393 100644
--- a/lib/gitlab/database/gitlab_schemas.yml
+++ b/lib/gitlab/database/gitlab_schemas.yml
@@ -35,10 +35,11 @@ approval_project_rules_users: :gitlab_main
approvals: :gitlab_main
approver_groups: :gitlab_main
approvers: :gitlab_main
-ar_internal_metadata: :gitlab_shared
+ar_internal_metadata: :gitlab_internal
atlassian_identities: :gitlab_main
audit_events_external_audit_event_destinations: :gitlab_main
audit_events: :gitlab_main
+audit_events_streaming_headers: :gitlab_main
authentication_events: :gitlab_main
award_emoji: :gitlab_main
aws_roles: :gitlab_main
@@ -121,6 +122,7 @@ ci_unit_tests: :gitlab_ci
ci_variables: :gitlab_ci
cluster_agents: :gitlab_main
cluster_agent_tokens: :gitlab_main
+cluster_enabled_grants: :gitlab_main
cluster_groups: :gitlab_main
cluster_platforms_kubernetes: :gitlab_main
cluster_projects: :gitlab_main
@@ -217,7 +219,6 @@ geo_event_log: :gitlab_main
geo_events: :gitlab_main
geo_hashed_storage_attachments_events: :gitlab_main
geo_hashed_storage_migrated_events: :gitlab_main
-geo_lfs_object_deleted_events: :gitlab_main
geo_node_namespace_links: :gitlab_main
geo_nodes: :gitlab_main
geo_node_statuses: :gitlab_main
@@ -266,6 +267,7 @@ integrations: :gitlab_main
internal_ids: :gitlab_main
ip_restrictions: :gitlab_main
issuable_metric_images: :gitlab_main
+issuable_resource_links: :gitlab_main
issuable_severities: :gitlab_main
issuable_slas: :gitlab_main
issue_assignees: :gitlab_main
@@ -465,7 +467,7 @@ routes: :gitlab_main
saml_group_links: :gitlab_main
saml_providers: :gitlab_main
saved_replies: :gitlab_main
-schema_migrations: :gitlab_shared
+schema_migrations: :gitlab_internal
scim_identities: :gitlab_main
scim_oauth_access_tokens: :gitlab_main
security_findings: :gitlab_main
@@ -491,6 +493,7 @@ software_license_policies: :gitlab_main
software_licenses: :gitlab_main
spam_logs: :gitlab_main
sprints: :gitlab_main
+ssh_signatures: :gitlab_main
status_check_responses: :gitlab_main
status_page_published_incidents: :gitlab_main
status_page_settings: :gitlab_main
@@ -503,6 +506,7 @@ term_agreements: :gitlab_main
terraform_states: :gitlab_main
terraform_state_versions: :gitlab_main
timelogs: :gitlab_main
+timelog_categories: :gitlab_main
todos: :gitlab_main
token_with_ivs: :gitlab_main
topics: :gitlab_main
@@ -549,6 +553,7 @@ vulnerability_occurrences: :gitlab_main
vulnerability_reads: :gitlab_main
vulnerability_remediations: :gitlab_main
vulnerability_scanners: :gitlab_main
+vulnerability_state_transitions: :gitlab_main
vulnerability_statistics: :gitlab_main
vulnerability_user_mentions: :gitlab_main
webauthn_registrations: :gitlab_main
@@ -556,10 +561,13 @@ web_hook_logs: :gitlab_main
web_hooks: :gitlab_main
wiki_page_meta: :gitlab_main
wiki_page_slugs: :gitlab_main
+work_item_parent_links: :gitlab_main
work_item_types: :gitlab_main
x509_certificates: :gitlab_main
x509_commit_signatures: :gitlab_main
x509_issuers: :gitlab_main
zentao_tracker_data: :gitlab_main
+# dingtalk_tracker_data JiHu-specific, see https://jihulab.com/gitlab-cn/gitlab/-/merge_requests/417
+dingtalk_tracker_data: :gitlab_main
zoom_meetings: :gitlab_main
batched_background_migration_job_transition_logs: :gitlab_shared
diff --git a/lib/gitlab/database/load_balancing/configuration.rb b/lib/gitlab/database/load_balancing/configuration.rb
index 0ddc745ebae..59b08fac7e9 100644
--- a/lib/gitlab/database/load_balancing/configuration.rb
+++ b/lib/gitlab/database/load_balancing/configuration.rb
@@ -41,8 +41,6 @@ module Gitlab
end
end
- config.reuse_primary_connection!
-
config
end
@@ -61,44 +59,17 @@ module Gitlab
disconnect_timeout: 120,
use_tcp: false
}
-
- # Temporary model for GITLAB_LOAD_BALANCING_REUSE_PRIMARY_
- # To be removed with FF
- @primary_model = nil
end
def db_config_name
@model.connection_db_config.name.to_sym
end
- # With connection re-use the primary connection can be overwritten
- # to be used from different model
- def primary_connection_specification_name
- primary_model_or_model_if_enabled.connection_specification_name
- end
-
- def primary_model_or_model_if_enabled
- if use_dedicated_connection?
- @model
- else
- @primary_model || @model
- end
- end
-
- def use_dedicated_connection?
- return true unless @primary_model # We can only use dedicated connection, if re-use of connections is disabled
- return false unless ::Gitlab::SafeRequestStore.active?
-
- ::Gitlab::SafeRequestStore.fetch(:force_no_sharing_primary_model) do
- ::Feature::FlipperFeature.table_exists? && ::Feature.enabled?(:force_no_sharing_primary_model)
- end
- end
-
- def primary_db_config
- primary_model_or_model_if_enabled.connection_db_config
+ def connection_specification_name
+ @model.connection_specification_name
end
- def replica_db_config
+ def db_config
@model.connection_db_config
end
@@ -131,30 +102,6 @@ module Gitlab
service_discovery[:record].present?
end
-
- # TODO: This is temporary code to allow re-use of primary connection
- # if the two connections are pointing to the same host. This is needed
- # to properly support transaction visibility.
- #
- # This behavior is required to support [Phase 3](https://gitlab.com/groups/gitlab-org/-/epics/6160#progress).
- # This method is meant to be removed as soon as it is finished.
- #
- # The remapping is done as-is:
- # export GITLAB_LOAD_BALANCING_REUSE_PRIMARY_<name-of-connection>=<new-name-of-connection>
- #
- # Ex.:
- # export GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=main
- #
- def reuse_primary_connection!
- new_connection = ENV["GITLAB_LOAD_BALANCING_REUSE_PRIMARY_#{db_config_name}"]
- return unless new_connection.present?
-
- @primary_model = Gitlab::Database.database_base_models[new_connection.to_sym]
-
- unless @primary_model
- raise "Invalid value for 'GITLAB_LOAD_BALANCING_REUSE_PRIMARY_#{db_config_name}=#{new_connection}'"
- end
- end
end
end
end
diff --git a/lib/gitlab/database/load_balancing/connection_proxy.rb b/lib/gitlab/database/load_balancing/connection_proxy.rb
index 1be63da8896..8799f8d8af8 100644
--- a/lib/gitlab/database/load_balancing/connection_proxy.rb
+++ b/lib/gitlab/database/load_balancing/connection_proxy.rb
@@ -23,6 +23,7 @@ module Gitlab
insert
update
update_all
+ exec_insert_all
).freeze
NON_STICKY_READS = %i(
diff --git a/lib/gitlab/database/load_balancing/load_balancer.rb b/lib/gitlab/database/load_balancing/load_balancer.rb
index 191ebe18b8a..40b76a1c028 100644
--- a/lib/gitlab/database/load_balancing/load_balancer.rb
+++ b/lib/gitlab/database/load_balancing/load_balancer.rb
@@ -104,12 +104,24 @@ module Gitlab
# Yields a connection that can be used for both reads and writes.
def read_write
connection = nil
+ transaction_open = nil
# In the event of a failover the primary may be briefly unavailable.
# Instead of immediately grinding to a halt we'll retry the operation
# a few times.
retry_with_backoff do
connection = pool.connection
+ transaction_open = connection.transaction_open?
+
yield connection
+ rescue StandardError => e
+ if transaction_open && connection_error?(e)
+ ::Gitlab::Database::LoadBalancing::Logger.warn(
+ event: :transaction_leak,
+ message: 'A write transaction has leaked during database fail-over'
+ )
+ end
+
+ raise e
end
end
@@ -232,14 +244,14 @@ module Gitlab
# host - An optional host name to use instead of the default one.
# port - An optional port to connect to.
def create_replica_connection_pool(pool_size, host = nil, port = nil)
- db_config = @configuration.replica_db_config
+ db_config = @configuration.db_config
env_config = db_config.configuration_hash.dup
env_config[:pool] = pool_size
env_config[:host] = host if host
env_config[:port] = port if port
- replica_db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(
+ db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(
db_config.env_name,
db_config.name + REPLICA_SUFFIX,
env_config
@@ -249,7 +261,7 @@ module Gitlab
# as it will rewrite ActiveRecord::Base.connection
ActiveRecord::ConnectionAdapters::ConnectionHandler
.new
- .establish_connection(replica_db_config)
+ .establish_connection(db_config)
end
# ActiveRecord::ConnectionAdapters::ConnectionHandler handles fetching,
@@ -258,7 +270,7 @@ module Gitlab
# rubocop:disable Database/MultipleDatabases
def pool
ActiveRecord::Base.connection_handler.retrieve_connection_pool(
- @configuration.primary_connection_specification_name,
+ @configuration.connection_specification_name,
role: ActiveRecord::Base.writing_role,
shard: ActiveRecord::Base.default_shard
) || raise(::ActiveRecord::ConnectionNotEstablished)
diff --git a/lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb b/lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb
index 62dfe75a851..13afbd8fd37 100644
--- a/lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb
+++ b/lib/gitlab/database/load_balancing/sidekiq_client_middleware.rb
@@ -39,18 +39,31 @@ module Gitlab
end
job['wal_locations'] = locations
+ job['wal_location_source'] = wal_location_source
+ end
+
+ def wal_location_source
+ if ::Gitlab::Database::LoadBalancing.primary_only? || uses_primary?
+ ::Gitlab::Database::LoadBalancing::ROLE_PRIMARY
+ else
+ ::Gitlab::Database::LoadBalancing::ROLE_REPLICA
+ end
end
def wal_location_for(load_balancer)
# When only using the primary there's no need for any WAL queries.
return if load_balancer.primary_only?
- if ::Gitlab::Database::LoadBalancing::Session.current.use_primary?
+ if uses_primary?
load_balancer.primary_write_location
else
load_balancer.host.database_replica_location
end
end
+
+ def uses_primary?
+ ::Gitlab::Database::LoadBalancing::Session.current.use_primary?
+ end
end
end
end
diff --git a/lib/gitlab/database/migration.rb b/lib/gitlab/database/migration.rb
index 038af570dbc..ab8b6988c3d 100644
--- a/lib/gitlab/database/migration.rb
+++ b/lib/gitlab/database/migration.rb
@@ -37,6 +37,7 @@ module Gitlab
class V1_0 < ActiveRecord::Migration[6.1] # rubocop:disable Naming/ClassAndModuleCamelCase
include LockRetriesConcern
include Gitlab::Database::MigrationHelpers::V2
+ include Gitlab::Database::MigrationHelpers::AnnounceDatabase
# When running migrations, the `db:migrate` switches connection of
# ActiveRecord::Base depending where the migration runs.
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 0453b81d67d..4bb1d71ce18 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -945,8 +945,13 @@ module Gitlab
end
def ensure_batched_background_migration_is_finished(job_class_name:, table_name:, column_name:, job_arguments:, finalize: true)
- migration = Gitlab::Database::BackgroundMigration::BatchedMigration
- .for_configuration(job_class_name, table_name, column_name, job_arguments).first
+ Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.require_dml_mode!
+
+ Gitlab::Database::BackgroundMigration::BatchedMigration.reset_column_information
+ migration = Gitlab::Database::BackgroundMigration::BatchedMigration.find_for_configuration(
+ Gitlab::Database.gitlab_schemas_for_connection(connection),
+ job_class_name, table_name, column_name, job_arguments
+ )
configuration = {
job_class_name: job_class_name,
@@ -966,7 +971,7 @@ module Gitlab
"but it is '#{migration.status_name}':" \
"\t#{configuration}" \
"\n\n" \
- "Finalize it manually by running" \
+ "Finalize it manually by running the following command in a `bash` or `sh` shell:" \
"\n\n" \
"\tsudo gitlab-rake gitlab:background_migrations:finalize[#{job_class_name},#{table_name},#{column_name},'#{job_arguments.to_json.gsub(',', '\,')}']" \
"\n\n" \
@@ -1494,6 +1499,20 @@ into similar problems in the future (e.g. when new tables are created).
SQL
end
+ def drop_sequence(table_name, column_name, sequence_name)
+ execute <<~SQL
+ ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} DROP DEFAULT;
+ DROP SEQUENCE IF EXISTS #{quote_table_name(sequence_name)}
+ SQL
+ end
+
+ def add_sequence(table_name, column_name, sequence_name, start_value)
+ execute <<~SQL
+ CREATE SEQUENCE #{quote_table_name(sequence_name)} START #{start_value};
+ ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT nextval(#{quote(sequence_name)})
+ SQL
+ end
+
private
def create_temporary_columns_and_triggers(table, columns, primary_key: :id, data_type: :bigint)
diff --git a/lib/gitlab/database/migration_helpers/announce_database.rb b/lib/gitlab/database/migration_helpers/announce_database.rb
new file mode 100644
index 00000000000..28710aab717
--- /dev/null
+++ b/lib/gitlab/database/migration_helpers/announce_database.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module MigrationHelpers
+ module AnnounceDatabase
+ extend ActiveSupport::Concern
+
+ def write(text = "")
+ if text.present? # announce/say
+ super("#{db_config_name}: #{text}")
+ else
+ super(text)
+ end
+ end
+
+ def db_config_name
+ @db_config_name ||= Gitlab::Database.db_config_name(connection)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb b/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
index d8d07fcaf2d..b8d1d21a0d2 100644
--- a/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
+++ b/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
@@ -21,7 +21,7 @@ module Gitlab
end
end
- def migrate(direction)
+ def exec_migration(conn, direction)
if unmatched_schemas.any?
migration_skipped
return
@@ -37,8 +37,9 @@ module Gitlab
private
def migration_skipped
- say "Current migration is skipped since it modifies "\
- "'#{self.class.allowed_gitlab_schemas}' which is outside of '#{allowed_schemas_for_connection}'"
+ say "The migration is skipped since it modifies the schemas: #{self.class.allowed_gitlab_schemas}."
+ say "This database can only apply migrations in one of the following schemas: " \
+ "#{allowed_schemas_for_connection}."
end
def validator_class
diff --git a/lib/gitlab/database/migrations/batched_background_migration_helpers.rb b/lib/gitlab/database/migrations/batched_background_migration_helpers.rb
index 7113c3686f1..4aaeaa7b365 100644
--- a/lib/gitlab/database/migrations/batched_background_migration_helpers.rb
+++ b/lib/gitlab/database/migrations/batched_background_migration_helpers.rb
@@ -67,10 +67,22 @@ module Gitlab
batch_class_name: BATCH_CLASS_NAME,
batch_size: BATCH_SIZE,
max_batch_size: nil,
- sub_batch_size: SUB_BATCH_SIZE
+ sub_batch_size: SUB_BATCH_SIZE,
+ gitlab_schema: nil
)
+ Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.require_dml_mode!
- if Gitlab::Database::BackgroundMigration::BatchedMigration.for_configuration(job_class_name, batch_table_name, batch_column_name, job_arguments).exists?
+ if transaction_open?
+ raise 'The `queue_batched_background_migration` cannot be run inside a transaction. ' \
+ 'You can disable transactions by calling `disable_ddl_transaction!` in the body of ' \
+ 'your migration class.'
+ end
+
+ gitlab_schema ||= gitlab_schema_from_context
+
+ Gitlab::Database::BackgroundMigration::BatchedMigration.reset_column_information
+
+ if Gitlab::Database::BackgroundMigration::BatchedMigration.for_configuration(gitlab_schema, job_class_name, batch_table_name, batch_column_name, job_arguments).exists?
Gitlab::AppLogger.warn "Batched background migration not enqueued because it already exists: " \
"job_class_name: #{job_class_name}, table_name: #{batch_table_name}, column_name: #{batch_column_name}, " \
"job_arguments: #{job_arguments.inspect}"
@@ -119,24 +131,77 @@ module Gitlab
end
end
+ if migration.respond_to?(:gitlab_schema)
+ migration.gitlab_schema = gitlab_schema
+ end
+
migration.save!
migration
end
def finalize_batched_background_migration(job_class_name:, table_name:, column_name:, job_arguments:)
- database_name = Gitlab::Database.db_config_name(connection)
+ Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.require_dml_mode!
- unless ActiveRecord::Base.configurations.primary?(database_name)
- raise 'The `#finalize_background_migration` is currently not supported when running in decomposed database, ' \
- 'and this database is not `main:`. For more information visit: ' \
- 'https://docs.gitlab.com/ee/development/database/migrations_for_multiple_databases.html'
+ if transaction_open?
+ raise 'The `finalize_batched_background_migration` cannot be run inside a transaction. ' \
+ 'You can disable transactions by calling `disable_ddl_transaction!` in the body of ' \
+ 'your migration class.'
end
- migration = Gitlab::Database::BackgroundMigration::BatchedMigration.find_for_configuration(job_class_name, table_name, column_name, job_arguments)
+ Gitlab::Database::BackgroundMigration::BatchedMigration.reset_column_information
+
+ migration = Gitlab::Database::BackgroundMigration::BatchedMigration.find_for_configuration(
+ gitlab_schema_from_context, job_class_name, table_name, column_name, job_arguments)
raise 'Could not find batched background migration' if migration.nil?
- Gitlab::Database::BackgroundMigration::BatchedMigrationRunner.finalize(job_class_name, table_name, column_name, job_arguments, connection: connection)
+ with_restored_connection_stack do |restored_connection|
+ Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.with_suppressed do
+ Gitlab::Database::BackgroundMigration::BatchedMigrationRunner.finalize(
+ job_class_name, table_name,
+ column_name, job_arguments,
+ connection: restored_connection)
+ end
+ end
+ end
+
+ # Deletes batched background migration for the given configuration.
+ #
+ # job_class_name - The background migration job class as a string
+ # table_name - The name of the table the migration iterates over
+ # column_name - The name of the column the migration will batch over
+ # job_arguments - Migration arguments
+ #
+ # Example:
+ #
+ # delete_batched_background_migration(
+ # 'CopyColumnUsingBackgroundMigrationJob',
+ # :events,
+ # :id,
+ # ['column1', 'column2'])
+ def delete_batched_background_migration(job_class_name, table_name, column_name, job_arguments)
+ Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.require_dml_mode!
+
+ if transaction_open?
+ raise 'The `#delete_batched_background_migration` cannot be run inside a transaction. ' \
+ 'You can disable transactions by calling `disable_ddl_transaction!` in the body of ' \
+ 'your migration class.'
+ end
+
+ Gitlab::Database::BackgroundMigration::BatchedMigration.reset_column_information
+
+ Gitlab::Database::BackgroundMigration::BatchedMigration
+ .for_configuration(
+ gitlab_schema_from_context, job_class_name, table_name, column_name, job_arguments
+ ).delete_all
+ end
+
+ def gitlab_schema_from_context
+ if respond_to?(:allowed_gitlab_schemas) # Gitlab::Database::Migration::V2_0
+ Array(allowed_gitlab_schemas).first
+ else # Gitlab::Database::Migration::V1_0
+ :gitlab_main
+ end
end
end
end
diff --git a/lib/gitlab/database/migrations/reestablished_connection_stack.rb b/lib/gitlab/database/migrations/reestablished_connection_stack.rb
index d7cf482c32a..addc9d874af 100644
--- a/lib/gitlab/database/migrations/reestablished_connection_stack.rb
+++ b/lib/gitlab/database/migrations/reestablished_connection_stack.rb
@@ -17,7 +17,9 @@ module Gitlab
original_handler = ActiveRecord::Base.connection_handler
original_db_config = ActiveRecord::Base.connection_db_config
- return yield if ActiveRecord::Base.configurations.primary?(original_db_config.name)
+ if ActiveRecord::Base.configurations.primary?(original_db_config.name)
+ return yield(ActiveRecord::Base.connection)
+ end
# If the `ActiveRecord::Base` connection is different than `:main`
# re-establish and configure `SharedModel` context accordingly
@@ -43,7 +45,7 @@ module Gitlab
ActiveRecord::Base.establish_connection :main # rubocop:disable Database/EstablishConnection
Gitlab::Database::SharedModel.using_connection(base_model.connection) do
- yield
+ yield(base_model.connection)
end
ensure
ActiveRecord::Base.connection_handler = original_handler
diff --git a/lib/gitlab/database/partitioning/monthly_strategy.rb b/lib/gitlab/database/partitioning/monthly_strategy.rb
index 9c8cccb3dc6..0f08a47d754 100644
--- a/lib/gitlab/database/partitioning/monthly_strategy.rb
+++ b/lib/gitlab/database/partitioning/monthly_strategy.rb
@@ -40,6 +40,10 @@ module Gitlab
# No-op, required by the partition manager
end
+ def validate_and_fix
+ # No-op, required by the partition manager
+ end
+
private
def desired_partitions
diff --git a/lib/gitlab/database/partitioning/partition_manager.rb b/lib/gitlab/database/partitioning/partition_manager.rb
index 3ee9a193b45..aac91eaadb1 100644
--- a/lib/gitlab/database/partitioning/partition_manager.rb
+++ b/lib/gitlab/database/partitioning/partition_manager.rb
@@ -22,15 +22,13 @@ module Gitlab
connection_name: @connection_name
)
- # Double-checking before getting the lease:
- # The prevailing situation is no missing partitions and no extra partitions
- return if missing_partitions.empty? && extra_partitions.empty?
-
only_with_exclusive_lease(model, lease_key: MANAGEMENT_LEASE_KEY) do
- partitions_to_create = missing_partitions
- create(partitions_to_create) unless partitions_to_create.empty?
+ model.partitioning_strategy.validate_and_fix
+ partitions_to_create = missing_partitions
partitions_to_detach = extra_partitions
+
+ create(partitions_to_create) unless partitions_to_create.empty?
detach(partitions_to_detach) unless partitions_to_detach.empty?
end
rescue StandardError => e
@@ -119,7 +117,8 @@ module Gitlab
parent_table_identifier = "#{connection.current_schema}.#{partition.table}"
if (example_fk = PostgresForeignKey.by_referenced_table_identifier(parent_table_identifier).first)
- raise UnsafeToDetachPartitionError, "Cannot detach #{partition.partition_name}, it would block while checking foreign key #{example_fk.name} on #{example_fk.constrained_table_identifier}"
+ raise UnsafeToDetachPartitionError, "Cannot detach #{partition.partition_name}, it would block while " \
+ "checking foreign key #{example_fk.name} on #{example_fk.constrained_table_identifier}"
end
end
diff --git a/lib/gitlab/database/partitioning/sliding_list_strategy.rb b/lib/gitlab/database/partitioning/sliding_list_strategy.rb
index e9865fb91d6..5cf32d3272c 100644
--- a/lib/gitlab/database/partitioning/sliding_list_strategy.rb
+++ b/lib/gitlab/database/partitioning/sliding_list_strategy.rb
@@ -48,9 +48,12 @@ module Gitlab
default_value = current_default_value
if extra.any? { |p| p.value == default_value }
- Gitlab::AppLogger.error(message: "Inconsistent partition detected: partition with value #{current_default_value} should not be deleted because it's used as the default value.",
- partition_number: current_default_value,
- table_name: model.table_name)
+ Gitlab::AppLogger.error(
+ message: "Inconsistent partition detected: partition with value #{current_default_value} should " \
+ "not be deleted because it's used as the default value.",
+ partition_number: current_default_value,
+ table_name: model.table_name
+ )
extra = extra.reject { |p| p.value == default_value }
end
@@ -73,6 +76,42 @@ module Gitlab
current_partitions.empty?
end
+ def validate_and_fix
+ return unless Feature.enabled?(:fix_sliding_list_partitioning)
+ return if no_partitions_exist?
+
+ old_default_value = current_default_value
+ expected_default_value = active_partition.value
+
+ if old_default_value != expected_default_value
+ with_lock_retries do
+ model.connection.execute("LOCK TABLE #{model.table_name} IN ACCESS EXCLUSIVE MODE")
+
+ old_default_value = current_default_value
+ expected_default_value = active_partition.value
+
+ if old_default_value == expected_default_value
+ Gitlab::AppLogger.warn(
+ message: "Table partitions or partition key default value have been changed by another process",
+ table_name: table_name,
+ default_value: expected_default_value
+ )
+ raise ActiveRecord::Rollback
+ end
+
+ model.connection.change_column_default(model.table_name, partitioning_key, expected_default_value)
+ Gitlab::AppLogger.warn(
+ message: "Fixed default value of sliding_list_strategy partitioning_key",
+ column: partitioning_key,
+ table_name: table_name,
+ connection_name: model.connection.pool.db_config.name,
+ old_value: old_default_value,
+ new_value: expected_default_value
+ )
+ end
+ end
+ end
+
private
def current_default_value
@@ -95,6 +134,14 @@ module Gitlab
raise "Add #{partitioning_key} to #{model.name}.ignored_columns to use it with SlidingListStrategy"
end
end
+
+ def with_lock_retries(&block)
+ Gitlab::Database::WithLockRetries.new(
+ klass: self.class,
+ logger: Gitlab::AppLogger,
+ connection: model.connection
+ ).run(&block)
+ end
end
end
end
diff --git a/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb b/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb
index 391375d472f..06e2b114c91 100644
--- a/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb
+++ b/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb
@@ -27,15 +27,9 @@ module Gitlab
# to reduce amount of labels sort schemas used
gitlab_schemas = gitlab_schemas.to_a.sort.join(",")
- # Temporary feature to observe relation of `gitlab_schemas` to `db_config_name`
- # depending on primary model
- ci_dedicated_primary_connection = ::Ci::ApplicationRecord.connection_class? &&
- ::Ci::ApplicationRecord.load_balancer.configuration.use_dedicated_connection?
-
schemas_metrics.increment({
gitlab_schemas: gitlab_schemas,
- db_config_name: db_config_name,
- ci_dedicated_primary_connection: ci_dedicated_primary_connection
+ db_config_name: db_config_name
})
end
diff --git a/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb b/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
index 3f0176cb654..c51282c9a55 100644
--- a/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
+++ b/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
@@ -9,7 +9,7 @@ module Gitlab
DMLNotAllowedError = Class.new(UnsupportedSchemaError)
DMLAccessDeniedError = Class.new(UnsupportedSchemaError)
- IGNORED_SCHEMAS = %i[gitlab_shared].freeze
+ IGNORED_SCHEMAS = %i[gitlab_shared gitlab_internal].freeze
class << self
def enabled?
diff --git a/lib/gitlab/database/shared_model.rb b/lib/gitlab/database/shared_model.rb
index f4c8fca8fa2..877866b9b23 100644
--- a/lib/gitlab/database/shared_model.rb
+++ b/lib/gitlab/database/shared_model.rb
@@ -20,6 +20,15 @@ module Gitlab
"to '#{Gitlab::Database.db_config_name(connection)}'"
end
+ # connection might not be yet adopted (returning nil, and no gitlab_schemas)
+ # in such cases it is fine to ignore such connections
+ gitlab_schemas = Gitlab::Database.gitlab_schemas_for_connection(connection)
+
+ unless gitlab_schemas.nil? || gitlab_schemas.include?(:gitlab_shared)
+ raise "Cannot set `SharedModel` to connection from `#{Gitlab::Database.db_config_name(connection)}` " \
+ "since this connection does not include `:gitlab_shared` schema."
+ end
+
self.overriding_connection = connection
yield