Commit 1d63cc1b authored by Yorick Peterse's avatar Yorick Peterse

Move load balancing configuration into a class

This moves load balancing configuration settings, such as the
replication lag settings, into a dedicated class. This allows us to
scope load balancer settings to load balancer instances, instead of
being forced to use global settings. This in turn will make it easier to
always enable the load balancing code, and to eventually support load
balancing of multiple databases.
parent 963d9101
...@@ -36,94 +36,42 @@ module Gitlab ...@@ -36,94 +36,42 @@ module Gitlab
# Returns a Hash containing the load balancing configuration. # Returns a Hash containing the load balancing configuration.
def self.configuration def self.configuration
Gitlab::Database.main.config[:load_balancing] || {} @configuration ||= Configuration.for_model(ActiveRecord::Base)
end
# Returns the maximum replica lag size in bytes.
def self.max_replication_difference
(configuration['max_replication_difference'] || 8.megabytes).to_i
end
# Returns the maximum lag time for a replica.
def self.max_replication_lag_time
(configuration['max_replication_lag_time'] || 60.0).to_f
end
# Returns the interval (in seconds) to use for checking the status of a
# replica.
def self.replica_check_interval
(configuration['replica_check_interval'] || 60).to_f
end
# Returns the additional hosts to use for load balancing.
def self.hosts
configuration['hosts'] || []
end
def self.service_discovery_enabled?
configuration.dig('discover', 'record').present?
end
def self.service_discovery_configuration
conf = configuration['discover'] || {}
{
nameserver: conf['nameserver'] || 'localhost',
port: conf['port'] || 8600,
record: conf['record'],
record_type: conf['record_type'] || 'A',
interval: conf['interval'] || 60,
disconnect_timeout: conf['disconnect_timeout'] || 120,
use_tcp: conf['use_tcp'] || false
}
end
def self.pool_size
Gitlab::Database.main.pool_size
end end
# Returns true if load balancing is to be enabled. # Returns true if load balancing is to be enabled.
def self.enable? def self.enable?
return false if Gitlab::Runtime.rake? return false if Gitlab::Runtime.rake?
return false unless self.configured?
true configured?
end end
# Returns true if load balancing has been configured. Since
# Sidekiq does not currently use load balancing, we
# may want Web application servers to detect replication lag by
# posting the write location of the database if load balancing is
# configured.
def self.configured? def self.configured?
hosts.any? || service_discovery_enabled? configuration.load_balancing_enabled? ||
configuration.service_discovery_enabled?
end end
def self.start_service_discovery def self.start_service_discovery
return unless service_discovery_enabled? return unless configuration.service_discovery_enabled?
ServiceDiscovery ServiceDiscovery
.new(proxy.load_balancer, **service_discovery_configuration) .new(proxy.load_balancer, **configuration.service_discovery)
.start .start
end end
# Configures proxying of requests. # Configures proxying of requests.
def self.configure_proxy def self.configure_proxy
lb = LoadBalancer.new(hosts, primary_only: !enable?) lb = LoadBalancer.new(configuration, primary_only: !enable?)
ActiveRecord::Base.load_balancing_proxy = ConnectionProxy.new(lb) ActiveRecord::Base.load_balancing_proxy = ConnectionProxy.new(lb)
# Populate service discovery immediately if it is configured # Populate service discovery immediately if it is configured
if service_discovery_enabled? if configuration.service_discovery_enabled?
ServiceDiscovery ServiceDiscovery
.new(lb, **service_discovery_configuration) .new(lb, **configuration.service_discovery)
.perform_service_discovery .perform_service_discovery
end end
end end
def self.active_record_models
ActiveRecord::Base.descendants
end
DB_ROLES = [ DB_ROLES = [
ROLE_PRIMARY = :primary, ROLE_PRIMARY = :primary,
ROLE_REPLICA = :replica, ROLE_REPLICA = :replica,
......
# frozen_string_literal: true
module Gitlab
module Database
module LoadBalancing
# Configuration settings for a single LoadBalancer instance.
class Configuration
attr_accessor :hosts, :max_replication_difference,
:max_replication_lag_time, :replica_check_interval,
:service_discovery, :pool_size, :model
# Creates a configuration object for the given ActiveRecord model.
def self.for_model(model)
cfg = model.connection_db_config.configuration_hash
lb_cfg = cfg[:load_balancing] || {}
config = new(model)
if (size = cfg[:pool])
config.pool_size = size
end
if (diff = lb_cfg[:max_replication_difference])
config.max_replication_difference = diff
end
if (lag = lb_cfg[:max_replication_lag_time])
config.max_replication_lag_time = lag.to_f
end
if (interval = lb_cfg[:replica_check_interval])
config.replica_check_interval = interval.to_f
end
if (hosts = lb_cfg[:hosts])
config.hosts = hosts
end
discover = (lb_cfg[:discover] || {}).symbolize_keys
# We iterate over the known/default keys so we don't end up with
# random keys in our configuration hash.
config.service_discovery.each do |key, _|
if (value = discover[key])
config.service_discovery[key] = value
end
end
config
end
def initialize(model, hosts = [])
@max_replication_difference = 8.megabytes
@max_replication_lag_time = 60.0
@replica_check_interval = 60.0
@model = model
@hosts = hosts
@pool_size = Database.default_pool_size
@service_discovery = {
nameserver: 'localhost',
port: 8600,
record: nil,
record_type: 'A',
interval: 60,
disconnect_timeout: 120,
use_tcp: false
}
end
def load_balancing_enabled?
hosts.any? || service_discovery_enabled?
end
def service_discovery_enabled?
service_discovery[:record].present?
end
end
end
end
end
...@@ -29,11 +29,15 @@ module Gitlab ...@@ -29,11 +29,15 @@ module Gitlab
@host = host @host = host
@port = port @port = port
@load_balancer = load_balancer @load_balancer = load_balancer
@pool = load_balancer.create_replica_connection_pool(::Gitlab::Database::LoadBalancing.pool_size, host, port) @pool = load_balancer.create_replica_connection_pool(
load_balancer.configuration.pool_size,
host,
port
)
@online = true @online = true
@last_checked_at = Time.zone.now @last_checked_at = Time.zone.now
interval = ::Gitlab::Database::LoadBalancing.replica_check_interval interval = load_balancer.configuration.replica_check_interval
@intervals = (interval..(interval * 2)).step(0.5).to_a @intervals = (interval..(interval * 2)).step(0.5).to_a
end end
...@@ -108,7 +112,7 @@ module Gitlab ...@@ -108,7 +112,7 @@ module Gitlab
def replication_lag_below_threshold? def replication_lag_below_threshold?
if (lag_time = replication_lag_time) if (lag_time = replication_lag_time)
lag_time <= ::Gitlab::Database::LoadBalancing.max_replication_lag_time lag_time <= load_balancer.configuration.max_replication_lag_time
else else
false false
end end
...@@ -125,7 +129,7 @@ module Gitlab ...@@ -125,7 +129,7 @@ module Gitlab
# only do this if we haven't replicated in a while so we only need # only do this if we haven't replicated in a while so we only need
# to connect to the primary when truly necessary. # to connect to the primary when truly necessary.
if (lag_size = replication_lag_size) if (lag_size = replication_lag_size)
lag_size <= ::Gitlab::Database::LoadBalancing.max_replication_difference lag_size <= load_balancer.configuration.max_replication_difference
else else
false false
end end
......
...@@ -12,20 +12,21 @@ module Gitlab ...@@ -12,20 +12,21 @@ module Gitlab
REPLICA_SUFFIX = '_replica' REPLICA_SUFFIX = '_replica'
attr_reader :host_list attr_reader :host_list, :configuration
# hosts - The hostnames/addresses of the additional databases. # configuration - An instance of `LoadBalancing::Configuration` that
# model - The ActiveRecord base model the load balancer is enabled for. # contains the configuration details (such as the hosts)
# for this load balancer.
# primary_only - If set, the replicas are ignored and the primary is # primary_only - If set, the replicas are ignored and the primary is
# always used. # always used.
def initialize(hosts = [], model = ActiveRecord::Base, primary_only: false) def initialize(configuration, primary_only: false)
@configuration = configuration
@primary_only = primary_only @primary_only = primary_only
@model = model
@host_list = @host_list =
if primary_only if primary_only
HostList.new([PrimaryHost.new(self)]) HostList.new([PrimaryHost.new(self)])
else else
HostList.new(hosts.map { |addr| Host.new(addr, self) }) HostList.new(configuration.hosts.map { |addr| Host.new(addr, self) })
end end
end end
...@@ -231,7 +232,7 @@ module Gitlab ...@@ -231,7 +232,7 @@ module Gitlab
# leverage that. # leverage that.
def pool def pool
ActiveRecord::Base.connection_handler.retrieve_connection_pool( ActiveRecord::Base.connection_handler.retrieve_connection_pool(
@model.connection_specification_name, @configuration.model.connection_specification_name,
role: ActiveRecord::Base.writing_role, role: ActiveRecord::Base.writing_role,
shard: ActiveRecord::Base.default_shard shard: ActiveRecord::Base.default_shard
) )
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::Configuration do
describe '.for_model' do
let(:model) do
config = ActiveRecord::DatabaseConfigurations::HashConfig
.new('main', 'test', configuration_hash)
double(:model, connection_db_config: config)
end
context 'when load balancing is not configured' do
let(:configuration_hash) { {} }
it 'uses the default settings' do
config = described_class.for_model(model)
expect(config.hosts).to eq([])
expect(config.max_replication_difference).to eq(8.megabytes)
expect(config.max_replication_lag_time).to eq(60.0)
expect(config.replica_check_interval).to eq(60.0)
expect(config.service_discovery).to eq(
nameserver: 'localhost',
port: 8600,
record: nil,
record_type: 'A',
interval: 60,
disconnect_timeout: 120,
use_tcp: false
)
expect(config.pool_size).to eq(Gitlab::Database.default_pool_size)
end
end
context 'when load balancing is configured' do
let(:configuration_hash) do
{
pool: 4,
load_balancing: {
max_replication_difference: 1,
max_replication_lag_time: 2,
replica_check_interval: 3,
hosts: %w[foo bar],
discover: {
'record' => 'foo.example.com'
}
}
}
end
it 'uses the custom configuration settings' do
config = described_class.for_model(model)
expect(config.hosts).to eq(%w[foo bar])
expect(config.max_replication_difference).to eq(1)
expect(config.max_replication_lag_time).to eq(2.0)
expect(config.replica_check_interval).to eq(3.0)
expect(config.service_discovery).to eq(
nameserver: 'localhost',
port: 8600,
record: 'foo.example.com',
record_type: 'A',
interval: 60,
disconnect_timeout: 120,
use_tcp: false
)
expect(config.pool_size).to eq(4)
end
end
end
describe '#load_balancing_enabled?' do
it 'returns true when hosts are configured' do
config = described_class.new(ActiveRecord::Base, %w[foo bar])
expect(config.load_balancing_enabled?).to eq(true)
end
it 'returns true when a service discovery record is configured' do
config = described_class.new(ActiveRecord::Base)
config.service_discovery[:record] = 'foo'
expect(config.load_balancing_enabled?).to eq(true)
end
it 'returns false when no hosts are configured and service discovery is disabled' do
config = described_class.new(ActiveRecord::Base)
expect(config.load_balancing_enabled?).to eq(false)
end
end
describe '#service_discovery_enabled?' do
it 'returns true when a record is configured' do
config = described_class.new(ActiveRecord::Base)
config.service_discovery[:record] = 'foo'
expect(config.service_discovery_enabled?).to eq(true)
end
it 'returns false when no record is configured' do
config = described_class.new(ActiveRecord::Base)
expect(config.service_discovery_enabled?).to eq(false)
end
end
end
...@@ -4,7 +4,10 @@ require 'spec_helper' ...@@ -4,7 +4,10 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
let(:proxy) do let(:proxy) do
described_class.new(Gitlab::Database::LoadBalancing::LoadBalancer.new([])) config = Gitlab::Database::LoadBalancing::Configuration
.new(ActiveRecord::Base)
described_class.new(Gitlab::Database::LoadBalancing::LoadBalancer.new(config))
end end
describe '#select' do describe '#select' do
......
...@@ -4,7 +4,12 @@ require 'spec_helper' ...@@ -4,7 +4,12 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::HostList do RSpec.describe Gitlab::Database::LoadBalancing::HostList do
let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host } let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host }
let(:load_balancer) { double(:load_balancer) } let(:load_balancer) do
Gitlab::Database::LoadBalancing::LoadBalancer.new(
Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base)
)
end
let(:host_count) { 2 } let(:host_count) { 2 }
let(:hosts) { Array.new(host_count) { Gitlab::Database::LoadBalancing::Host.new(db_host, load_balancer, port: 5432) } } let(:hosts) { Array.new(host_count) { Gitlab::Database::LoadBalancing::Host.new(db_host, load_balancer, port: 5432) } }
let(:host_list) { described_class.new(hosts) } let(:host_list) { described_class.new(hosts) }
......
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::Host do RSpec.describe Gitlab::Database::LoadBalancing::Host do
let(:load_balancer) { Gitlab::Database::LoadBalancing::LoadBalancer.new } let(:load_balancer) do
Gitlab::Database::LoadBalancing::LoadBalancer
.new(Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base))
end
let(:host) do let(:host) do
Gitlab::Database::LoadBalancing::Host.new('localhost', load_balancer) Gitlab::Database::LoadBalancing::Host.new('localhost', load_balancer)
...@@ -274,7 +277,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Host do ...@@ -274,7 +277,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Host do
end end
it 'returns false when the data is not recent enough' do it 'returns false when the data is not recent enough' do
diff = Gitlab::Database::LoadBalancing.max_replication_difference * 2 diff = load_balancer.configuration.max_replication_difference * 2
expect(host) expect(host)
.to receive(:query_and_release) .to receive(:query_and_release)
......
...@@ -5,7 +5,12 @@ require 'spec_helper' ...@@ -5,7 +5,12 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
let(:conflict_error) { Class.new(RuntimeError) } let(:conflict_error) { Class.new(RuntimeError) }
let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host } let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host }
let(:lb) { described_class.new([db_host, db_host]) } let(:config) do
Gitlab::Database::LoadBalancing::Configuration
.new(ActiveRecord::Base, [db_host, db_host])
end
let(:lb) { described_class.new(config) }
let(:request_cache) { lb.send(:request_cache) } let(:request_cache) { lb.send(:request_cache) }
before do before do
...@@ -43,7 +48,9 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do ...@@ -43,7 +48,9 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
describe '#initialize' do describe '#initialize' do
it 'ignores the hosts when the primary_only option is enabled' do it 'ignores the hosts when the primary_only option is enabled' do
lb = described_class.new([db_host], primary_only: true) config = Gitlab::Database::LoadBalancing::Configuration
.new(ActiveRecord::Base, [db_host])
lb = described_class.new(config, primary_only: true)
hosts = lb.host_list.hosts hosts = lb.host_list.hosts
expect(hosts.length).to eq(1) expect(hosts.length).to eq(1)
...@@ -134,7 +141,9 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do ...@@ -134,7 +141,9 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
end end
it 'uses the primary when the primary_only option is enabled' do it 'uses the primary when the primary_only option is enabled' do
lb = described_class.new(primary_only: true) config = Gitlab::Database::LoadBalancing::Configuration
.new(ActiveRecord::Base)
lb = described_class.new(config, primary_only: true)
# When no hosts are configured, we don't want to produce any warnings, as # When no hosts are configured, we don't want to produce any warnings, as
# they aren't useful/too noisy. # they aren't useful/too noisy.
...@@ -174,8 +183,11 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do ...@@ -174,8 +183,11 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
end end
it 'does not create conflicts with other load balancers when caching hosts' do it 'does not create conflicts with other load balancers when caching hosts' do
lb1 = described_class.new([db_host, db_host], ActiveRecord::Base) ci_config = Gitlab::Database::LoadBalancing::Configuration
lb2 = described_class.new([db_host, db_host], Ci::CiDatabaseRecord) .new(Ci::CiDatabaseRecord, [db_host, db_host])
lb1 = described_class.new(config)
lb2 = described_class.new(ci_config)
host1 = lb1.host host1 = lb1.host
host2 = lb2.host host2 = lb2.host
......
...@@ -3,7 +3,12 @@ ...@@ -3,7 +3,12 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::PrimaryHost do RSpec.describe Gitlab::Database::LoadBalancing::PrimaryHost do
let(:load_balancer) { Gitlab::Database::LoadBalancing::LoadBalancer.new } let(:load_balancer) do
Gitlab::Database::LoadBalancing::LoadBalancer.new(
Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base)
)
end
let(:host) { Gitlab::Database::LoadBalancing::PrimaryHost.new(load_balancer) } let(:host) { Gitlab::Database::LoadBalancing::PrimaryHost.new(load_balancer) }
describe '#connection' do describe '#connection' do
......
...@@ -3,7 +3,12 @@ ...@@ -3,7 +3,12 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do
let(:load_balancer) { Gitlab::Database::LoadBalancing::LoadBalancer.new([]) } let(:load_balancer) do
Gitlab::Database::LoadBalancing::LoadBalancer.new(
Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base)
)
end
let(:service) do let(:service) do
described_class.new( described_class.new(
load_balancer, load_balancer,
...@@ -184,7 +189,10 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do ...@@ -184,7 +189,10 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do
let(:address_bar) { described_class::Address.new('bar') } let(:address_bar) { described_class::Address.new('bar') }
let(:load_balancer) do let(:load_balancer) do
Gitlab::Database::LoadBalancing::LoadBalancer.new([address_foo]) Gitlab::Database::LoadBalancing::LoadBalancer.new(
Gitlab::Database::LoadBalancing::Configuration
.new(ActiveRecord::Base, [address_foo])
)
end end
before do before do
...@@ -307,7 +315,10 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do ...@@ -307,7 +315,10 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do
describe '#addresses_from_load_balancer' do describe '#addresses_from_load_balancer' do
let(:load_balancer) do let(:load_balancer) do
Gitlab::Database::LoadBalancing::LoadBalancer.new(%w[b a]) Gitlab::Database::LoadBalancing::LoadBalancer.new(
Gitlab::Database::LoadBalancing::Configuration
.new(ActiveRecord::Base, %w[b a])
)
end end
it 'returns the ordered host names of the load balancer' do it 'returns the ordered host names of the load balancer' do
......
...@@ -40,106 +40,25 @@ RSpec.describe Gitlab::Database::LoadBalancing do ...@@ -40,106 +40,25 @@ RSpec.describe Gitlab::Database::LoadBalancing do
end end
describe '.configuration' do describe '.configuration' do
it 'returns a Hash' do it 'returns the configuration for the load balancer' do
lb_config = { 'hosts' => %w(foo) } raw = ActiveRecord::Base.connection_db_config.configuration_hash
cfg = described_class.configuration
original_db_config = Gitlab::Database.main.config # There isn't much to test here as the load balancing settings might not
modified_db_config = original_db_config.merge(load_balancing: lb_config) # (and likely aren't) set when running tests.
expect(Gitlab::Database.main).to receive(:config).and_return(modified_db_config) expect(cfg.pool_size).to eq(raw[:pool])
expect(described_class.configuration).to eq(lb_config)
end
end
describe '.max_replication_difference' do
context 'without an explicitly configured value' do
it 'returns the default value' do
allow(described_class)
.to receive(:configuration)
.and_return({})
expect(described_class.max_replication_difference).to eq(8.megabytes)
end
end
context 'with an explicitly configured value' do
it 'returns the configured value' do
allow(described_class)
.to receive(:configuration)
.and_return({ 'max_replication_difference' => 4 })
expect(described_class.max_replication_difference).to eq(4)
end
end
end
describe '.max_replication_lag_time' do
context 'without an explicitly configured value' do
it 'returns the default value' do
allow(described_class)
.to receive(:configuration)
.and_return({})
expect(described_class.max_replication_lag_time).to eq(60)
end
end
context 'with an explicitly configured value' do
it 'returns the configured value' do
allow(described_class)
.to receive(:configuration)
.and_return({ 'max_replication_lag_time' => 4 })
expect(described_class.max_replication_lag_time).to eq(4)
end
end
end
describe '.replica_check_interval' do
context 'without an explicitly configured value' do
it 'returns the default value' do
allow(described_class)
.to receive(:configuration)
.and_return({})
expect(described_class.replica_check_interval).to eq(60)
end
end
context 'with an explicitly configured value' do
it 'returns the configured value' do
allow(described_class)
.to receive(:configuration)
.and_return({ 'replica_check_interval' => 4 })
expect(described_class.replica_check_interval).to eq(4)
end
end
end
describe '.hosts' do
it 'returns a list of hosts' do
allow(described_class)
.to receive(:configuration)
.and_return({ 'hosts' => %w(foo bar baz) })
expect(described_class.hosts).to eq(%w(foo bar baz))
end
end
describe '.pool_size' do
it 'returns a Fixnum' do
expect(described_class.pool_size).to be_a_kind_of(Integer)
end end
end end
describe '.enable?' do describe '.enable?' do
before do before do
allow(described_class).to receive(:hosts).and_return(%w(foo)) allow(described_class.configuration)
.to receive(:hosts)
.and_return(%w(foo))
end end
it 'returns false when no hosts are specified' do it 'returns false when no hosts are specified' do
allow(described_class).to receive(:hosts).and_return([]) allow(described_class.configuration).to receive(:hosts).and_return([])
expect(described_class.enable?).to eq(false) expect(described_class.enable?).to eq(false)
end end
...@@ -163,10 +82,10 @@ RSpec.describe Gitlab::Database::LoadBalancing do ...@@ -163,10 +82,10 @@ RSpec.describe Gitlab::Database::LoadBalancing do
end end
it 'returns true when service discovery is enabled' do it 'returns true when service discovery is enabled' do
allow(described_class).to receive(:hosts).and_return([]) allow(described_class.configuration).to receive(:hosts).and_return([])
allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(false) allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(false)
allow(described_class) allow(described_class.configuration)
.to receive(:service_discovery_enabled?) .to receive(:service_discovery_enabled?)
.and_return(true) .and_return(true)
...@@ -175,17 +94,17 @@ RSpec.describe Gitlab::Database::LoadBalancing do ...@@ -175,17 +94,17 @@ RSpec.describe Gitlab::Database::LoadBalancing do
end end
describe '.configured?' do describe '.configured?' do
it 'returns true when Sidekiq is being used' do it 'returns true when hosts are configured' do
allow(described_class).to receive(:hosts).and_return(%w(foo)) allow(described_class.configuration)
allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true) .to receive(:hosts)
.and_return(%w[foo])
expect(described_class.configured?).to eq(true) expect(described_class.configured?).to eq(true)
end end
it 'returns true when service discovery is enabled in Sidekiq' do it 'returns true when service discovery is enabled' do
allow(described_class).to receive(:hosts).and_return([]) allow(described_class.configuration).to receive(:hosts).and_return([])
allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true) allow(described_class.configuration)
allow(described_class)
.to receive(:service_discovery_enabled?) .to receive(:service_discovery_enabled?)
.and_return(true) .and_return(true)
...@@ -193,9 +112,8 @@ RSpec.describe Gitlab::Database::LoadBalancing do ...@@ -193,9 +112,8 @@ RSpec.describe Gitlab::Database::LoadBalancing do
end end
it 'returns false when neither service discovery nor hosts are configured' do it 'returns false when neither service discovery nor hosts are configured' do
allow(described_class).to receive(:hosts).and_return([]) allow(described_class.configuration).to receive(:hosts).and_return([])
allow(described_class.configuration)
allow(described_class)
.to receive(:service_discovery_enabled?) .to receive(:service_discovery_enabled?)
.and_return(false) .and_return(false)
...@@ -219,9 +137,9 @@ RSpec.describe Gitlab::Database::LoadBalancing do ...@@ -219,9 +137,9 @@ RSpec.describe Gitlab::Database::LoadBalancing do
it 'runs initial service discovery when configuring the connection proxy' do it 'runs initial service discovery when configuring the connection proxy' do
discover = instance_spy(Gitlab::Database::LoadBalancing::ServiceDiscovery) discover = instance_spy(Gitlab::Database::LoadBalancing::ServiceDiscovery)
allow(described_class) allow(described_class.configuration)
.to receive(:configuration) .to receive(:service_discovery)
.and_return('discover' => { 'record' => 'foo' }) .and_return({ record: 'foo' })
expect(Gitlab::Database::LoadBalancing::ServiceDiscovery) expect(Gitlab::Database::LoadBalancing::ServiceDiscovery)
.to receive(:new) .to receive(:new)
...@@ -238,60 +156,6 @@ RSpec.describe Gitlab::Database::LoadBalancing do ...@@ -238,60 +156,6 @@ RSpec.describe Gitlab::Database::LoadBalancing do
end end
end end
describe '.active_record_models' do
it 'returns an Array' do
expect(described_class.active_record_models).to be_an_instance_of(Array)
end
end
describe '.service_discovery_enabled?' do
it 'returns true if service discovery is enabled' do
allow(described_class)
.to receive(:configuration)
.and_return('discover' => { 'record' => 'foo' })
expect(described_class.service_discovery_enabled?).to eq(true)
end
it 'returns false if service discovery is disabled' do
expect(described_class.service_discovery_enabled?).to eq(false)
end
end
describe '.service_discovery_configuration' do
context 'when no configuration is provided' do
it 'returns a default configuration Hash' do
expect(described_class.service_discovery_configuration).to eq(
nameserver: 'localhost',
port: 8600,
record: nil,
record_type: 'A',
interval: 60,
disconnect_timeout: 120,
use_tcp: false
)
end
end
context 'when configuration is provided' do
it 'returns a Hash including the custom configuration' do
allow(described_class)
.to receive(:configuration)
.and_return('discover' => { 'record' => 'foo', 'record_type' => 'SRV' })
expect(described_class.service_discovery_configuration).to eq(
nameserver: 'localhost',
port: 8600,
record: 'foo',
record_type: 'SRV',
interval: 60,
disconnect_timeout: 120,
use_tcp: false
)
end
end
end
describe '.start_service_discovery' do describe '.start_service_discovery' do
it 'does not start if service discovery is disabled' do it 'does not start if service discovery is disabled' do
expect(Gitlab::Database::LoadBalancing::ServiceDiscovery) expect(Gitlab::Database::LoadBalancing::ServiceDiscovery)
...@@ -301,12 +165,14 @@ RSpec.describe Gitlab::Database::LoadBalancing do ...@@ -301,12 +165,14 @@ RSpec.describe Gitlab::Database::LoadBalancing do
end end
it 'starts service discovery if enabled' do it 'starts service discovery if enabled' do
allow(described_class) allow(described_class.configuration)
.to receive(:service_discovery_enabled?) .to receive(:service_discovery_enabled?)
.and_return(true) .and_return(true)
instance = double(:instance) instance = double(:instance)
lb = Gitlab::Database::LoadBalancing::LoadBalancer.new([]) config = Gitlab::Database::LoadBalancing::Configuration
.new(ActiveRecord::Base)
lb = Gitlab::Database::LoadBalancing::LoadBalancer.new(config)
proxy = Gitlab::Database::LoadBalancing::ConnectionProxy.new(lb) proxy = Gitlab::Database::LoadBalancing::ConnectionProxy.new(lb)
allow(described_class) allow(described_class)
...@@ -345,7 +211,12 @@ RSpec.describe Gitlab::Database::LoadBalancing do ...@@ -345,7 +211,12 @@ RSpec.describe Gitlab::Database::LoadBalancing do
context 'when the load balancing is configured' do context 'when the load balancing is configured' do
let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host } let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host }
let(:load_balancer) { described_class::LoadBalancer.new([db_host]) } let(:config) do
Gitlab::Database::LoadBalancing::Configuration
.new(ActiveRecord::Base, [db_host])
end
let(:load_balancer) { described_class::LoadBalancer.new(config) }
let(:proxy) { described_class::ConnectionProxy.new(load_balancer) } let(:proxy) { described_class::ConnectionProxy.new(load_balancer) }
context 'when a proxy connection is used' do context 'when a proxy connection is used' do
......
...@@ -4,8 +4,9 @@ RSpec.configure do |config| ...@@ -4,8 +4,9 @@ RSpec.configure do |config|
config.before(:each, :db_load_balancing) do config.before(:each, :db_load_balancing) do
allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true) allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
lb = ::Gitlab::Database::LoadBalancing::LoadBalancer config = Gitlab::Database::LoadBalancing::Configuration
.new([Gitlab::Database.main.config['host']]) .new(ActiveRecord::Base, [Gitlab::Database.main.config['host']])
lb = ::Gitlab::Database::LoadBalancing::LoadBalancer.new(config)
proxy = ::Gitlab::Database::LoadBalancing::ConnectionProxy.new(lb) proxy = ::Gitlab::Database::LoadBalancing::ConnectionProxy.new(lb)
allow(ActiveRecord::Base).to receive(:load_balancing_proxy).and_return(proxy) allow(ActiveRecord::Base).to receive(:load_balancing_proxy).and_return(proxy)
......
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