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:
parent
c7e5d046a5
commit
f3efec2029
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Reset merge status from mergeable MRs
|
||||||
|
merge_request: 28843
|
||||||
|
author:
|
||||||
|
type: other
|
|
@ -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
|
|
@ -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
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
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_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_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 ["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_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", 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
|
t.index ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid_opened", where: "((state)::text = 'opened'::text)", using: :btree
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue