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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Database::LockTablesWorker, feature_category: :cell do
using RSpec::Parameterized::TableSyntax
let(:worker) { described_class.new }
let(:exception_class) { described_class::TableShouldNotBeLocked }
describe '#perform' do
context 'when running with single database' do # this covers both single-db and single-db-ci-connection cases
before do
skip_if_database_exists(:ci)
end
it 'skips executing the job' do
expect do
worker.perform('ci', %w[ci_pipelines])
end.to raise_error(exception_class, 'GitLab is not running in multiple database mode')
end
end
context 'when running in decomposed database' do
before do
skip_if_shared_database(:ci)
end
context 'when the table is wrong' do
context 'when trying to lock tables on an unknown database' do
it 'raises an exception' do
expect do
worker.perform('foobar', %w[ci_pipelines])
end.to raise_error(exception_class, /does not support locking writes on tables/)
end
end
context 'when trying to lock tables on the database that does not support locking' do
it 'raises an exception' do
expect do
worker.perform('geo', %w[ci_pipelines]) # ci tables should be locked only on main
end.to raise_error(exception_class, /does not support locking writes on tables/)
end
end
context 'when trying to lock tables on the wrong database' do
it 'raises an exception' do
expect do
worker.perform('ci', %w[ci_pipelines]) # ci tables should be locked only on main
end.to raise_error(exception_class, "table 'ci_pipelines' should not be locked on the database 'ci'")
end
end
context 'when trying to lock shared tables on the database' do
it 'raises an exception' do
expect do
worker.perform('main', %w[loose_foreign_keys_deleted_records])
end.to raise_error(exception_class, /should not be locked on the database 'main'/)
end
end
end
context 'when the table is correct' do
context 'when the table is not locked for writes' do
where(:database_name, :tables) do
:ci | %w[users namespaces]
:main | %w[ci_pipelines ci_builds]
end
with_them do
it 'locks the tables on the corresponding database' do
tables.each do |table_name|
unlock_table(database_name, table_name)
expect(lock_writes_manager(database_name, table_name).table_locked_for_writes?).to eq(false)
end
expected_log_results = tables.map do |table_name|
{ action: "locked", database: database_name, dry_run: false, table: table_name }
end
expect(worker).to receive(:log_extra_metadata_on_done).with(:performed_actions, expected_log_results)
worker.perform(database_name, tables)
tables.each do |table_name|
expect(lock_writes_manager(database_name, table_name).table_locked_for_writes?).to eq(true)
end
end
end
context 'when the table is already locked for writes' do
where(:database_name, :tables) do
:ci | %w[users namespaces]
:main | %w[ci_pipelines ci_builds]
end
with_them do
it 'skips locking the tables on the corresponding database' do
tables.each do |table_name|
lock_table(database_name, table_name)
end
expected_log_results = tables.map do |table_name|
{ action: 'skipped', database: database_name, dry_run: false, table: table_name }
end
expect(worker).to receive(:log_extra_metadata_on_done).with(:performed_actions, expected_log_results)
worker.perform(database_name, tables)
tables.each do |table_name|
expect(lock_writes_manager(database_name, table_name).table_locked_for_writes?).to eq(true)
end
end
end
end
end
end
end
end
def lock_table(database_name, table_name)
lock_writes_manager(database_name, table_name).lock_writes
end
def unlock_table(database_name, table_name)
lock_writes_manager(database_name, table_name).unlock_writes
end
def lock_writes_manager(database_name, table_name)
connection = Gitlab::Database.database_base_models_with_gitlab_shared[database_name].connection
Gitlab::Database::LockWritesManager.new(
table_name: table_name,
connection: connection,
database_name: database_name,
with_retries: false,
dry_run: false
)
end
end
|