Make migrations reschedulable

This commit is contained in:
Felipe Artur 2019-02-18 17:43:16 -03:00
parent d88b44caad
commit cc7a44c8e1
5 changed files with 92 additions and 27 deletions

View File

@ -27,7 +27,7 @@ module Gitlab
private
def should_reschedule?
def need_reschedule?
wait_for_deadtuple_vacuum?(MergeRequestDiffFile.table_name)
end

View File

@ -14,7 +14,7 @@ module Gitlab
extend ActiveSupport::Concern
def reschedule_if_needed(args, &block)
if should_reschedule?
if need_reschedule?
BackgroundMigrationWorker.perform_in(vacuum_wait_time, self.class.name.demodulize, args)
else
yield
@ -22,9 +22,9 @@ module Gitlab
end
# Override this on base class if you need a different reschedule condition
def should_reschedule?
raise NotImplementedError, "#{self.class} does not implement #{__method__}"
end
# def need_reschedule?
# raise NotImplementedError, "#{self.class} does not implement #{__method__}"
# end
def wait_for_deadtuple_vacuum?(table_name)
return false unless Gitlab::Database.postgresql?

View File

@ -4,19 +4,29 @@
module Gitlab
module BackgroundMigration
class SyncIssuesStateId
include Helpers::Reschedulable
def perform(start_id, end_id)
Rails.logger.info("Issues - Populating state_id: #{start_id} - #{end_id}")
ActiveRecord::Base.connection.execute <<~SQL
UPDATE issues
SET state_id =
CASE state
WHEN 'opened' THEN 1
WHEN 'closed' THEN 2
END
WHERE state_id IS NULL
AND id BETWEEN #{start_id} AND #{end_id}
SQL
reschedule_if_needed([start_id, end_id]) do
ActiveRecord::Base.connection.execute <<~SQL
UPDATE issues
SET state_id =
CASE state
WHEN 'opened' THEN 1
WHEN 'closed' THEN 2
END
WHERE state_id IS NULL
AND id BETWEEN #{start_id} AND #{end_id}
SQL
end
end
private
def need_reschedule?
wait_for_deadtuple_vacuum?('issues')
end
end
end

View File

@ -4,21 +4,31 @@
module Gitlab
module BackgroundMigration
class SyncMergeRequestsStateId
include Helpers::Reschedulable
def perform(start_id, end_id)
Rails.logger.info("Merge Requests - Populating state_id: #{start_id} - #{end_id}")
ActiveRecord::Base.connection.execute <<~SQL
UPDATE merge_requests
SET state_id =
CASE state
WHEN 'opened' THEN 1
WHEN 'closed' THEN 2
WHEN 'merged' THEN 3
WHEN 'locked' THEN 4
END
WHERE state_id IS NULL
AND id BETWEEN #{start_id} AND #{end_id}
SQL
reschedule_if_needed([start_id, end_id]) do
ActiveRecord::Base.connection.execute <<~SQL
UPDATE merge_requests
SET state_id =
CASE state
WHEN 'opened' THEN 1
WHEN 'closed' THEN 2
WHEN 'merged' THEN 3
WHEN 'locked' THEN 4
END
WHERE state_id IS NULL
AND id BETWEEN #{start_id} AND #{end_id}
SQL
end
end
private
def need_reschedule?
wait_for_deadtuple_vacuum?('issues')
end
end
end

View File

@ -15,7 +15,40 @@ describe ScheduleSyncIssuablesStateId, :migration do
@project = projects.create!(namespace_id: @group.id)
end
shared_examples 'rescheduling migrations' do
before do
Sidekiq::Worker.clear_all
end
it 'reschedules migrations when should wait for dead tuple vacuum' do
worker = worker_class.new
Sidekiq::Testing.fake! do
allow(worker).to receive(:wait_for_deadtuple_vacuum?) { true }
worker.perform(resource_1.id, resource_2.id)
expect(worker_class.name.demodulize).to be_scheduled_delayed_migration(5.minutes, resource_1.id, resource_2.id)
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
end
end
end
describe '#up' do
# it 'correctly schedules diff file deletion workers' do
# Sidekiq::Testing.fake! do
# Timecop.freeze do
# described_class.new.perform
# expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, [1, 4, 5])
# expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, [6])
# expect(BackgroundMigrationWorker.jobs.size).to eq(2)
# end
# end
# end
context 'issues' do
it 'migrates state column to integer' do
opened_issue = issues.create!(description: 'first', state: 'opened')
@ -30,6 +63,12 @@ describe ScheduleSyncIssuablesStateId, :migration do
expect(invalid_state_issue.reload.state_id).to be_nil
expect(nil_state_issue.reload.state_id).to be_nil
end
it_behaves_like 'rescheduling migrations' do
let(:worker_class) { Gitlab::BackgroundMigration::SyncIssuesStateId }
let(:resource_1) { issues.create!(description: 'first', state: 'opened') }
let(:resource_2) { issues.create!(description: 'second', state: 'closed') }
end
end
context 'merge requests' do
@ -48,6 +87,12 @@ describe ScheduleSyncIssuablesStateId, :migration do
expect(locked_merge_request.reload.state_id).to eq(MergeRequest.available_states[:locked])
expect(invalid_state_merge_request.reload.state_id).to be_nil
end
it_behaves_like 'rescheduling migrations' do
let(:worker_class) { Gitlab::BackgroundMigration::SyncMergeRequestsStateId }
let(:resource_1) { merge_requests.create!(state: 'opened', target_project_id: @project.id, target_branch: 'feature1', source_branch: 'master') }
let(:resource_2) { merge_requests.create!(state: 'closed', target_project_id: @project.id, target_branch: 'feature2', source_branch: 'master') }
end
end
end
end