Commit 589ebfcd authored by Jan Provaznik's avatar Jan Provaznik

Background migration to replace blocked_by links

`blocked_by` links will be replaced with `blocks` links
where source and target is swapped.
parent 80a34e2b
---
title: Migrate blocked_by issue links to blocks type by swapping source and target
merge_request: 45262
author:
type: other
# frozen_string_literal: true
class ScheduleBlockedByLinksReplacement < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INTERVAL = 2.minutes
# at the time of writing there were 47600 blocked_by issues:
# estimated time is 48 batches * 2 minutes -> 100 minutes
BATCH_SIZE = 1000
MIGRATION = 'ReplaceBlockedByLinks'
disable_ddl_transaction!
class IssueLink < ActiveRecord::Base
include EachBatch
self.table_name = 'issue_links'
end
def up
relation = IssueLink.where(link_type: 2)
queue_background_migration_jobs_by_range_at_intervals(
relation, MIGRATION, INTERVAL, batch_size: BATCH_SIZE)
end
def down
end
end
e44ab2a7b3014b44d7d84de1f7e618d2fc89f98b8d59f5f6fa331544e206355f
\ No newline at end of file
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
class ReplaceBlockedByLinks
class IssueLink < ActiveRecord::Base
self.table_name = 'issue_links'
end
def perform(start_id, stop_id)
blocked_by_links = IssueLink.where(id: start_id..stop_id).where(link_type: 2)
ActiveRecord::Base.transaction do
# if there is duplicit bi-directional relation (issue2 is blocked by issue1
# and issue1 already links issue2), then we can just delete 'blocked by'.
# This should be rare as we have a pre-create check which checks if issues are
# already linked
blocked_by_links
.joins('INNER JOIN issue_links as opposite_links ON issue_links.source_id = opposite_links.target_id AND issue_links.target_id = opposite_links.source_id')
.where('opposite_links.link_type': 1)
.delete_all
blocked_by_links.update_all('source_id=target_id,target_id=source_id,link_type=1')
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::ReplaceBlockedByLinks, schema: 20201015073808 do
let(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab-org') }
let(:project) { table(:projects).create!(namespace_id: namespace.id, name: 'gitlab') }
let(:issue1) { table(:issues).create!(project_id: project.id, title: 'a') }
let(:issue2) { table(:issues).create!(project_id: project.id, title: 'b') }
let(:issue3) { table(:issues).create!(project_id: project.id, title: 'c') }
let(:issue_links) { table(:issue_links) }
let!(:blocks_link) { issue_links.create!(source_id: issue1.id, target_id: issue2.id, link_type: 1) }
let!(:bidirectional_link) { issue_links.create!(source_id: issue2.id, target_id: issue1.id, link_type: 2) }
let!(:blocked_link) { issue_links.create!(source_id: issue1.id, target_id: issue3.id, link_type: 2) }
subject { described_class.new.perform(issue_links.minimum(:id), issue_links.maximum(:id)) }
it 'deletes issue links where opposite relation already exists' do
expect { subject }.to change { issue_links.count }.by(-1)
end
it 'ignores issue links other than blocked_by' do
subject
expect(blocks_link.reload.link_type).to eq(1)
end
it 'updates blocked_by issue links' do
subject
link = blocked_link.reload
expect(link.link_type).to eq(1)
expect(link.source_id).to eq(issue3.id)
expect(link.target_id).to eq(issue1.id)
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20201015073808_schedule_blocked_by_links_replacement')
RSpec.describe ScheduleBlockedByLinksReplacement do
let(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab-org') }
let(:project) { table(:projects).create!(namespace_id: namespace.id, name: 'gitlab') }
let(:issue1) { table(:issues).create!(project_id: project.id, title: 'a') }
let(:issue2) { table(:issues).create!(project_id: project.id, title: 'b') }
let(:issue3) { table(:issues).create!(project_id: project.id, title: 'c') }
let!(:issue_links) do
[
table(:issue_links).create!(source_id: issue1.id, target_id: issue2.id, link_type: 1),
table(:issue_links).create!(source_id: issue2.id, target_id: issue1.id, link_type: 2),
table(:issue_links).create!(source_id: issue1.id, target_id: issue3.id, link_type: 2)
]
end
before do
stub_const("#{described_class.name}::BATCH_SIZE", 1)
end
it 'schedules jobs for blocked_by links' do
Sidekiq::Testing.fake! do
freeze_time do
migrate!
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(
2.minutes, issue_links[1].id, issue_links[1].id)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(
4.minutes, issue_links[2].id, issue_links[2].id)
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment