Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb')
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb227
1 files changed, 148 insertions, 79 deletions
diff --git a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
index 0be55fd2a3e..09ebc495e61 100644
--- a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
+++ b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
@@ -125,10 +125,28 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
context 'when no active migrations exist' do
- it 'does nothing' do
- expect(worker).not_to receive(:run_active_migration)
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
- worker.perform
+ it 'does nothing' do
+ expect(worker).not_to receive(:run_active_migration)
+
+ worker.perform
+ end
+ end
+
+ context 'when parallel execution is enabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
+ end
+
+ it 'does nothing' do
+ expect(worker).not_to receive(:queue_migrations_for_execution)
+
+ worker.perform
+ end
end
end
@@ -136,7 +154,6 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
let(:job_interval) { 5.minutes }
let(:lease_timeout) { 15.minutes }
let(:lease_key) { described_class.name.demodulize.underscore }
- let(:interval_variance) { described_class::INTERVAL_VARIANCE }
let(:migration_id) { 123 }
let(:migration) do
build(
@@ -145,52 +162,86 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
)
end
+ let(:execution_worker_class) do
+ case tracking_database
+ when :main
+ Database::BatchedBackgroundMigration::MainExecutionWorker
+ when :ci
+ Database::BatchedBackgroundMigration::CiExecutionWorker
+ end
+ end
+
before do
allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration)
.with(connection: base_model.connection)
.and_return(migration)
-
- allow(migration).to receive(:interval_elapsed?).with(variance: interval_variance).and_return(true)
- allow(migration).to receive(:reload)
end
- context 'when the calculated timeout is less than the minimum allowed' do
- let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT }
- let(:job_interval) { 2.minutes }
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
+
+ let(:execution_worker) { instance_double(execution_worker_class) }
- it 'sets the lease timeout to the minimum value' do
- expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout)
+ context 'when the calculated timeout is less than the minimum allowed' do
+ let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT }
+ let(:job_interval) { 2.minutes }
- expect_next_instance_of(Database::BatchedBackgroundMigration::ExecutionWorker) do |worker|
- expect(worker).to receive(:perform).with(tracking_database, migration_id)
+ it 'sets the lease timeout to the minimum value' do
+ expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout)
+
+ expect(execution_worker_class).to receive(:new).and_return(execution_worker)
+ expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
+
+ expect(worker).to receive(:run_active_migration).and_call_original
+
+ worker.perform
end
+ end
- expect(worker).to receive(:run_active_migration).and_call_original
+ it 'always cleans up the exclusive lease' do
+ lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+
+ expect(lease).to receive(:try_obtain).and_return(true)
+
+ expect(worker).to receive(:run_active_migration).and_raise(RuntimeError, 'I broke')
+ expect(lease).to receive(:cancel)
+
+ expect { worker.perform }.to raise_error(RuntimeError, 'I broke')
+ end
+
+ it 'delegetes the execution to ExecutionWorker' do
+ base_model = Gitlab::Database.database_base_models[tracking_database]
+
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
+ expect(execution_worker_class).to receive(:new).and_return(execution_worker)
+ expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
worker.perform
end
end
- it 'always cleans up the exclusive lease' do
- lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+ context 'when parallel execution is enabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
+ end
- expect(lease).to receive(:try_obtain).and_return(true)
+ it 'delegetes the execution to ExecutionWorker' do
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration)
+ .to receive(:active_migrations_distinct_on_table).with(
+ connection: base_model.connection,
+ limit: execution_worker_class.max_running_jobs
+ ).and_return([migration])
- expect(worker).to receive(:run_active_migration).and_raise(RuntimeError, 'I broke')
- expect(lease).to receive(:cancel)
+ expected_arguments = [
+ [tracking_database.to_s, migration_id]
+ ]
- expect { worker.perform }.to raise_error(RuntimeError, 'I broke')
- end
+ expect(execution_worker_class).to receive(:perform_with_capacity).with(expected_arguments)
- it 'delegetes the execution to ExecutionWorker' do
- base_model = Gitlab::Database.database_base_models[tracking_database]
-
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
- expect_next_instance_of(Database::BatchedBackgroundMigration::ExecutionWorker) do |worker|
- expect(worker).to receive(:perform).with(tracking_database, migration_id)
+ worker.perform
end
-
- worker.perform
end
end
end
@@ -249,6 +300,8 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: true)
+
# Create example table populated with test data to migrate.
#
# Test data should have two records that won't be updated:
@@ -269,80 +322,96 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
WHERE some_column = #{migration_records - 5};
SQL
- stub_feature_flags(execute_batched_migrations_on_schedule: true)
-
stub_const('Gitlab::BackgroundMigration::ExampleDataMigration', migration_class)
end
- subject(:full_migration_run) do
- # process all batches, then do an extra execution to mark the job as finished
- (number_of_batches + 1).times do
- described_class.new.perform
+ shared_examples 'batched background migration execution' do
+ subject(:full_migration_run) do
+ # process all batches, then do an extra execution to mark the job as finished
+ (number_of_batches + 1).times do
+ described_class.new.perform
- travel_to((migration.interval + described_class::INTERVAL_VARIANCE).seconds.from_now)
+ travel_to((migration.interval + described_class::INTERVAL_VARIANCE).seconds.from_now)
+ end
end
- end
- it 'marks the migration record as finished' do
- expect { full_migration_run }.to change { migration.reload.status }.from(1).to(3) # active -> finished
- end
+ it 'marks the migration record as finished' do
+ expect { full_migration_run }.to change { migration.reload.status }.from(1).to(3) # active -> finished
+ end
- it 'creates job records for each processed batch', :aggregate_failures do
- expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
+ it 'creates job records for each processed batch', :aggregate_failures do
+ expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
- final_min_value = migration.batched_jobs.reduce(1) do |next_min_value, batched_job|
- expect(batched_job.min_value).to eq(next_min_value)
+ final_min_value = migration.batched_jobs.order(id: :asc).reduce(1) do |next_min_value, batched_job|
+ expect(batched_job.min_value).to eq(next_min_value)
- batched_job.max_value + 1
+ batched_job.max_value + 1
+ end
+
+ final_max_value = final_min_value - 1
+ expect(final_max_value).to eq(migration_records)
end
- final_max_value = final_min_value - 1
- expect(final_max_value).to eq(migration_records)
- end
+ it 'marks all job records as succeeded', :aggregate_failures do
+ expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
- it 'marks all job records as succeeded', :aggregate_failures do
- expect { full_migration_run }.to change { migration.reload.batched_jobs.count }.from(0)
+ expect(migration.batched_jobs).to all(be_succeeded)
+ end
- expect(migration.batched_jobs).to all(be_succeeded)
- end
+ it 'updates matching records in the range', :aggregate_failures do
+ expect { full_migration_run }
+ .to change { example_data.where('status = 1 AND some_column <> 0').count }
+ .from(migration_records).to(1)
- it 'updates matching records in the range', :aggregate_failures do
- expect { full_migration_run }
- .to change { example_data.where('status = 1 AND some_column <> 0').count }
- .from(migration_records).to(1)
+ record_outside_range = example_data.last
- record_outside_range = example_data.last
+ expect(record_outside_range.status).to eq(1)
+ expect(record_outside_range.some_column).not_to eq(0)
+ end
- expect(record_outside_range.status).to eq(1)
- expect(record_outside_range.some_column).not_to eq(0)
- end
+ it 'does not update non-matching records in the range' do
+ expect { full_migration_run }.not_to change { example_data.where('status <> 1 AND some_column <> 0').count }
+ end
- it 'does not update non-matching records in the range' do
- expect { full_migration_run }.not_to change { example_data.where('status <> 1 AND some_column <> 0').count }
- end
+ context 'health status' do
+ subject(:migration_run) { described_class.new.perform }
+
+ it 'puts migration on hold when there is autovaccum activity on related tables' do
+ swapout_view_for_table(:postgres_autovacuum_activity, connection: connection)
+ create(
+ :postgres_autovacuum_activity,
+ table: migration.table_name,
+ table_identifier: "public.#{migration.table_name}"
+ )
+
+ expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ end
- context 'health status' do
- subject(:migration_run) { described_class.new.perform }
+ it 'puts migration on hold when the pending WAL count is above the limit' do
+ sql = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::PENDING_WAL_COUNT_SQL
+ limit = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::LIMIT
- it 'puts migration on hold when there is autovaccum activity on related tables' do
- swapout_view_for_table(:postgres_autovacuum_activity, connection: connection)
- create(
- :postgres_autovacuum_activity,
- table: migration.table_name,
- table_identifier: "public.#{migration.table_name}"
- )
+ expect(connection).to receive(:execute).with(sql).and_return([{ 'pending_wal_count' => limit + 1 }])
- expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ end
end
+ end
- it 'puts migration on hold when the pending WAL count is above the limit' do
- sql = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::PENDING_WAL_COUNT_SQL
- limit = Gitlab::Database::BackgroundMigration::HealthStatus::Indicators::WriteAheadLog::LIMIT
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
- expect(connection).to receive(:execute).with(sql).and_return([{ 'pending_wal_count' => limit + 1 }])
+ it_behaves_like 'batched background migration execution'
+ end
- expect { migration_run }.to change { migration.reload.on_hold? }.from(false).to(true)
+ context 'when parallel execution is enabled', :sidekiq_inline do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
end
+
+ it_behaves_like 'batched background migration execution'
end
end
end