Commit db759c5d authored by Stan Hu's avatar Stan Hu

Allow ref name caching CommitService#find_commit

For a given merge request, it's quite common to see duplicate FindCommit
Gitaly requests because the Gitaly CommitService caches the request by
the commit SHA, not by the ref name. However, most of the duplicate
requests use the ref name, so the cache is never actually used in
practice. This leads to unnecessary requests that slow performance.

This commit allows certain callers to bypass the ref name to
OID conversion in the cache. We don't do this by default because it's
possible the tip of the branch changes during the commit, which
would cause the caller to get stale data.

This commit also forces the Ci::Pipeline to use the full ref name
so that caching can work for merge requests.

Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/57083
parent c624c61a
...@@ -315,8 +315,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -315,8 +315,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end end
def serializer def serializer
::Gitlab::GitalyClient.allow_ref_name_caching do
MergeRequestSerializer.new(current_user: current_user, project: merge_request.project) MergeRequestSerializer.new(current_user: current_user, project: merge_request.project)
end end
end
def define_edit_vars def define_edit_vars
@source_project = @merge_request.source_project @source_project = @merge_request.source_project
......
...@@ -465,9 +465,9 @@ module Ci ...@@ -465,9 +465,9 @@ module Ci
end end
def latest? def latest?
return false unless ref && commit.present? return false unless git_ref && commit.present?
project.commit(ref) == commit project.commit(git_ref) == commit
end end
def retried def retried
...@@ -781,6 +781,7 @@ module Ci ...@@ -781,6 +781,7 @@ module Ci
end end
def git_ref def git_ref
strong_memoize(:git_pref) do
if merge_request_event? if merge_request_event?
## ##
# In the future, we're going to change this ref to # In the future, we're going to change this ref to
...@@ -793,6 +794,7 @@ module Ci ...@@ -793,6 +794,7 @@ module Ci
super super
end end
end end
end
def latest_builds_status def latest_builds_status
return 'failed' unless yaml_errors.blank? return 'failed' unless yaml_errors.blank?
......
---
title: Allow ref name caching CommitService#find_commit
merge_request: 26248
author:
type: performance
...@@ -296,6 +296,25 @@ module Gitlab ...@@ -296,6 +296,25 @@ module Gitlab
end end
end end
# Normally a FindCommit RPC will cache the commit with its SHA
# instead of a ref name, since it's possible the branch is mutated
# afterwards. However, for read-only requests that never mutate the
# branch, this method allows caching of the ref name directly.
def self.allow_ref_name_caching
return yield unless Gitlab::SafeRequestStore.active?
begin
Gitlab::SafeRequestStore[:allow_ref_name_caching] = true
yield
ensure
Gitlab::SafeRequestStore[:allow_ref_name_caching] = false
end
end
def self.ref_name_caching_allowed?
Gitlab::SafeRequestStore[:allow_ref_name_caching]
end
def self.get_call_count(key) def self.get_call_count(key)
Gitlab::SafeRequestStore[key] || 0 Gitlab::SafeRequestStore[key] || 0
end end
......
...@@ -286,7 +286,7 @@ module Gitlab ...@@ -286,7 +286,7 @@ module Gitlab
commit = call_find_commit(revision) commit = call_find_commit(revision)
return unless commit return unless commit
key[:commit_id] = commit.id key[:commit_id] = commit.id unless GitalyClient.ref_name_caching_allowed?
Gitlab::SafeRequestStore[key] = commit Gitlab::SafeRequestStore[key] = commit
else else
call_find_commit(revision) call_find_commit(revision)
......
...@@ -86,6 +86,10 @@ describe Projects::MergeRequestsController do ...@@ -86,6 +86,10 @@ describe Projects::MergeRequestsController do
end end
describe 'as json' do describe 'as json' do
before do
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
end
context 'with basic serializer param' do context 'with basic serializer param' do
it 'renders basic MR entity as json' do it 'renders basic MR entity as json' do
go(serializer: 'basic', format: :json) go(serializer: 'basic', format: :json)
......
...@@ -221,6 +221,21 @@ describe Gitlab::GitalyClient::CommitService do ...@@ -221,6 +221,21 @@ describe Gitlab::GitalyClient::CommitService do
expect(commit).to eq(commit_dbl) expect(commit).to eq(commit_dbl)
end end
end end
context 'when caching of the ref name is enabled' do
it 'returns a cached commit' do
expect_any_instance_of(Gitaly::CommitService::Stub).to receive(:find_commit).once.and_return(double(commit: commit_dbl))
commit = nil
2.times do
::Gitlab::GitalyClient.allow_ref_name_caching do
commit = described_class.new(repository).find_commit('master')
end
end
expect(commit).to eq(commit_dbl)
end
end
end end
end end
......
...@@ -1460,6 +1460,14 @@ describe Ci::Pipeline, :mailer do ...@@ -1460,6 +1460,14 @@ describe Ci::Pipeline, :mailer do
end end
end end
context 'with a branch name as the ref' do
it 'looks up commit with the full ref name' do
expect(pipeline.project).to receive(:commit).with('refs/heads/master').and_call_original
expect(pipeline).to be_latest
end
end
context 'with not latest sha' do context 'with not latest sha' do
before do before do
pipeline.update( pipeline.update(
......
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