debian-mirror-gitlab/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
2023-03-04 22:38:38 +05:30

203 lines
7 KiB
Ruby

# frozen_string_literal: true
RSpec.shared_examples 'batched background migrations execution worker' do
include ExclusiveLeaseHelpers
it 'is a limited capacity worker' do
expect(described_class.new).to be_a(LimitedCapacity::Worker)
end
describe 'defining the job attributes' do
it 'defines the data_consistency as always' do
expect(described_class.get_data_consistency).to eq(:always)
end
it 'defines the feature_category as database' do
expect(described_class.get_feature_category).to eq(:database)
end
it 'defines the idempotency as false' do
expect(described_class).not_to be_idempotent
end
it 'does not retry failed jobs' do
expect(described_class.sidekiq_options['retry']).to eq(0)
end
it 'does not deduplicate jobs' do
expect(described_class.get_deduplicate_strategy).to eq(:none)
end
it 'defines the queue namespace' do
expect(described_class.queue_namespace).to eq('batched_background_migrations')
end
end
describe '.perform_with_capacity' do
it 'enqueues jobs without modifying provided arguments' do
expect_next_instance_of(described_class) do |instance|
expect(instance).to receive(:remove_failed_jobs)
end
args = [['main', 123]]
expect(described_class)
.to receive(:bulk_perform_async)
.with(args)
described_class.perform_with_capacity(args)
end
end
describe '.max_running_jobs' do
it 'returns MAX_RUNNING_MIGRATIONS' do
expect(described_class.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
end
end
describe '#max_running_jobs' do
it 'returns MAX_RUNNING_MIGRATIONS' do
expect(described_class.new.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
end
end
describe '#remaining_work_count' do
it 'returns 0' do
expect(described_class.new.remaining_work_count).to eq(0)
end
end
describe '#perform_work' do
let(:database_name) { Gitlab::Database::MAIN_DATABASE_NAME.to_sym }
let(:base_model) { Gitlab::Database.database_base_models[database_name] }
let(:table_name) { :events }
let(:job_interval) { 5.minutes }
let(:lease_timeout) { job_interval * described_class::LEASE_TIMEOUT_MULTIPLIER }
let(:interval_variance) { described_class::INTERVAL_VARIANCE }
subject(:worker) { described_class.new }
context 'when the feature flag is disabled' do
let(:migration) do
create(:batched_background_migration, :active, interval: job_interval, table_name: table_name)
end
before do
stub_feature_flags(execute_batched_migrations_on_schedule: false)
end
it 'does nothing' do
expect(Gitlab::Database::BackgroundMigration::BatchedMigration).not_to receive(:find_executable)
expect(worker).not_to receive(:run_migration_job)
worker.perform_work(database_name, migration.id)
end
end
context 'when the feature flag is enabled' do
before do
stub_feature_flags(execute_batched_migrations_on_schedule: true)
end
context 'when the provided database is sharing config' do
before do
skip_if_multiple_databases_not_setup
end
it 'does nothing' do
ci_model = Gitlab::Database.database_base_models['ci']
expect(Gitlab::Database).to receive(:db_config_share_with)
.with(ci_model.connection_db_config).and_return('main')
expect(Gitlab::Database::BackgroundMigration::BatchedMigration).not_to receive(:find_executable)
expect(worker).not_to receive(:run_migration_job)
worker.perform_work(:ci, 123)
end
end
context 'when migration does not exist' do
it 'does nothing' do
expect(worker).not_to receive(:run_migration_job)
worker.perform_work(database_name, non_existing_record_id)
end
end
context 'when migration exist' do
let(:migration) do
create(:batched_background_migration, :active, interval: job_interval, table_name: table_name)
end
before do
allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:find_executable)
.with(migration.id, connection: base_model.connection)
.and_return(migration)
end
context 'when the migration is no longer active' do
it 'does not run the migration' do
expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
expect(migration).to receive(:active?).and_return(false)
expect(worker).not_to receive(:run_migration_job)
worker.perform_work(database_name, migration.id)
end
end
context 'when the interval has not elapsed' do
it 'does not run the migration' do
expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
expect(migration).to receive(:interval_elapsed?).with(variance: interval_variance).and_return(false)
expect(worker).not_to receive(:run_migration_job)
worker.perform_work(database_name, migration.id)
end
end
context 'when the migration is still active and the interval has elapsed' do
let(:table_name_lease_key) do
"#{described_class.name.underscore}:database_name:#{database_name}:" \
"table_name:#{table_name}"
end
context 'when can not obtain lease on the table name' do
it 'does nothing' do
stub_exclusive_lease_taken(table_name_lease_key, timeout: lease_timeout)
expect(worker).not_to receive(:run_migration_job)
worker.perform_work(database_name, migration.id)
end
end
it 'always cleans up the exclusive lease' do
expect_to_obtain_exclusive_lease(table_name_lease_key, 'uuid-table-name', timeout: lease_timeout)
expect_to_cancel_exclusive_lease(table_name_lease_key, 'uuid-table-name')
expect(worker).to receive(:run_migration_job).and_raise(RuntimeError, 'I broke')
expect { worker.perform_work(database_name, migration.id) }.to raise_error(RuntimeError, 'I broke')
end
it 'runs the migration' do
expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |instance|
expect(instance).to receive(:run_migration_job).with(migration)
end
expect_to_obtain_exclusive_lease(table_name_lease_key, 'uuid-table-name', timeout: lease_timeout)
expect_to_cancel_exclusive_lease(table_name_lease_key, 'uuid-table-name')
expect(worker).to receive(:run_migration_job).and_call_original
worker.perform_work(database_name, migration.id)
end
end
end
end
end
end