summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2017-10-26 11:32:47 +0100
committerSean McGivern <sean@gitlab.com>2017-11-02 16:09:56 +0000
commitd8299e320ec8c93311f678ed4dddab66528d021e (patch)
treed48d32e262694667d7cb4e672601b6a4d73d383a
parentbfb5107ae720232a15060ee55feba213ee7dd097 (diff)
downloadgitlab-ce-d8299e320ec8c93311f678ed4dddab66528d021e.tar.gz
Add a column linking an MR to its diff
We already had this the other way around (merge_request_diffs.merge_request_id), but this is needed to gather only the most recent diffs for a set of merge requests.
-rw-r--r--changelogs/unreleased/37631-add-a-merge_request_diff_id-column-to-merge_requests.yml5
-rw-r--r--db/migrate/20171025110159_add_latest_merge_request_diff_id_to_merge_requests.rb26
-rw-r--r--db/post_migrate/20171026082505_populate_merge_requests_latest_merge_request_diff_id.rb27
-rw-r--r--db/schema.rb5
-rw-r--r--lib/gitlab/import_export/import_export.yml1
-rw-r--r--spec/migrations/populate_merge_requests_latest_merge_request_diff_id_spec.rb61
6 files changed, 124 insertions, 1 deletions
diff --git a/changelogs/unreleased/37631-add-a-merge_request_diff_id-column-to-merge_requests.yml b/changelogs/unreleased/37631-add-a-merge_request_diff_id-column-to-merge_requests.yml
new file mode 100644
index 00000000000..1a8632f7450
--- /dev/null
+++ b/changelogs/unreleased/37631-add-a-merge_request_diff_id-column-to-merge_requests.yml
@@ -0,0 +1,5 @@
+---
+title: Make finding most recent merge request diffs more efficient
+merge_request: 15035
+author:
+type: performance
diff --git a/db/migrate/20171025110159_add_latest_merge_request_diff_id_to_merge_requests.rb b/db/migrate/20171025110159_add_latest_merge_request_diff_id_to_merge_requests.rb
new file mode 100644
index 00000000000..74a2badc130
--- /dev/null
+++ b/db/migrate/20171025110159_add_latest_merge_request_diff_id_to_merge_requests.rb
@@ -0,0 +1,26 @@
+class AddLatestMergeRequestDiffIdToMergeRequests < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column :merge_requests, :latest_merge_request_diff_id, :integer
+ add_concurrent_index :merge_requests, :latest_merge_request_diff_id
+
+ add_concurrent_foreign_key :merge_requests, :merge_request_diffs,
+ column: :latest_merge_request_diff_id,
+ on_delete: :nullify
+ end
+
+ def down
+ remove_foreign_key :merge_requests, column: :latest_merge_request_diff_id
+
+ if index_exists?(:merge_requests, :latest_merge_request_diff_id)
+ remove_concurrent_index :merge_requests, :latest_merge_request_diff_id
+ end
+
+ remove_column :merge_requests, :latest_merge_request_diff_id
+ end
+end
diff --git a/db/post_migrate/20171026082505_populate_merge_requests_latest_merge_request_diff_id.rb b/db/post_migrate/20171026082505_populate_merge_requests_latest_merge_request_diff_id.rb
new file mode 100644
index 00000000000..a7ebbbf34c0
--- /dev/null
+++ b/db/post_migrate/20171026082505_populate_merge_requests_latest_merge_request_diff_id.rb
@@ -0,0 +1,27 @@
+class PopulateMergeRequestsLatestMergeRequestDiffId < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ BATCH_SIZE = 1_000
+
+ class MergeRequest < ActiveRecord::Base
+ self.table_name = 'merge_requests'
+
+ include ::EachBatch
+ end
+
+ disable_ddl_transaction!
+
+ def up
+ update = '
+ latest_merge_request_diff_id = (
+ SELECT MAX(id)
+ FROM merge_request_diffs
+ WHERE merge_requests.id = merge_request_diffs.merge_request_id
+ )'.squish
+
+ MergeRequest.where(latest_merge_request_diff_id: nil).each_batch(of: BATCH_SIZE) do |relation|
+ relation.update_all(update)
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 530f08022be..6e23f17d393 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20171017145932) do
+ActiveRecord::Schema.define(version: 20171026082505) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -973,6 +973,7 @@ ActiveRecord::Schema.define(version: 20171017145932) do
t.boolean "ref_fetched"
t.string "merge_jid"
t.boolean "discussion_locked"
+ t.integer "latest_merge_request_diff_id"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
@@ -981,6 +982,7 @@ ActiveRecord::Schema.define(version: 20171017145932) do
add_index "merge_requests", ["deleted_at"], name: "index_merge_requests_on_deleted_at", using: :btree
add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
add_index "merge_requests", ["head_pipeline_id"], name: "index_merge_requests_on_head_pipeline_id", using: :btree
+ add_index "merge_requests", ["latest_merge_request_diff_id"], name: "index_merge_requests_on_latest_merge_request_diff_id", using: :btree
add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
add_index "merge_requests", ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_id_and_source_branch", using: :btree
@@ -1846,6 +1848,7 @@ ActiveRecord::Schema.define(version: 20171017145932) do
add_foreign_key "merge_request_metrics", "ci_pipelines", column: "pipeline_id", on_delete: :cascade
add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade
add_foreign_key "merge_requests", "ci_pipelines", column: "head_pipeline_id", name: "fk_fd82eae0b9", on_delete: :nullify
+ add_foreign_key "merge_requests", "merge_request_diffs", column: "latest_merge_request_diff_id", name: "fk_06067f5644", on_delete: :nullify
add_foreign_key "merge_requests", "projects", column: "target_project_id", name: "fk_a6963e8447", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index dec8b4c5acd..57678ffdd2a 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -113,6 +113,7 @@ excluded_attributes:
- :milestone_id
- :ref_fetched
- :merge_jid
+ - :merge_request_diff_id
award_emoji:
- :awardable_id
statuses:
diff --git a/spec/migrations/populate_merge_requests_latest_merge_request_diff_id_spec.rb b/spec/migrations/populate_merge_requests_latest_merge_request_diff_id_spec.rb
new file mode 100644
index 00000000000..4ea7f441f7c
--- /dev/null
+++ b/spec/migrations/populate_merge_requests_latest_merge_request_diff_id_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20171026082505_populate_merge_requests_latest_merge_request_diff_id')
+
+describe PopulateMergeRequestsLatestMergeRequestDiffId, :migration do
+ let(:projects_table) { table(:projects) }
+ let(:merge_requests_table) { table(:merge_requests) }
+ let(:merge_request_diffs_table) { table(:merge_request_diffs) }
+
+ let(:project) { projects_table.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce') }
+
+ def create_mr!(name, diffs: 0)
+ merge_request =
+ merge_requests_table.create!(target_project_id: project.id,
+ target_branch: 'master',
+ source_project_id: project.id,
+ source_branch: name,
+ title: name)
+
+ diffs.times do
+ merge_request_diffs_table.create!(merge_request_id: merge_request.id)
+ end
+
+ merge_request
+ end
+
+ def diffs_for(merge_request)
+ merge_request_diffs_table.where(merge_request_id: merge_request.id)
+ end
+
+ describe '#up' do
+ it 'ignores MRs without diffs' do
+ merge_request_without_diff = create_mr!('without_diff')
+
+ expect(merge_request_without_diff.latest_merge_request_diff_id).to be_nil
+
+ expect { migrate! }
+ .not_to change { merge_request_without_diff.reload.latest_merge_request_diff_id }
+ end
+
+ it 'ignores MRs that have a diff ID already set' do
+ merge_request_with_multiple_diffs = create_mr!('with_multiple_diffs', diffs: 3)
+ diff_id = diffs_for(merge_request_with_multiple_diffs).minimum(:id)
+
+ merge_request_with_multiple_diffs.update!(latest_merge_request_diff_id: diff_id)
+
+ expect { migrate! }
+ .not_to change { merge_request_with_multiple_diffs.reload.latest_merge_request_diff_id }
+ end
+
+ it 'migrates multiple MR diffs to the correct values' do
+ merge_requests = Array.new(3).map.with_index { |_, i| create_mr!(i, diffs: 3) }
+
+ migrate!
+
+ merge_requests.each do |merge_request|
+ expect(merge_request.reload.latest_merge_request_diff_id)
+ .to eq(diffs_for(merge_request).maximum(:id))
+ end
+ end
+ end
+end