Commit db464758 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'gitaly-reuse-stubs' into 'master'

Reuse gRPC stubs instead of channels

Closes #31991 and gitaly#187

See merge request !11244
parents 53a9f6a0 60106c1e
...@@ -1163,8 +1163,6 @@ class Repository ...@@ -1163,8 +1163,6 @@ class Repository
@project.repository_storage_path @project.repository_storage_path
end end
delegate :gitaly_channel, :gitaly_repository, to: :raw_repository
def initialize_raw_repository def initialize_raw_repository
Gitlab::Git::Repository.new(project.repository_storage, path_with_namespace + '.git') Gitlab::Git::Repository.new(project.repository_storage, path_with_namespace + '.git')
end end
......
require 'uri' require 'uri'
# Make sure we initialize our Gitaly channels before Sidekiq starts multi-threaded execution.
if Gitlab.config.gitaly.enabled || Rails.env.test? if Gitlab.config.gitaly.enabled || Rails.env.test?
Gitlab::GitalyClient.configure_channels Gitlab.config.repositories.storages.keys.each do |storage|
# Force validation of each address
Gitlab::GitalyClient.address(storage)
end
end end
...@@ -27,13 +27,15 @@ module Gitlab ...@@ -27,13 +27,15 @@ module Gitlab
# Rugged repo object # Rugged repo object
attr_reader :rugged attr_reader :rugged
attr_reader :storage
# 'path' must be the path to a _bare_ git repository, e.g. # 'path' must be the path to a _bare_ git repository, e.g.
# /path/to/my-repo.git # /path/to/my-repo.git
def initialize(repository_storage, relative_path) def initialize(storage, relative_path)
@repository_storage = repository_storage @storage = storage
@relative_path = relative_path @relative_path = relative_path
storage_path = Gitlab.config.repositories.storages[@repository_storage]['path'] storage_path = Gitlab.config.repositories.storages[@storage]['path']
@path = File.join(storage_path, @relative_path) @path = File.join(storage_path, @relative_path)
@name = @relative_path.split("/").last @name = @relative_path.split("/").last
@attributes = Gitlab::Git::Attributes.new(path) @attributes = Gitlab::Git::Attributes.new(path)
...@@ -965,11 +967,7 @@ module Gitlab ...@@ -965,11 +967,7 @@ module Gitlab
end end
def gitaly_repository def gitaly_repository
Gitlab::GitalyClient::Util.repository(@repository_storage, @relative_path) Gitlab::GitalyClient::Util.repository(@storage, @relative_path)
end
def gitaly_channel
Gitlab::GitalyClient.get_channel(@repository_storage)
end end
private private
......
...@@ -4,56 +4,42 @@ module Gitlab ...@@ -4,56 +4,42 @@ module Gitlab
module GitalyClient module GitalyClient
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'.freeze SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'.freeze
# This function is not thread-safe because it updates Hashes in instance variables. MUTEX = Mutex.new
def self.configure_channels private_constant :MUTEX
@addresses = {}
@channels = {}
Gitlab.config.repositories.storages.each do |name, params|
address = params['gitaly_address']
unless address.present?
raise "storage #{name.inspect} is missing a gitaly_address"
end
unless URI(address).scheme.in?(%w(tcp unix)) def self.stub(name, storage)
raise "Unsupported Gitaly address: #{address.inspect} does not use URL scheme 'tcp' or 'unix'" MUTEX.synchronize do
@stubs ||= {}
@stubs[storage] ||= {}
@stubs[storage][name] ||= begin
klass = Gitaly.const_get(name.to_s.camelcase.to_sym).const_get(:Stub)
addr = address(storage)
addr = addr.sub(%r{^tcp://}, '') if URI(addr).scheme == 'tcp'
klass.new(addr, :this_channel_is_insecure)
end end
@addresses[name] = address
@channels[name] = new_channel(address)
end end
end end
def self.new_channel(address) def self.clear_stubs!
address = address.sub(%r{^tcp://}, '') if URI(address).scheme == 'tcp' MUTEX.synchronize do
# NOTE: When Gitaly runs on a Unix socket, permissions are @stubs = nil
# handled using the file system and no additional authentication is
# required (therefore the :this_channel_is_insecure flag)
# TODO: Add authentication support when Gitaly is running on a TCP socket.
GRPC::Core::Channel.new(address, {}, :this_channel_is_insecure)
end end
def self.get_channel(storage)
if !Rails.env.production? && @channels.nil?
# In development mode the Rails auto-loader may reset the instance
# variables of this class. What we do here is not thread-safe. In normal
# circumstances (including production) these instance variables have
# been initialized from config/initializers.
configure_channels
end end
@channels[storage] def self.address(storage)
params = Gitlab.config.repositories.storages[storage]
raise "storage not found: #{storage.inspect}" if params.nil?
address = params['gitaly_address']
unless address.present?
raise "storage #{storage.inspect} is missing a gitaly_address"
end end
def self.get_address(storage) unless URI(address).scheme.in?(%w(tcp unix))
if !Rails.env.production? && @addresses.nil? raise "Unsupported Gitaly address: #{address.inspect} does not use URL scheme 'tcp' or 'unix'"
# In development mode the Rails auto-loader may reset the instance
# variables of this class. What we do here is not thread-safe. In normal
# circumstances (including development) these instance variables have
# been initialized from config/initializers.
configure_channels
end end
@addresses[storage] address
end end
def self.enabled? def self.enabled?
......
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
end end
def is_ancestor(ancestor_id, child_id) def is_ancestor(ancestor_id, child_id)
stub = Gitaly::Commit::Stub.new(nil, nil, channel_override: @repository.gitaly_channel) stub = GitalyClient.stub(:commit, @repository.storage)
request = Gitaly::CommitIsAncestorRequest.new( request = Gitaly::CommitIsAncestorRequest.new(
repository: @gitaly_repo, repository: @gitaly_repo,
ancestor_id: ancestor_id, ancestor_id: ancestor_id,
...@@ -52,7 +52,7 @@ module Gitlab ...@@ -52,7 +52,7 @@ module Gitlab
end end
def diff_service_stub def diff_service_stub
Gitaly::Diff::Stub.new(nil, nil, channel_override: @repository.gitaly_channel) GitalyClient.stub(:diff, @repository.storage)
end end
end end
end end
......
...@@ -6,7 +6,7 @@ module Gitlab ...@@ -6,7 +6,7 @@ module Gitlab
# 'repository' is a Gitlab::Git::Repository # 'repository' is a Gitlab::Git::Repository
def initialize(repository) def initialize(repository)
@gitaly_repo = repository.gitaly_repository @gitaly_repo = repository.gitaly_repository
@stub = Gitaly::Notifications::Stub.new(nil, nil, channel_override: repository.gitaly_channel) @stub = GitalyClient.stub(:notifications, repository.storage)
end end
def post_receive def post_receive
......
...@@ -6,7 +6,7 @@ module Gitlab ...@@ -6,7 +6,7 @@ module Gitlab
# 'repository' is a Gitlab::Git::Repository # 'repository' is a Gitlab::Git::Repository
def initialize(repository) def initialize(repository)
@gitaly_repo = repository.gitaly_repository @gitaly_repo = repository.gitaly_repository
@stub = Gitaly::Ref::Stub.new(nil, nil, channel_override: repository.gitaly_channel) @stub = GitalyClient.stub(:ref, repository.storage)
end end
def default_branch_name def default_branch_name
......
...@@ -26,7 +26,7 @@ module Gitlab ...@@ -26,7 +26,7 @@ module Gitlab
} }
if Gitlab.config.gitaly.enabled if Gitlab.config.gitaly.enabled
address = Gitlab::GitalyClient.get_address(project.repository_storage) address = Gitlab::GitalyClient.address(project.repository_storage)
params[:Repository] = repository.gitaly_repository.to_h params[:Repository] = repository.gitaly_repository.to_h
feature_enabled = case action.to_s feature_enabled = case action.to_s
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::GitalyClient, lib: true do describe Gitlab::GitalyClient, lib: true do
describe '.new_channel' do describe '.stub' do
before { described_class.clear_stubs! }
context 'when passed a UNIX socket address' do context 'when passed a UNIX socket address' do
it 'passes the address as-is to GRPC::Core::Channel initializer' do it 'passes the address as-is to GRPC' do
address = 'unix:/tmp/gitaly.sock' address = 'unix:/tmp/gitaly.sock'
allow(Gitlab.config.repositories).to receive(:storages).and_return({
'default' => { 'gitaly_address' => address }
})
expect(GRPC::Core::Channel).to receive(:new).with(address, any_args) expect(Gitaly::Commit::Stub).to receive(:new).with(address, any_args)
described_class.new_channel(address) described_class.stub(:commit, 'default')
end end
end end
...@@ -17,9 +22,13 @@ describe Gitlab::GitalyClient, lib: true do ...@@ -17,9 +22,13 @@ describe Gitlab::GitalyClient, lib: true do
address = 'localhost:9876' address = 'localhost:9876'
prefixed_address = "tcp://#{address}" prefixed_address = "tcp://#{address}"
expect(GRPC::Core::Channel).to receive(:new).with(address, any_args) allow(Gitlab.config.repositories).to receive(:storages).and_return({
'default' => { 'gitaly_address' => prefixed_address }
})
expect(Gitaly::Commit::Stub).to receive(:new).with(address, any_args)
described_class.new_channel(prefixed_address) described_class.stub(:commit, 'default')
end end
end end
end end
......
...@@ -202,7 +202,7 @@ describe Gitlab::Workhorse, lib: true do ...@@ -202,7 +202,7 @@ describe Gitlab::Workhorse, lib: true do
context 'when Gitaly is enabled' do context 'when Gitaly is enabled' do
let(:gitaly_params) do let(:gitaly_params) do
{ {
GitalyAddress: Gitlab::GitalyClient.get_address('default') GitalyAddress: Gitlab::GitalyClient.address('default')
} }
end end
......
...@@ -120,7 +120,7 @@ module TestEnv ...@@ -120,7 +120,7 @@ module TestEnv
end end
def setup_gitaly def setup_gitaly
socket_path = Gitlab::GitalyClient.get_address('default').sub(/\Aunix:/, '') socket_path = Gitlab::GitalyClient.address('default').sub(/\Aunix:/, '')
gitaly_dir = File.dirname(socket_path) gitaly_dir = File.dirname(socket_path)
unless File.directory?(gitaly_dir) || system('rake', "gitlab:gitaly:install[#{gitaly_dir}]") unless File.directory?(gitaly_dir) || system('rake', "gitlab:gitaly:install[#{gitaly_dir}]")
...@@ -133,7 +133,8 @@ module TestEnv ...@@ -133,7 +133,8 @@ module TestEnv
def start_gitaly(gitaly_dir) def start_gitaly(gitaly_dir)
gitaly_exec = File.join(gitaly_dir, 'gitaly') gitaly_exec = File.join(gitaly_dir, 'gitaly')
gitaly_config = File.join(gitaly_dir, 'config.toml') gitaly_config = File.join(gitaly_dir, 'config.toml')
@gitaly_pid = spawn(gitaly_exec, gitaly_config, [:out, :err] => '/dev/null') log_file = Rails.root.join('log/gitaly-test.log').to_s
@gitaly_pid = spawn(gitaly_exec, gitaly_config, [:out, :err] => log_file)
end end
def stop_gitaly def stop_gitaly
......
...@@ -236,7 +236,6 @@ describe 'gitlab:app namespace rake task' do ...@@ -236,7 +236,6 @@ describe 'gitlab:app namespace rake task' do
'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage'), 'gitaly_address' => gitaly_address } 'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage'), 'gitaly_address' => gitaly_address }
} }
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
Gitlab::GitalyClient.configure_channels
# Create the projects now, after mocking the settings but before doing the backup # Create the projects now, after mocking the settings but before doing the backup
project_a project_a
......
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