Commit 89d7678f authored by Sean McGivern's avatar Sean McGivern

Merge branch 'zj-memoization-mr-commits-ee' into 'master'

Use memoization for commits on diffs

See merge request gitlab-org/gitlab-ee!3744
parents eb2e39b9 a0fddb81
...@@ -8,6 +8,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -8,6 +8,7 @@ class MergeRequest < ActiveRecord::Base
include TimeTrackable include TimeTrackable
include ManualInverseAssociation include ManualInverseAssociation
include EachBatch include EachBatch
include Gitlab::Utils::StrongMemoize
ignore_column :locked_at, ignore_column :locked_at,
:ref_fetched :ref_fetched
...@@ -55,6 +56,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -55,6 +56,7 @@ class MergeRequest < ActiveRecord::Base
serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize
after_create :ensure_merge_request_diff, unless: :importing? after_create :ensure_merge_request_diff, unless: :importing?
after_update :clear_memoized_shas
after_update :reload_diff_if_branch_changed after_update :reload_diff_if_branch_changed
# When this attribute is true some MR validation is ignored # When this attribute is true some MR validation is ignored
...@@ -403,13 +405,17 @@ class MergeRequest < ActiveRecord::Base ...@@ -403,13 +405,17 @@ class MergeRequest < ActiveRecord::Base
end end
def source_branch_head def source_branch_head
return unless source_project strong_memoize(:source_branch_head) do
if source_project && source_branch_ref
source_project.repository.commit(source_branch_ref) if source_branch_ref source_project.repository.commit(source_branch_ref)
end
end
end end
def target_branch_head def target_branch_head
target_project.repository.commit(target_branch_ref) strong_memoize(:target_branch_head) do
target_project.repository.commit(target_branch_ref)
end
end end
def branch_merge_base_commit def branch_merge_base_commit
...@@ -557,6 +563,13 @@ class MergeRequest < ActiveRecord::Base ...@@ -557,6 +563,13 @@ class MergeRequest < ActiveRecord::Base
end end
end end
def clear_memoized_shas
@target_branch_sha = @source_branch_sha = nil
clear_memoization(:source_branch_head)
clear_memoization(:target_branch_head)
end
def reload_diff_if_branch_changed def reload_diff_if_branch_changed
if (source_branch_changed? || target_branch_changed?) && if (source_branch_changed? || target_branch_changed?) &&
(source_branch_head && target_branch_head) (source_branch_head && target_branch_head)
......
...@@ -104,19 +104,19 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -104,19 +104,19 @@ class MergeRequestDiff < ActiveRecord::Base
def base_commit def base_commit
return unless base_commit_sha return unless base_commit_sha
project.commit(base_commit_sha) project.commit_by(oid: base_commit_sha)
end end
def start_commit def start_commit
return unless start_commit_sha return unless start_commit_sha
project.commit(start_commit_sha) project.commit_by(oid: start_commit_sha)
end end
def head_commit def head_commit
return unless head_commit_sha return unless head_commit_sha
project.commit(head_commit_sha) project.commit_by(oid: head_commit_sha)
end end
def commit_shas def commit_shas
......
---
title: Cache commits for MergeRequest diffs
merge_request:
author:
type: performance
...@@ -19,6 +19,8 @@ module Gitlab ...@@ -19,6 +19,8 @@ module Gitlab
commit_message: commit_message || default_commit_message commit_message: commit_message || default_commit_message
} }
resolver.resolve_conflicts(user, files, args) resolver.resolve_conflicts(user, files, args)
ensure
@merge_request.clear_memoized_shas
end end
def files def files
......
...@@ -11,6 +11,8 @@ module Gitlab ...@@ -11,6 +11,8 @@ module Gitlab
# #
# We could write it like: # We could write it like:
# #
# include Gitlab::Utils::StrongMemoize
#
# def trigger_from_token # def trigger_from_token
# strong_memoize(:trigger) do # strong_memoize(:trigger) do
# Ci::Trigger.find_by_token(params[:token].to_s) # Ci::Trigger.find_by_token(params[:token].to_s)
...@@ -18,14 +20,22 @@ module Gitlab ...@@ -18,14 +20,22 @@ module Gitlab
# end # end
# #
def strong_memoize(name) def strong_memoize(name)
ivar_name = "@#{name}" if instance_variable_defined?(ivar(name))
instance_variable_get(ivar(name))
if instance_variable_defined?(ivar_name)
instance_variable_get(ivar_name)
else else
instance_variable_set(ivar_name, yield) instance_variable_set(ivar(name), yield)
end end
end end
def clear_memoization(name)
remove_instance_variable(ivar(name)) if instance_variable_defined?(ivar(name))
end
private
def ivar(name)
"@#{name}"
end
end end
end end
end end
...@@ -49,4 +49,16 @@ describe Gitlab::Utils::StrongMemoize do ...@@ -49,4 +49,16 @@ describe Gitlab::Utils::StrongMemoize do
end end
end end
end end
describe '#clear_memoization' do
let(:value) { 'mepmep' }
it 'removes the instance variable' do
object.method_name
object.clear_memoization(:method_name)
expect(object.instance_variable_defined?(:@method_name)).to be(false)
end
end
end end
...@@ -124,6 +124,7 @@ describe MergeRequest do ...@@ -124,6 +124,7 @@ describe MergeRequest do
context 'when the target branch does not exist' do context 'when the target branch does not exist' do
before do before do
project.repository.rm_branch(subject.author, subject.target_branch) project.repository.rm_branch(subject.author, subject.target_branch)
subject.clear_memoized_shas
end end
it 'returns nil' do it 'returns nil' do
...@@ -809,30 +810,30 @@ describe MergeRequest do ...@@ -809,30 +810,30 @@ describe MergeRequest do
end end
describe '#can_remove_source_branch?' do describe '#can_remove_source_branch?' do
let(:user) { create(:user) } set(:user) { create(:user) }
let(:user2) { create(:user) } set(:merge_request) { create(:merge_request, :simple) }
before do subject { merge_request }
subject.source_project.team << [user, :master]
subject.source_branch = "feature" before do
subject.target_branch = "master" subject.source_project.add_master(user)
subject.save!
end end
it "can't be removed when its a protected branch" do it "can't be removed when its a protected branch" do
allow(ProtectedBranch).to receive(:protected?).and_return(true) allow(ProtectedBranch).to receive(:protected?).and_return(true)
expect(subject.can_remove_source_branch?(user)).to be_falsey expect(subject.can_remove_source_branch?(user)).to be_falsey
end end
it "can't remove a root ref" do it "can't remove a root ref" do
subject.source_branch = "master" subject.update(source_branch: 'master', target_branch: 'feature')
subject.target_branch = "feature"
expect(subject.can_remove_source_branch?(user)).to be_falsey expect(subject.can_remove_source_branch?(user)).to be_falsey
end end
it "is unable to remove the source branch for a project the user cannot push to" do it "is unable to remove the source branch for a project the user cannot push to" do
user2 = create(:user)
expect(subject.can_remove_source_branch?(user2)).to be_falsey expect(subject.can_remove_source_branch?(user2)).to be_falsey
end end
...@@ -843,6 +844,7 @@ describe MergeRequest do ...@@ -843,6 +844,7 @@ describe MergeRequest do
end end
it "cannot be removed if the last commit is not also the head of the source branch" do it "cannot be removed if the last commit is not also the head of the source branch" do
subject.clear_memoized_shas
subject.source_branch = "lfs" subject.source_branch = "lfs"
expect(subject.can_remove_source_branch?(user)).to be_falsey expect(subject.can_remove_source_branch?(user)).to be_falsey
...@@ -942,7 +944,7 @@ describe MergeRequest do ...@@ -942,7 +944,7 @@ describe MergeRequest do
before do before do
project.repository.raw_repository.delete_branch(subject.target_branch) project.repository.raw_repository.delete_branch(subject.target_branch)
subject.reload subject.clear_memoized_shas
end end
it 'does not crash' do it 'does not crash' do
...@@ -1633,6 +1635,16 @@ describe MergeRequest do ...@@ -1633,6 +1635,16 @@ describe MergeRequest do
subject.reload_diff subject.reload_diff
end end
context 'when using the after_update hook to update' do
context 'when the branches are updated' do
it 'uses the new heads to generate the diff' do
expect { subject.update!(source_branch: subject.target_branch, target_branch: subject.source_branch) }
.to change { subject.merge_request_diff.start_commit_sha }
.and change { subject.merge_request_diff.head_commit_sha }
end
end
end
end end
describe '#update_diff_discussion_positions' do describe '#update_diff_discussion_positions' do
...@@ -1895,6 +1907,7 @@ describe MergeRequest do ...@@ -1895,6 +1907,7 @@ describe MergeRequest do
context 'when the target branch does not exist' do context 'when the target branch does not exist' do
before do before do
subject.project.repository.rm_branch(subject.author, subject.target_branch) subject.project.repository.rm_branch(subject.author, subject.target_branch)
subject.clear_memoized_shas
end end
it 'returns nil' do it 'returns nil' do
......
require 'spec_helper' require 'spec_helper'
describe MergeRequests::MergeService do describe MergeRequests::MergeService do
let(:user) { create(:user) } set(:user) { create(:user) }
let(:user2) { create(:user) } set(:user2) { create(:user) }
let(:merge_request) { create(:merge_request, :simple, author: user2, assignee: user2) } let(:merge_request) { create(:merge_request, :simple, author: user2, assignee: user2) }
let(:project) { merge_request.project } let(:project) { merge_request.project }
before do before do
project.team << [user, :master] project.add_master(user)
project.team << [user2, :developer] project.add_developer(user2)
end end
describe '#execute' do describe '#execute' do
......
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