Commit cd2b2bbf authored by Kamil Trzciński's avatar Kamil Trzciński

Remove `Gitlab::Database#create_connection_pool` and provide LB one

This changes how, and where the connection pool is created.
Since, we need Connection Pool be created in a context of LB
this moves this method to LB context, and properly mutates
config to also indicate a spec name, and replica usage.
parent f8107c11
...@@ -152,20 +152,6 @@ module Gitlab ...@@ -152,20 +152,6 @@ module Gitlab
end end
end end
# pool_size - The size of the DB pool.
# host - An optional host name to use instead of the default one.
# port - An optional port to connect to.
def create_connection_pool(pool_size, host = nil, port = nil)
original_config = config
env_config = original_config.merge(pool: pool_size)
env_config[:host] = host if host
env_config[:port] = port if port
ActiveRecord::ConnectionAdapters::ConnectionHandler
.new.establish_connection(env_config)
end
def cached_column_exists?(table_name, column_name) def cached_column_exists?(table_name, column_name)
connection connection
.schema_cache.columns_hash(table_name) .schema_cache.columns_hash(table_name)
......
...@@ -29,7 +29,7 @@ module Gitlab ...@@ -29,7 +29,7 @@ module Gitlab
@host = host @host = host
@port = port @port = port
@load_balancer = load_balancer @load_balancer = load_balancer
@pool = Database.main.create_connection_pool(LoadBalancing.pool_size, host, port) @pool = load_balancer.create_replica_connection_pool(LoadBalancing.pool_size, host, port)
@online = true @online = true
@last_checked_at = Time.zone.now @last_checked_at = Time.zone.now
...@@ -41,7 +41,7 @@ module Gitlab ...@@ -41,7 +41,7 @@ module Gitlab
# #
# timeout - The time after which the pool should be forcefully # timeout - The time after which the pool should be forcefully
# disconnected. # disconnected.
def disconnect!(timeout = 120) def disconnect!(timeout: 120)
start_time = ::Gitlab::Metrics::System.monotonic_time start_time = ::Gitlab::Metrics::System.monotonic_time
while (::Gitlab::Metrics::System.monotonic_time - start_time) <= timeout while (::Gitlab::Metrics::System.monotonic_time - start_time) <= timeout
......
...@@ -14,10 +14,14 @@ module Gitlab ...@@ -14,10 +14,14 @@ module Gitlab
# hosts - The hostnames/addresses of the additional databases. # hosts - The hostnames/addresses of the additional databases.
def initialize(hosts = [], model = ActiveRecord::Base) def initialize(hosts = [], model = ActiveRecord::Base)
@model = model
@host_list = HostList.new(hosts.map { |addr| Host.new(addr, self) }) @host_list = HostList.new(hosts.map { |addr| Host.new(addr, self) })
@connection_db_roles = {}.compare_by_identity @connection_db_roles = {}.compare_by_identity
@connection_db_roles_count = {}.compare_by_identity @connection_db_roles_count = {}.compare_by_identity
@model = model end
def disconnect!(timeout: 120)
host_list.hosts.each { |host| host.disconnect!(timeout: timeout) }
end end
# Yields a connection that can be used for reads. # Yields a connection that can be used for reads.
...@@ -205,6 +209,30 @@ module Gitlab ...@@ -205,6 +209,30 @@ module Gitlab
end end
end end
# pool_size - The size of the DB pool.
# host - An optional host name to use instead of the default one.
# port - An optional port to connect to.
def create_replica_connection_pool(pool_size, host = nil, port = nil)
db_config = pool.db_config
env_config = db_config.configuration_hash.dup
env_config[:pool] = pool_size
env_config[:host] = host if host
env_config[:port] = port if port
replica_db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(
db_config.env_name,
db_config.name + "_replica",
env_config
)
# We cannot use ActiveRecord::Base.connection_handler.establish_connection
# as it will rewrite ActiveRecord::Base.connection
ActiveRecord::ConnectionAdapters::ConnectionHandler
.new
.establish_connection(replica_db_config)
end
private private
# ActiveRecord::ConnectionAdapters::ConnectionHandler handles fetching, # ActiveRecord::ConnectionAdapters::ConnectionHandler handles fetching,
......
...@@ -120,7 +120,7 @@ module Gitlab ...@@ -120,7 +120,7 @@ module Gitlab
# host/connection. While this connection will be checked in and out, # host/connection. While this connection will be checked in and out,
# it won't be explicitly disconnected. # it won't be explicitly disconnected.
old_hosts.each do |host| old_hosts.each do |host|
host.disconnect!(disconnect_timeout) host.disconnect!(timeout: disconnect_timeout)
end end
end end
......
...@@ -378,42 +378,6 @@ RSpec.describe Gitlab::Database::Connection do ...@@ -378,42 +378,6 @@ RSpec.describe Gitlab::Database::Connection do
end end
end end
describe '#create_connection_pool' do
it 'creates a new connection pool with specific pool size' do
pool = connection.create_connection_pool(5)
begin
expect(pool)
.to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
expect(pool.db_config.pool).to eq(5)
ensure
pool.disconnect!
end
end
it 'allows setting of a custom hostname' do
pool = connection.create_connection_pool(5, '127.0.0.1')
begin
expect(pool.db_config.host).to eq('127.0.0.1')
ensure
pool.disconnect!
end
end
it 'allows setting of a custom hostname and port' do
pool = connection.create_connection_pool(5, '127.0.0.1', 5432)
begin
expect(pool.db_config.host).to eq('127.0.0.1')
expect(pool.db_config.configuration_hash[:port]).to eq(5432)
ensure
pool.disconnect!
end
end
end
describe '#cached_column_exists?' do describe '#cached_column_exists?' do
it 'only retrieves data once' do it 'only retrieves data once' do
expect(connection.scope.connection) expect(connection.scope.connection)
......
...@@ -3,25 +3,17 @@ ...@@ -3,25 +3,17 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::HostList do RSpec.describe Gitlab::Database::LoadBalancing::HostList do
def expect_metrics(hosts) let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host }
expect(Gitlab::Metrics.registry.get(:db_load_balancing_hosts).get({})).to eq(hosts)
end
before do
allow(Gitlab::Database.main)
.to receive(:create_connection_pool)
.and_return(ActiveRecord::Base.connection_pool)
end
let(:load_balancer) { double(:load_balancer) } let(:load_balancer) { double(:load_balancer) }
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(:host_list) { described_class.new(hosts) }
let(:host_list) do before do
hosts = Array.new(host_count) do # each call generate a new replica pool
Gitlab::Database::LoadBalancing::Host.new('localhost', load_balancer, port: 5432) allow(load_balancer).to receive(:create_replica_connection_pool) do
double(:replica_connection_pool)
end end
described_class.new(hosts)
end end
describe '#initialize' do describe '#initialize' do
...@@ -42,8 +34,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::HostList do ...@@ -42,8 +34,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::HostList do
context 'with ports' do context 'with ports' do
it 'returns the host names of all hosts' do it 'returns the host names of all hosts' do
hosts = [ hosts = [
['localhost', 5432], [db_host, 5432],
['localhost', 5432] [db_host, 5432]
] ]
expect(host_list.host_names_and_ports).to eq(hosts) expect(host_list.host_names_and_ports).to eq(hosts)
...@@ -51,18 +43,12 @@ RSpec.describe Gitlab::Database::LoadBalancing::HostList do ...@@ -51,18 +43,12 @@ RSpec.describe Gitlab::Database::LoadBalancing::HostList do
end end
context 'without ports' do context 'without ports' do
let(:host_list) do let(:hosts) { Array.new(2) { Gitlab::Database::LoadBalancing::Host.new(db_host, load_balancer) } }
hosts = Array.new(2) do
Gitlab::Database::LoadBalancing::Host.new('localhost', load_balancer)
end
described_class.new(hosts)
end
it 'returns the host names of all hosts' do it 'returns the host names of all hosts' do
hosts = [ hosts = [
['localhost', nil], [db_host, nil],
['localhost', nil] [db_host, nil]
] ]
expect(host_list.host_names_and_ports).to eq(hosts) expect(host_list.host_names_and_ports).to eq(hosts)
...@@ -71,10 +57,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::HostList do ...@@ -71,10 +57,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::HostList do
end end
describe '#manage_pool?' do describe '#manage_pool?' do
before do
allow(Gitlab::Database.main).to receive(:create_connection_pool) { double(:connection) }
end
context 'when the testing pool belongs to one host of the host list' do context 'when the testing pool belongs to one host of the host list' do
it 'returns true' do it 'returns true' do
pool = host_list.hosts.first.pool pool = host_list.hosts.first.pool
...@@ -185,4 +167,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::HostList do ...@@ -185,4 +167,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::HostList do
end end
end end
end end
def expect_metrics(hosts)
expect(Gitlab::Metrics.registry.get(:db_load_balancing_hosts).get({})).to eq(hosts)
end
end end
...@@ -3,15 +3,16 @@ ...@@ -3,15 +3,16 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::Host do RSpec.describe Gitlab::Database::LoadBalancing::Host do
let(:load_balancer) do let(:load_balancer) { Gitlab::Database::LoadBalancing::LoadBalancer.new }
Gitlab::Database::LoadBalancing::LoadBalancer.new(%w[localhost])
end
let(:host) { load_balancer.host_list.hosts.first } let(:host) do
Gitlab::Database::LoadBalancing::Host.new('localhost', load_balancer)
end
before do before do
allow(Gitlab::Database.main).to receive(:create_connection_pool) allow(load_balancer).to receive(:create_replica_connection_pool) do
.and_return(ActiveRecord::Base.connection_pool) ActiveRecord::Base.connection_pool
end
end end
def raise_and_wrap(wrapper, original) def raise_and_wrap(wrapper, original)
...@@ -63,7 +64,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Host do ...@@ -63,7 +64,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Host do
expect(host.pool) expect(host.pool)
.to receive(:disconnect!) .to receive(:disconnect!)
host.disconnect!(1) host.disconnect!(timeout: 1)
end end
end end
......
...@@ -3,21 +3,22 @@ ...@@ -3,21 +3,22 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
let(:pool) { Gitlab::Database.main.create_connection_pool(2) }
let(:conflict_error) { Class.new(RuntimeError) } let(:conflict_error) { Class.new(RuntimeError) }
let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host }
let(:lb) { described_class.new(%w(localhost localhost)) } let(:lb) { described_class.new([db_host, db_host]) }
let(:request_cache) { lb.send(:request_cache) } let(:request_cache) { lb.send(:request_cache) }
before do before do
allow(Gitlab::Database.main).to receive(:create_connection_pool)
.and_return(pool)
stub_const( stub_const(
'Gitlab::Database::LoadBalancing::LoadBalancer::PG::TRSerializationFailure', 'Gitlab::Database::LoadBalancing::LoadBalancer::PG::TRSerializationFailure',
conflict_error conflict_error
) )
end end
after do |example|
lb.disconnect!(timeout: 0) unless example.metadata[:skip_disconnect]
end
def raise_and_wrap(wrapper, original) def raise_and_wrap(wrapper, original)
raise original raise original
rescue original.class rescue original.class
...@@ -239,7 +240,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do ...@@ -239,7 +240,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
context 'when the connection comes from the primary pool' do context 'when the connection comes from the primary pool' do
it 'returns :primary' do it 'returns :primary' do
connection = double(:connection) connection = double(:connection)
allow(connection).to receive(:pool).and_return(ActiveRecord::Base.connection_pool) allow(connection).to receive(:pool).and_return(lb.send(:pool))
expect(lb.db_role_for_connection(connection)).to be(:primary) expect(lb.db_role_for_connection(connection)).to be(:primary)
end end
...@@ -271,8 +272,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do ...@@ -271,8 +272,8 @@ 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(%w(localhost localhost), ActiveRecord::Base) lb1 = described_class.new([db_host, db_host], ActiveRecord::Base)
lb2 = described_class.new(%w(localhost localhost), Ci::CiDatabaseRecord) lb2 = described_class.new([db_host, db_host], Ci::CiDatabaseRecord)
host1 = lb1.host host1 = lb1.host
host2 = lb2.host host2 = lb2.host
...@@ -456,4 +457,45 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do ...@@ -456,4 +457,45 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
end end
end end
end end
describe '#create_replica_connection_pool' do
it 'creates a new connection pool with specific pool size and name' do
with_replica_pool(5, 'other_host') do |replica_pool|
expect(replica_pool)
.to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
expect(replica_pool.db_config.host).to eq('other_host')
expect(replica_pool.db_config.pool).to eq(5)
expect(replica_pool.db_config.name).to end_with("_replica")
end
end
it 'allows setting of a custom hostname and port' do
with_replica_pool(5, 'other_host', 5432) do |replica_pool|
expect(replica_pool.db_config.host).to eq('other_host')
expect(replica_pool.db_config.configuration_hash[:port]).to eq(5432)
end
end
it 'does not modify connection class pool' do
expect { with_replica_pool(5) { } }.not_to change { ActiveRecord::Base.connection_pool }
end
def with_replica_pool(*args)
pool = lb.create_replica_connection_pool(*args)
yield pool
ensure
pool&.disconnect!
end
end
describe '#disconnect!' do
it 'calls disconnect on all hosts with a timeout', :skip_disconnect do
expect_next_instances_of(Gitlab::Database::LoadBalancing::Host, 2) do |host|
expect(host).to receive(:disconnect!).with(timeout: 30)
end
lb.disconnect!(timeout: 30)
end
end
end end
...@@ -169,7 +169,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do ...@@ -169,7 +169,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do
expect(host) expect(host)
.to receive(:disconnect!) .to receive(:disconnect!)
.with(2) .with(timeout: 2)
service.replace_hosts([address_bar]) service.replace_hosts([address_bar])
end end
......
...@@ -3,10 +3,6 @@ ...@@ -3,10 +3,6 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing do RSpec.describe Gitlab::Database::LoadBalancing do
before do
stub_env('ENABLE_LOAD_BALANCING_FOR_FOSS', 'true')
end
describe '.proxy' do describe '.proxy' do
before do before do
@previous_proxy = ActiveRecord::Base.load_balancing_proxy @previous_proxy = ActiveRecord::Base.load_balancing_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