161 lines
5.1 KiB
Ruby
161 lines
5.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '#perform' do
|
|
subject(:perform) { described_class.new(connection: connection, metrics: metrics_tracker).perform(job_record) }
|
|
|
|
let(:connection) { Gitlab::Database.database_base_models[:main].connection }
|
|
let(:metrics_tracker) { instance_double('::Gitlab::Database::BackgroundMigration::PrometheusMetrics', track: nil) }
|
|
let(:job_class) { Class.new(Gitlab::BackgroundMigration::BatchedMigrationJob) }
|
|
|
|
let_it_be(:pause_ms) { 250 }
|
|
let_it_be(:active_migration) { create(:batched_background_migration, :active, job_arguments: [:id, :other_id]) }
|
|
|
|
let!(:job_record) do
|
|
create(:batched_background_migration_job, batched_migration: active_migration, pause_ms: pause_ms)
|
|
end
|
|
|
|
let(:job_instance) { instance_double('Gitlab::BackgroundMigration::BatchedMigrationJob') }
|
|
|
|
around do |example|
|
|
Gitlab::Database::SharedModel.using_connection(connection) do
|
|
example.run
|
|
end
|
|
end
|
|
|
|
before do
|
|
allow(active_migration).to receive(:job_class).and_return(job_class)
|
|
|
|
allow(job_class).to receive(:new).and_return(job_instance)
|
|
end
|
|
|
|
it 'runs the migration job' do
|
|
expect(job_class).to receive(:new)
|
|
.with(start_id: 1,
|
|
end_id: 10,
|
|
batch_table: 'events',
|
|
batch_column: 'id',
|
|
sub_batch_size: 1,
|
|
pause_ms: pause_ms,
|
|
job_arguments: active_migration.job_arguments,
|
|
connection: connection)
|
|
.and_return(job_instance)
|
|
|
|
expect(job_instance).to receive(:perform).with(no_args)
|
|
|
|
perform
|
|
end
|
|
|
|
it 'updates the tracking record in the database' do
|
|
test_metrics = { 'my_metrics' => 'some value' }
|
|
|
|
expect(job_instance).to receive(:perform).with(no_args)
|
|
expect(job_instance).to receive(:batch_metrics).and_return(test_metrics)
|
|
|
|
freeze_time do
|
|
perform
|
|
|
|
reloaded_job_record = job_record.reload
|
|
|
|
expect(reloaded_job_record).not_to be_pending
|
|
expect(reloaded_job_record.attempts).to eq(1)
|
|
expect(reloaded_job_record.started_at).to eq(Time.current)
|
|
expect(reloaded_job_record.metrics).to eq(test_metrics)
|
|
end
|
|
end
|
|
|
|
context 'when running a job that failed previously' do
|
|
let!(:job_record) do
|
|
create(:batched_background_migration_job, :failed,
|
|
batched_migration: active_migration,
|
|
pause_ms: pause_ms,
|
|
attempts: 1,
|
|
finished_at: 1.hour.ago,
|
|
metrics: { 'my_metrics' => 'some_value' }
|
|
)
|
|
end
|
|
|
|
it 'increments attempts and updates other fields' do
|
|
updated_metrics = { 'updated_metrics' => 'some_value' }
|
|
|
|
expect(job_instance).to receive(:perform).with(no_args)
|
|
expect(job_instance).to receive(:batch_metrics).and_return(updated_metrics)
|
|
|
|
freeze_time do
|
|
perform
|
|
|
|
job_record.reload
|
|
|
|
expect(job_record).not_to be_failed
|
|
expect(job_record.attempts).to eq(2)
|
|
expect(job_record.started_at).to eq(Time.current)
|
|
expect(job_record.finished_at).to eq(Time.current)
|
|
expect(job_record.metrics).to eq(updated_metrics)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when the migration job does not raise an error' do
|
|
it 'marks the tracking record as succeeded' do
|
|
expect(job_instance).to receive(:perform).with(no_args)
|
|
|
|
freeze_time do
|
|
perform
|
|
|
|
reloaded_job_record = job_record.reload
|
|
|
|
expect(reloaded_job_record).to be_succeeded
|
|
expect(reloaded_job_record.finished_at).to eq(Time.current)
|
|
end
|
|
end
|
|
|
|
it 'tracks metrics of the execution' do
|
|
expect(job_instance).to receive(:perform).with(no_args)
|
|
expect(metrics_tracker).to receive(:track).with(job_record)
|
|
|
|
perform
|
|
end
|
|
end
|
|
|
|
context 'when the migration job raises an error' do
|
|
shared_examples 'an error is raised' do |error_class|
|
|
it 'marks the tracking record as failed' do
|
|
expect(job_instance).to receive(:perform).with(no_args).and_raise(error_class)
|
|
|
|
freeze_time do
|
|
expect { perform }.to raise_error(error_class)
|
|
|
|
reloaded_job_record = job_record.reload
|
|
|
|
expect(reloaded_job_record).to be_failed
|
|
expect(reloaded_job_record.finished_at).to eq(Time.current)
|
|
end
|
|
end
|
|
|
|
it 'tracks metrics of the execution' do
|
|
expect(job_instance).to receive(:perform).with(no_args).and_raise(error_class)
|
|
expect(metrics_tracker).to receive(:track).with(job_record)
|
|
|
|
expect { perform }.to raise_error(error_class)
|
|
end
|
|
end
|
|
|
|
it_behaves_like 'an error is raised', RuntimeError.new('Something broke!')
|
|
it_behaves_like 'an error is raised', SignalException.new('SIGTERM')
|
|
it_behaves_like 'an error is raised', ActiveRecord::StatementTimeout.new('Timeout!')
|
|
end
|
|
|
|
context 'when the batched background migration does not inherit from BatchedMigrationJob' do
|
|
let(:job_class) { Class.new }
|
|
let(:job_instance) { job_class.new }
|
|
|
|
it 'runs the job with the correct arguments' do
|
|
expect(job_class).to receive(:new).with(no_args).and_return(job_instance)
|
|
expect(job_instance).to receive(:perform).with(1, 10, 'events', 'id', 1, pause_ms, 'id', 'other_id')
|
|
|
|
perform
|
|
end
|
|
end
|
|
end
|