2019-07-25 05:21:37 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-06-02 15:12:36 +00:00
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-06-24 09:08:32 +00:00
|
|
|
RSpec.describe Gitlab::BackgroundMigration do
|
2021-12-17 03:12:52 +00:00
|
|
|
let(:default_tracking_database) { described_class::DEFAULT_TRACKING_DATABASE }
|
|
|
|
let(:coordinator) { described_class::JobCoordinator.for_tracking_database(default_tracking_database) }
|
2021-11-05 21:10:23 +00:00
|
|
|
|
|
|
|
before do
|
|
|
|
allow(described_class).to receive(:coordinator_for_database)
|
2021-12-17 03:12:52 +00:00
|
|
|
.with(default_tracking_database)
|
2021-11-05 21:10:23 +00:00
|
|
|
.and_return(coordinator)
|
|
|
|
end
|
|
|
|
|
2017-07-11 13:42:00 +00:00
|
|
|
describe '.queue' do
|
|
|
|
it 'returns background migration worker queue' do
|
|
|
|
expect(described_class.queue)
|
|
|
|
.to eq BackgroundMigrationWorker.sidekiq_options['queue']
|
|
|
|
end
|
|
|
|
end
|
2017-06-02 15:12:36 +00:00
|
|
|
|
2017-07-11 13:42:00 +00:00
|
|
|
describe '.steal' do
|
2021-11-05 21:10:23 +00:00
|
|
|
context 'when the queue contains unprocessed jobs' do
|
2017-07-14 13:40:51 +00:00
|
|
|
let(:queue) do
|
2021-08-12 00:10:37 +00:00
|
|
|
[
|
|
|
|
double(args: ['Foo', [10, 20]], klass: 'BackgroundMigrationWorker'),
|
|
|
|
double(args: ['Bar', [20, 30]], klass: 'BackgroundMigrationWorker'),
|
|
|
|
double(args: ['Foo', [20, 30]], klass: 'MergeWorker')
|
|
|
|
]
|
2017-07-14 13:40:51 +00:00
|
|
|
end
|
2017-06-02 15:12:36 +00:00
|
|
|
|
2017-07-11 13:42:00 +00:00
|
|
|
before do
|
|
|
|
allow(Sidekiq::Queue).to receive(:new)
|
2021-11-05 21:10:23 +00:00
|
|
|
.with(coordinator.queue)
|
2017-07-11 13:42:00 +00:00
|
|
|
.and_return(queue)
|
|
|
|
end
|
2017-06-02 15:12:36 +00:00
|
|
|
|
2021-11-09 03:42:22 +00:00
|
|
|
it 'uses the coordinator to steal jobs' do
|
2021-11-05 21:10:23 +00:00
|
|
|
expect(queue[0]).to receive(:delete).and_return(true)
|
2020-06-30 00:09:05 +00:00
|
|
|
|
2021-11-05 21:10:23 +00:00
|
|
|
expect(coordinator).to receive(:steal).with('Foo', retry_dead_jobs: false).and_call_original
|
|
|
|
expect(coordinator).to receive(:perform).with('Foo', [10, 20])
|
2020-06-30 00:09:05 +00:00
|
|
|
|
2021-11-05 21:10:23 +00:00
|
|
|
described_class.steal('Foo')
|
2017-07-14 10:48:11 +00:00
|
|
|
end
|
|
|
|
|
2021-11-05 21:10:23 +00:00
|
|
|
context 'when a custom predicate is given' do
|
|
|
|
it 'steals jobs that match the predicate' do
|
|
|
|
expect(queue[0]).to receive(:delete).and_return(true)
|
2017-07-13 09:44:52 +00:00
|
|
|
|
2021-11-05 21:10:23 +00:00
|
|
|
expect(coordinator).to receive(:perform).with('Foo', [10, 20])
|
2017-07-13 09:44:52 +00:00
|
|
|
|
2021-11-05 21:10:23 +00:00
|
|
|
described_class.steal('Foo') { |job| job.args.second.first == 10 && job.args.second.second == 20 }
|
2017-07-13 09:44:52 +00:00
|
|
|
end
|
2017-07-13 10:00:23 +00:00
|
|
|
|
2021-11-05 21:10:23 +00:00
|
|
|
it 'does not steal jobs that do not match the predicate' do
|
|
|
|
expect(coordinator).not_to receive(:perform)
|
2017-07-13 10:00:23 +00:00
|
|
|
|
2021-11-05 21:10:23 +00:00
|
|
|
expect(queue[0]).not_to receive(:delete)
|
2017-07-13 10:00:23 +00:00
|
|
|
|
2021-11-05 21:10:23 +00:00
|
|
|
described_class.steal('Foo') { |(arg1, _)| arg1 == 5 }
|
2017-07-13 10:00:23 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-01-30 09:03:23 +00:00
|
|
|
|
2020-02-05 21:09:02 +00:00
|
|
|
context 'when retry_dead_jobs is true', :redis do
|
2019-01-30 09:03:23 +00:00
|
|
|
let(:retry_queue) do
|
2021-08-12 00:10:37 +00:00
|
|
|
[double(args: ['Object', [3]], klass: 'BackgroundMigrationWorker', delete: true)]
|
2019-01-30 09:03:23 +00:00
|
|
|
end
|
2020-08-11 03:11:00 +00:00
|
|
|
|
2019-01-30 09:03:23 +00:00
|
|
|
let(:dead_queue) do
|
2021-08-12 00:10:37 +00:00
|
|
|
[double(args: ['Object', [4]], klass: 'BackgroundMigrationWorker', delete: true)]
|
2019-01-30 09:03:23 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(Sidekiq::RetrySet).to receive(:new).and_return(retry_queue)
|
|
|
|
allow(Sidekiq::DeadSet).to receive(:new).and_return(dead_queue)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'steals from the dead and retry queue' do
|
|
|
|
Sidekiq::Testing.disable! do
|
2021-11-05 21:10:23 +00:00
|
|
|
expect(coordinator).to receive(:perform).with('Object', [1]).ordered
|
|
|
|
expect(coordinator).to receive(:perform).with('Object', [2]).ordered
|
|
|
|
expect(coordinator).to receive(:perform).with('Object', [3]).ordered
|
|
|
|
expect(coordinator).to receive(:perform).with('Object', [4]).ordered
|
2019-01-30 09:03:23 +00:00
|
|
|
|
|
|
|
BackgroundMigrationWorker.perform_async('Object', [2])
|
|
|
|
BackgroundMigrationWorker.perform_in(10.minutes, 'Object', [1])
|
|
|
|
|
|
|
|
described_class.steal('Object', retry_dead_jobs: true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-06-02 15:12:36 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
describe '.perform' do
|
2017-07-17 08:16:42 +00:00
|
|
|
let(:migration) { spy(:migration) }
|
2017-06-02 15:12:36 +00:00
|
|
|
|
2017-07-17 08:16:42 +00:00
|
|
|
before do
|
|
|
|
stub_const("#{described_class.name}::Foo", migration)
|
|
|
|
end
|
2017-06-02 15:12:36 +00:00
|
|
|
|
2021-11-09 03:42:22 +00:00
|
|
|
it 'uses the coordinator to perform a background migration' do
|
2021-11-05 21:10:23 +00:00
|
|
|
expect(coordinator).to receive(:perform).with('Foo', [10, 20]).and_call_original
|
2017-07-17 12:02:12 +00:00
|
|
|
expect(migration).to receive(:perform).with(10, 20).once
|
2017-07-17 08:16:42 +00:00
|
|
|
|
2017-07-17 12:02:12 +00:00
|
|
|
described_class.perform('Foo', [10, 20])
|
2017-06-02 15:12:36 +00:00
|
|
|
end
|
2020-02-12 15:09:37 +00:00
|
|
|
end
|
|
|
|
|
2021-08-12 00:10:37 +00:00
|
|
|
describe '.exists?', :redis do
|
2021-11-09 03:42:22 +00:00
|
|
|
before do
|
|
|
|
Sidekiq::Testing.disable! do
|
|
|
|
MergeWorker.perform_async('Bar')
|
|
|
|
BackgroundMigrationWorker.perform_async('Foo')
|
2018-12-19 08:34:34 +00:00
|
|
|
end
|
2021-11-09 03:42:22 +00:00
|
|
|
end
|
2018-12-19 08:34:34 +00:00
|
|
|
|
2021-11-09 03:42:22 +00:00
|
|
|
it 'uses the coordinator to find if a job exists' do
|
|
|
|
expect(coordinator).to receive(:exists?).with('Foo', []).and_call_original
|
2018-12-19 08:34:34 +00:00
|
|
|
|
2021-11-09 03:42:22 +00:00
|
|
|
expect(described_class.exists?('Foo')).to eq(true)
|
|
|
|
end
|
2018-12-19 08:34:34 +00:00
|
|
|
|
2021-11-09 03:42:22 +00:00
|
|
|
it 'uses the coordinator to find a job does not exist' do
|
|
|
|
expect(coordinator).to receive(:exists?).with('Bar', []).and_call_original
|
2019-04-08 22:47:23 +00:00
|
|
|
|
2021-11-09 03:42:22 +00:00
|
|
|
expect(described_class.exists?('Bar')).to eq(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.remaining', :redis do
|
|
|
|
before do
|
|
|
|
Sidekiq::Testing.disable! do
|
|
|
|
MergeWorker.perform_async('Foo')
|
|
|
|
MergeWorker.perform_in(10.minutes, 'Foo')
|
|
|
|
|
|
|
|
5.times do
|
|
|
|
BackgroundMigrationWorker.perform_async('Foo')
|
|
|
|
end
|
|
|
|
3.times do
|
|
|
|
BackgroundMigrationWorker.perform_in(10.minutes, 'Foo')
|
|
|
|
end
|
2019-04-08 22:47:23 +00:00
|
|
|
end
|
|
|
|
end
|
2021-11-09 03:42:22 +00:00
|
|
|
|
|
|
|
it 'uses the coordinator to find the number of remaining jobs' do
|
|
|
|
expect(coordinator).to receive(:remaining).and_call_original
|
|
|
|
|
|
|
|
expect(described_class.remaining).to eq(8)
|
|
|
|
end
|
2019-04-08 22:47:23 +00:00
|
|
|
end
|
2017-06-02 15:12:36 +00:00
|
|
|
end
|