1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# frozen_string_literal: true
module Gitlab
module Database
module MigrationHelpers
module AutomaticLockWritesOnTables
extend ActiveSupport::Concern
included do
class_attribute :skip_automatic_lock_on_writes
end
def exec_migration(connection, direction)
return super if %w[main ci].exclude?(Gitlab::Database.db_config_name(connection))
return super if automatic_lock_on_writes_disabled?
# This compares the tables only on the `public` schema. Partitions are not affected
tables = connection.tables
super
new_tables = connection.tables - tables
new_tables.each do |table_name|
lock_writes_on_table(connection, table_name) if should_lock_writes_on_table?(table_name)
end
end
private
def automatic_lock_on_writes_disabled?
# Feature flags are set on the main database, see tables features/feature_gates.
# That is why we switch the ActiveRecord::Base.connection temporarily here back to the 'main' database
# for the cases when the migration is targeting another database, like the 'ci' database.
with_restored_connection_stack do |_|
Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.with_suppressed do
skip_automatic_lock_on_writes ||
Gitlab::Utils.to_boolean(ENV['SKIP_AUTOMATIC_LOCK_ON_WRITES']) ||
Feature.disabled?(:automatic_lock_writes_on_table, type: :ops)
end
end
end
def should_lock_writes_on_table?(table_name)
# currently gitlab_schema represents only present existing tables, this is workaround for deleted tables
# that should be skipped as they will be removed in a future migration.
return false if Gitlab::Database::GitlabSchema.deleted_tables_to_schema[table_name]
table_schema = Gitlab::Database::GitlabSchema.table_schema(table_name.to_s, undefined: false)
if table_schema.nil?
error_message = <<~ERROR
No gitlab_schema is defined for the table #{table_name}. Please consider
adding it to the database dictionary.
More info: https://docs.gitlab.com/ee/development/database/database_dictionary.html
ERROR
raise error_message
end
return false unless %i[gitlab_main gitlab_ci].include?(table_schema)
Gitlab::Database.gitlab_schemas_for_connection(connection).exclude?(table_schema)
end
# with_retries creates new a transaction. So we set it to false if the connection is
# already has an open transaction, to avoid sub-transactions.
def lock_writes_on_table(connection, table_name)
database_name = Gitlab::Database.db_config_name(connection)
LockWritesManager.new(
table_name: table_name,
connection: connection,
database_name: database_name,
with_retries: !connection.transaction_open?,
logger: Logger.new($stdout)
).lock_writes
end
end
end
end
end
|