Class: Gitlab::Database::LockWritesManager
- Inherits:
-
Object
- Object
- Gitlab::Database::LockWritesManager
- Defined in:
- lib/gitlab/database/lock_writes_manager.rb
Constant Summary collapse
- TRIGGER_FUNCTION_NAME =
'gitlab_schema_prevent_write'- EXPECTED_TRIGGER_RECORD_COUNT =
Triggers to block INSERT / UPDATE / DELETE Triggers on TRUNCATE are not added to the information_schema.triggers See www.postgresql.org/message-id/16934.1568989957%40sss.pgh.pa.us
3
Instance Method Summary collapse
-
#initialize(table_name:, connection:, database_name:, with_retries: true, logger: nil, dry_run: false, force: true) ⇒ LockWritesManager
constructor
table_name can include schema name as a prefix.
- #lock_writes ⇒ Object
- #table_locked_for_writes? ⇒ Boolean
- #unlock_writes ⇒ Object
Constructor Details
#initialize(table_name:, connection:, database_name:, with_retries: true, logger: nil, dry_run: false, force: true) ⇒ LockWritesManager
table_name can include schema name as a prefix. For example: ‘gitlab_partitions_static.events_03’, otherwise, it will default to current used schema, for example ‘public’.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/gitlab/database/lock_writes_manager.rb', line 15 def initialize( table_name:, connection:, database_name:, with_retries: true, logger: nil, dry_run: false, force: true ) @table_name = table_name.to_s @connection = connection @database_name = database_name @logger = logger @dry_run = dry_run @force = force @with_retries = with_retries @table_name_without_schema = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils .extract_schema_qualified_name(table_name.to_s) .identifier end |
Instance Method Details
#lock_writes ⇒ Object
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 |
# File 'lib/gitlab/database/lock_writes_manager.rb', line 42 def lock_writes unless force unless table_exist? logger&.info "Skipping lock_writes, because #{table_name} does not exist" return result_hash(action: 'skipped') end if table_locked_for_writes? logger&.info "Skipping lock_writes, because #{table_name} is already locked for writes" return result_hash(action: 'skipped') end end logger&.info Rainbow("Database: '#{database_name}', Table: '#{table_name}': Lock Writes").yellow sql_statement = <<~SQL CREATE OR REPLACE TRIGGER #{write_trigger_name} BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON #{table_name} FOR EACH STATEMENT EXECUTE FUNCTION #{TRIGGER_FUNCTION_NAME}(); SQL result = process_query(sql_statement, 'lock') result_hash(action: result) rescue ActiveRecord::StatementInvalid => e # Errors like Gitlab::Database::GitlabSchema::UnknownSchemaError, PG::UndefinedTable # are raised under this error logger&.warn "Failed lock_writes, because #{table_name} raised an error. Error: #{e}" result_hash(action: 'skipped') end |
#table_locked_for_writes? ⇒ Boolean
32 33 34 35 36 37 38 39 40 |
# File 'lib/gitlab/database/lock_writes_manager.rb', line 32 def table_locked_for_writes? query = <<~SQL SELECT COUNT(*) from information_schema.triggers WHERE event_object_table = '#{table_name_without_schema}' AND trigger_name = '#{write_trigger_name}' SQL connection.select_value(query) == EXPECTED_TRIGGER_RECORD_COUNT end |
#unlock_writes ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/gitlab/database/lock_writes_manager.rb', line 73 def unlock_writes if force || table_locked_for_writes? logger&.info Rainbow("Database: '#{database_name}', Table: '#{table_name}': Allow Writes").green sql_statement = <<~SQL DROP TRIGGER IF EXISTS #{write_trigger_name} ON #{table_name}; SQL result = process_query(sql_statement, 'unlock') result_hash(action: result) else logger&.info "Skipping unlock_writes, because #{table_name} is already unlocked for writes" result_hash(action: 'skipped') end end |