Reset merge status from mergeable MRs

Adds migrations to reset the merge_status of opened,
mergeable MRs. That's required by
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/28513
so we're able to sync the status update along merge-ref,
without leaving MRs with a stale merge-ref.
This commit is contained in:
Oswaldo Ferreira 2019-05-28 18:28:12 -03:00
parent c7e5d046a5
commit f3efec2029
7 changed files with 172 additions and 1 deletions

View File

@ -0,0 +1,5 @@
---
title: Reset merge status from mergeable MRs
merge_request: 28843
author:
type: other

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexToMergeRequestsStateAndMergeStatus < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :merge_requests, [:state, :merge_status],
where: "state = 'opened' AND merge_status = 'can_be_merged'"
end
def down
remove_concurrent_index :merge_requests, [:state, :merge_status]
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class EnqueueResetMergeStatus < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
BATCH_SIZE = 10_000
MIGRATION = 'ResetMergeStatus'
DELAY_INTERVAL = 5.minutes.to_i
disable_ddl_transaction!
def up
say 'Scheduling `ResetMergeStatus` jobs'
# We currently have around 135_000 opened, mergeable MRs in GitLab.com. This iteration
# will schedule around 13 batches of 10_000 MRs, which should take around 1 hour to
# complete.
relation = MergeRequest.where(state: 'opened', merge_status: 'can_be_merged')
relation.each_batch(of: BATCH_SIZE) do |batch, index|
range = batch.pluck('MIN(id)', 'MAX(id)').first
BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, range)
end
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20190527194900) do
ActiveRecord::Schema.define(version: 20190530154715) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -1361,6 +1361,7 @@ ActiveRecord::Schema.define(version: 20190527194900) do
t.index ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
t.index ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_and_branch_state_opened", where: "((state)::text = 'opened'::text)", using: :btree
t.index ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_id_and_source_branch", using: :btree
t.index ["state", "merge_status"], name: "index_merge_requests_on_state_and_merge_status", where: "(((state)::text = 'opened'::text) AND ((merge_status)::text = 'can_be_merged'::text))", using: :btree
t.index ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
t.index ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree
t.index ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid_opened", where: "((state)::text = 'opened'::text)", using: :btree

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Updates the range of given MRs to merge_status "unchecked", if they're opened
# and mergeable.
class ResetMergeStatus
def perform(from_id, to_id)
relation = MergeRequest.where(id: from_id..to_id,
state: 'opened',
merge_status: 'can_be_merged')
relation.update_all(merge_status: 'unchecked')
end
end
end
end

View File

@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'rails_helper'
describe Gitlab::BackgroundMigration::ResetMergeStatus, :migration, schema: 20190528180441 do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
let(:merge_requests) { table(:merge_requests) }
def create_merge_request(id, extra_params = {})
params = {
id: id,
target_project_id: project.id,
target_branch: 'master',
source_project_id: project.id,
source_branch: 'mr name',
title: "mr name#{id}"
}.merge(extra_params)
merge_requests.create!(params)
end
it 'correctly updates opened mergeable MRs to unchecked' do
create_merge_request(1, state: 'opened', merge_status: 'can_be_merged')
create_merge_request(2, state: 'opened', merge_status: 'can_be_merged')
create_merge_request(3, state: 'opened', merge_status: 'can_be_merged')
create_merge_request(4, state: 'merged', merge_status: 'can_be_merged')
create_merge_request(5, state: 'opened', merge_status: 'cannot_be_merged')
subject.perform(1, 5)
expected_rows = [
{ id: 1, state: 'opened', merge_status: 'unchecked' },
{ id: 2, state: 'opened', merge_status: 'unchecked' },
{ id: 3, state: 'opened', merge_status: 'unchecked' },
{ id: 4, state: 'merged', merge_status: 'can_be_merged' },
{ id: 5, state: 'opened', merge_status: 'cannot_be_merged' }
]
rows = merge_requests.order(:id).map do |row|
row.attributes.slice('id', 'state', 'merge_status').symbolize_keys
end
expect(rows).to eq(expected_rows)
end
end

View File

@ -0,0 +1,49 @@
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20190528180441_enqueue_reset_merge_status.rb')
describe EnqueueResetMergeStatus, :migration, :sidekiq do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
let(:merge_requests) { table(:merge_requests) }
def create_merge_request(id, extra_params = {})
params = {
id: id,
target_project_id: project.id,
target_branch: 'master',
source_project_id: project.id,
source_branch: 'mr name',
title: "mr name#{id}"
}.merge(extra_params)
merge_requests.create!(params)
end
it 'correctly schedules background migrations' do
create_merge_request(1, state: 'opened', merge_status: 'can_be_merged')
create_merge_request(2, state: 'opened', merge_status: 'can_be_merged')
create_merge_request(3, state: 'opened', merge_status: 'can_be_merged')
create_merge_request(4, state: 'merged', merge_status: 'can_be_merged')
create_merge_request(5, state: 'opened', merge_status: 'unchecked')
stub_const("#{described_class.name}::BATCH_SIZE", 2)
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(described_class::MIGRATION)
.to be_scheduled_delayed_migration(5.minutes, 1, 2)
expect(described_class::MIGRATION)
.to be_scheduled_delayed_migration(10.minutes, 3, 3)
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
end
end
end
end