Module: Gitlab::Database::MigrationHelpers::RequireDisableDdlTransactionForMultipleLocks
- Extended by:
- ActiveSupport::Concern
- Included in:
- Gitlab::Database::Migration::V2_3
- Defined in:
- lib/gitlab/database/migration_helpers/require_disable_ddl_transaction_for_multiple_locks.rb
Overview
This cop detects multiple table locks across different statements in a single migration. This scenario has caused incidents in the past due to deadlocks (for example app.incident.io/gitlab/incidents/202).
Constant Summary collapse
- LOCK_ACQUIRING_COMMANDS =
%w[ALTER CREATE DROP TRUNCATE LOCK UPDATE DELETE INSERT].freeze
- LOCK_TYPES =
{ high_severity: [ 'AccessExclusiveLock', # Conflicts with all lock modes 'ExclusiveLock' # Conflicts with all except ROW SHARE ], low_severity: [ 'RowShareLock', # Conflicts with EXCLUSIVE 'AccessShareLock' # Conflicts with ACCESS EXCLUSIVE only ] }.freeze
Instance Method Summary collapse
Instance Method Details
#exec_migration(connection, direction) ⇒ Object
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 79 |
# File 'lib/gitlab/database/migration_helpers/require_disable_ddl_transaction_for_multiple_locks.rb', line 39 def exec_migration(connection, direction) return super if should_skip_check? # In-memory tracking structures statement_tracking = [] tables_locked_up_till_now = Set.new begin # Subscribe to SQL execution to track each statement subscription_id = ActiveSupport::Notifications.subscribe('sql.active_record') do |*args| event = ActiveSupport::Notifications::Event.new(*args) sql = event.payload[:sql].strip next if should_skip_sql_statement?(sql) newly_locked_tables = [] if likely_to_acquire_locks?(sql) newly_locked_tables = check_current_locks(connection).excluding(tables_locked_up_till_now.to_a) end newly_locked_tables.each { |table| tables_locked_up_till_now.add(table) } # Record new statement current_statement = { number: 1 + statement_tracking.size, sql: sql, locked_tables: newly_locked_tables.uniq } statement_tracking << current_statement end # Run the migration super.tap do # After the migration completes, analyze the collected lock data verify_single_table_per_statement(statement_tracking) end ensure # Cleanup ActiveSupport::Notifications.unsubscribe(subscription_id) if subscription_id end end |