Use Gitaly for CommitController#show

This commit is contained in:
Ahmad Sherif 2017-02-24 17:53:44 +02:00
parent b716680692
commit c0a4f527db
11 changed files with 199 additions and 7 deletions

View file

@ -352,4 +352,4 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client
gem 'gitaly', '~> 0.2.1'
gem 'gitaly', '~> 0.3.0'

View file

@ -250,7 +250,7 @@ GEM
json
get_process_mem (0.2.0)
gherkin-ruby (0.3.2)
gitaly (0.2.1)
gitaly (0.3.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
@ -896,7 +896,7 @@ DEPENDENCIES
fuubar (~> 2.0.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.0)
gitaly (~> 0.2.1)
gitaly (~> 0.3.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1)

View file

@ -317,7 +317,14 @@ class Commit
end
def raw_diffs(*args)
raw.diffs(*args)
use_gitaly = Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
deltas_only = args.last.is_a?(Hash) && args.last[:deltas_only]
if use_gitaly && !deltas_only
Gitlab::GitalyClient::Commit.diff_from_parent(self, *args)
else
raw.diffs(*args)
end
end
def diffs(diff_options = nil)

View file

@ -0,0 +1,4 @@
---
title: Use Gitaly for CommitController#show
merge_request: 9629
author:

View file

@ -176,9 +176,13 @@ module Gitlab
def initialize(raw_diff, collapse: false)
case raw_diff
when Hash
init_from_hash(raw_diff, collapse: collapse)
init_from_hash(raw_diff)
prune_diff_if_eligible(collapse)
when Rugged::Patch, Rugged::Diff::Delta
init_from_rugged(raw_diff, collapse: collapse)
when Gitaly::CommitDiffResponse
init_from_gitaly(raw_diff)
prune_diff_if_eligible(collapse)
when nil
raise "Nil as raw diff passed"
else
@ -266,13 +270,26 @@ module Gitlab
@diff = encode!(strip_diff_headers(patch.to_s))
end
def init_from_hash(hash, collapse: false)
def init_from_hash(hash)
raw_diff = hash.symbolize_keys
serialize_keys.each do |key|
send(:"#{key}=", raw_diff[key.to_sym])
end
end
def init_from_gitaly(diff_msg)
@diff = diff_msg.raw_chunks.join
@new_path = encode!(diff_msg.to_path.dup)
@old_path = encode!(diff_msg.from_path.dup)
@a_mode = diff_msg.old_mode.to_s(8)
@b_mode = diff_msg.new_mode.to_s(8)
@new_file = diff_msg.from_id == BLANK_SHA
@renamed_file = diff_msg.from_path != diff_msg.to_path
@deleted_file = diff_msg.to_id == BLANK_SHA
end
def prune_diff_if_eligible(collapse = false)
prune_large_diff! if too_large?
prune_collapsed_diff! if collapse && collapsible?
end

View file

@ -30,7 +30,9 @@ module Gitlab
elsif @deltas_only
each_delta(&block)
else
each_patch(&block)
Gitlab::GitalyClient.migrate(:commit_raw_diffs) do
each_patch(&block)
end
end
end

View file

@ -25,5 +25,19 @@ module Gitlab
def self.enabled?
gitaly_address.present?
end
def self.feature_enabled?(feature)
enabled? && ENV["GITALY_#{feature.upcase}"] == '1'
end
def self.migrate(feature)
is_enabled = feature_enabled?(feature)
metric_name = feature.to_s
metric_name += "_gitaly" if is_enabled
Gitlab::Metrics.measure(metric_name) do
yield is_enabled
end
end
end
end

View file

@ -0,0 +1,25 @@
module Gitlab
module GitalyClient
class Commit
# The ID of empty tree.
# See http://stackoverflow.com/a/40884093/1856239 and https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012
EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze
class << self
def diff_from_parent(commit, options = {})
stub = Gitaly::Diff::Stub.new(nil, nil, channel_override: GitalyClient.channel)
repo = Gitaly::Repository.new(path: commit.project.repository.path_to_repo)
parent = commit.parents[0]
parent_id = parent ? parent.id : EMPTY_TREE_ID
request = Gitaly::CommitDiffRequest.new(
repository: repo,
left_commit_id: parent_id,
right_commit_id: commit.id
)
Gitlab::Git::DiffCollection.new(stub.commit_diff(request), options)
end
end
end
end
end

View file

@ -109,6 +109,43 @@ EOT
end
end
end
context 'using a Gitaly::CommitDiffResponse' do
let(:diff) do
described_class.new(
Gitaly::CommitDiffResponse.new(
to_path: ".gitmodules",
from_path: ".gitmodules",
old_mode: 0100644,
new_mode: 0100644,
from_id: '357406f3075a57708d0163752905cc1576fceacc',
to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0',
raw_chunks: raw_chunks,
)
)
end
context 'with a small diff' do
let(:raw_chunks) { [@raw_diff_hash[:diff]] }
it 'initializes the diff' do
expect(diff.to_hash).to eq(@raw_diff_hash)
end
it 'does not prune the diff' do
expect(diff).not_to be_too_large
end
end
context 'using a diff that is too large' do
let(:raw_chunks) { ['a' * 204800] }
it 'prunes the diff' do
expect(diff.diff).to be_empty
expect(diff).to be_too_large
end
end
end
end
describe 'straight diffs' do

View file

@ -0,0 +1,58 @@
require 'spec_helper'
describe Gitlab::GitalyClient::Commit do
describe '.diff_from_parent' do
let(:diff_stub) { double('Gitaly::Diff::Stub') }
let(:project) { create(:project, :repository) }
let(:repository_message) { Gitaly::Repository.new(path: project.repository.path) }
let(:commit) { project.commit('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
before do
allow(Gitaly::Diff::Stub).to receive(:new).and_return(diff_stub)
allow(diff_stub).to receive(:commit_diff).and_return([])
end
context 'when a commit has a parent' do
it 'sends an RPC request with the parent ID as left commit' do
request = Gitaly::CommitDiffRequest.new(
repository: repository_message,
left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
right_commit_id: commit.id,
)
expect(diff_stub).to receive(:commit_diff).with(request)
described_class.diff_from_parent(commit)
end
end
context 'when a commit does not have a parent' do
it 'sends an RPC request with empty tree ref as left commit' do
initial_commit = project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863')
request = Gitaly::CommitDiffRequest.new(
repository: repository_message,
left_commit_id: '4b825dc642cb6eb9a060e54bf8d69288fbee4904',
right_commit_id: initial_commit.id,
)
expect(diff_stub).to receive(:commit_diff).with(request)
described_class.diff_from_parent(initial_commit)
end
end
it 'returns a Gitlab::Git::DiffCollection' do
ret = described_class.diff_from_parent(commit)
expect(ret).to be_kind_of(Gitlab::Git::DiffCollection)
end
it 'passes options to Gitlab::Git::DiffCollection' do
options = { max_files: 31, max_lines: 13 }
expect(Gitlab::Git::DiffCollection).to receive(:new).with([], options)
described_class.diff_from_parent(commit, options)
end
end
end

View file

@ -369,4 +369,32 @@ eos
expect(described_class.valid_hash?('a' * 41)).to be false
end
end
describe '#raw_diffs' do
context 'Gitaly commit_raw_diffs feature enabled' do
before do
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:commit_raw_diffs).and_return(true)
end
context 'when a truthy deltas_only is not passed to args' do
it 'fetches diffs from Gitaly server' do
expect(Gitlab::GitalyClient::Commit).to receive(:diff_from_parent).
with(commit)
commit.raw_diffs
end
end
context 'when a truthy deltas_only is passed to args' do
it 'fetches diffs using Rugged' do
opts = { deltas_only: true }
expect(Gitlab::GitalyClient::Commit).not_to receive(:diff_from_parent)
expect(commit.raw).to receive(:diffs).with(opts)
commit.raw_diffs(opts)
end
end
end
end
end