Commit 6f706bb0 authored by Adam Hegyi's avatar Adam Hegyi

Merge branch 'fix-establish_connection-in-specs' into 'master'

Add `#with_reestablished_active_record_base` to allow usage of `establish_connection`

See merge request gitlab-org/gitlab!70606
parents 4c93874c a049dd5b
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Geo::DatabaseTasks do RSpec.describe Gitlab::Geo::DatabaseTasks, :reestablished_active_record_base do
let(:schema_file) { Rails.root.join('tmp', 'tests', 'geo_schema.rb').to_s } let(:schema_file) { Rails.root.join('tmp', 'tests', 'geo_schema.rb').to_s }
subject { described_class } subject { described_class }
......
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe SystemCheck::Geo::GeoDatabaseConfiguredCheck, :silence_stdout do RSpec.describe SystemCheck::Geo::GeoDatabaseConfiguredCheck, :silence_stdout do
subject { described_class.new } subject { described_class.new }
describe '#multi_check' do describe '#multi_check', :reestablished_active_record_base do
it "checks database configuration" do it "checks database configuration" do
stub_configuration_check(false) stub_configuration_check(false)
......
...@@ -235,7 +235,7 @@ module Gitlab ...@@ -235,7 +235,7 @@ module Gitlab
@configuration.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
) ) || raise(::ActiveRecord::ConnectionNotEstablished)
end end
private private
......
...@@ -2,19 +2,11 @@ ...@@ -2,19 +2,11 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Database config initializer' do RSpec.describe 'Database config initializer', :reestablished_active_record_base do
subject do subject do
load Rails.root.join('config/initializers/database_config.rb') load Rails.root.join('config/initializers/database_config.rb')
end end
around do |example|
original_config = ActiveRecord::Base.connection_db_config
example.run
ActiveRecord::Base.establish_connection(original_config)
end
before do before do
allow(Gitlab::Runtime).to receive(:max_threads).and_return(max_threads) allow(Gitlab::Runtime).to receive(:max_threads).and_return(max_threads)
end end
......
...@@ -91,45 +91,38 @@ RSpec.describe Gitlab::Database::BulkUpdate do ...@@ -91,45 +91,38 @@ RSpec.describe Gitlab::Database::BulkUpdate do
.to eq(['MR a', 'Issue a', 'Issue b']) .to eq(['MR a', 'Issue a', 'Issue b'])
end end
shared_examples 'basic functionality' do context 'validates prepared_statements support', :reestablished_active_record_base do
it 'sets multiple values' do using RSpec::Parameterized::TableSyntax
create_default(:user)
create_default(:project)
i_a, i_b = create_list(:issue, 2)
mapping = { where(:prepared_statements) do
i_a => { title: 'Issue a' }, [false, true]
i_b => { title: 'Issue b' } end
}
described_class.execute(%i[title], mapping) before do
configuration_hash = ActiveRecord::Base.connection_db_config.configuration_hash
expect([i_a, i_b].map { |x| x.reset.title }) ActiveRecord::Base.establish_connection(
.to eq(['Issue a', 'Issue b']) configuration_hash.merge(prepared_statements: prepared_statements)
)
end end
end
include_examples 'basic functionality' with_them do
it 'sets multiple values' do
create_default(:user)
create_default(:project)
context 'when prepared statements are configured differently to the normal test environment' do i_a, i_b = create_list(:issue, 2)
before do
klass = Class.new(ActiveRecord::Base) do
def self.abstract_class?
true # So it gets its own connection
end
end
stub_const('ActiveRecordBasePreparedStatementsInverted', klass) mapping = {
i_a => { title: 'Issue a' },
i_b => { title: 'Issue b' }
}
c = ActiveRecord::Base.retrieve_connection.instance_variable_get(:@config) described_class.execute(%i[title], mapping)
inverted = c.merge(prepared_statements: !ActiveRecord::Base.connection.prepared_statements)
ActiveRecordBasePreparedStatementsInverted.establish_connection(inverted)
allow(ActiveRecord::Base).to receive(:connection_specification_name) expect([i_a, i_b].map { |x| x.reset.title })
.and_return(ActiveRecordBasePreparedStatementsInverted.connection_specification_name) .to eq(['Issue a', 'Issue b'])
end
end end
include_examples 'basic functionality'
end end
end end
...@@ -126,15 +126,7 @@ RSpec.describe Gitlab::Database::Connection do ...@@ -126,15 +126,7 @@ RSpec.describe Gitlab::Database::Connection do
end end
end end
describe '#disable_prepared_statements' do describe '#disable_prepared_statements', :reestablished_active_record_base do
around do |example|
original_config = connection.scope.connection.pool.db_config
example.run
connection.scope.establish_connection(original_config)
end
it 'disables prepared statements' do it 'disables prepared statements' do
connection.scope.establish_connection( connection.scope.establish_connection(
::Gitlab::Database.main.config.merge(prepared_statements: true) ::Gitlab::Database.main.config.merge(prepared_statements: true)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::PostgresqlAdapter::ForceDisconnectableMixin do RSpec.describe Gitlab::Database::PostgresqlAdapter::ForceDisconnectableMixin, :reestablished_active_record_base do
describe 'checking in a connection to the pool' do describe 'checking in a connection to the pool' do
let(:model) do let(:model) do
Class.new(ActiveRecord::Base) do Class.new(ActiveRecord::Base) do
......
...@@ -23,7 +23,7 @@ RSpec.describe Gitlab::Database::SchemaMigrations::Context do ...@@ -23,7 +23,7 @@ RSpec.describe Gitlab::Database::SchemaMigrations::Context do
end end
end end
context 'multiple databases' do context 'multiple databases', :reestablished_active_record_base do
let(:connection_class) do let(:connection_class) do
Class.new(::ApplicationRecord) do Class.new(::ApplicationRecord) do
self.abstract_class = true self.abstract_class = true
...@@ -34,8 +34,6 @@ RSpec.describe Gitlab::Database::SchemaMigrations::Context do ...@@ -34,8 +34,6 @@ RSpec.describe Gitlab::Database::SchemaMigrations::Context do
end end
end end
let(:configuration_overrides) { {} }
before do before do
connection_class.establish_connection( connection_class.establish_connection(
ActiveRecord::Base ActiveRecord::Base
......
...@@ -5,5 +5,57 @@ module Database ...@@ -5,5 +5,57 @@ module Database
def skip_if_multiple_databases_not_setup def skip_if_multiple_databases_not_setup
skip 'Skipping because multiple databases not set up' unless Gitlab::Database.has_config?(:ci) skip 'Skipping because multiple databases not set up' unless Gitlab::Database.has_config?(:ci)
end end
# The usage of this method switches temporarily used `connection_handler`
# allowing full manipulation of ActiveRecord::Base connections without
# having side effects like:
# - misaligned transactions since this is managed by `BeforeAllAdapter`
# - removal of primary connections
#
# The execution within a block ensures safe cleanup of all allocated resources.
#
# rubocop:disable Database/MultipleDatabases
def with_reestablished_active_record_base(reconnect: true)
connection_classes = ActiveRecord::Base.connection_handler.connection_pool_names.map(&:constantize).to_h do |klass|
[klass, klass.connection_db_config]
end
original_handler = ActiveRecord::Base.connection_handler
new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
ActiveRecord::Base.connection_handler = new_handler
if reconnect
connection_classes.each { |klass, db_config| klass.establish_connection(db_config) }
end
yield
ensure
ActiveRecord::Base.connection_handler = original_handler
new_handler&.clear_all_connections!
end
# rubocop:enable Database/MultipleDatabases
end
module ActiveRecordBaseEstablishConnection
def establish_connection(*args)
# rubocop:disable Database/MultipleDatabases
if connected? && connection&.transaction_open? && ActiveRecord::Base.connection_handler == ActiveRecord::Base.default_connection_handler
raise "Cannot re-establish '#{self}.establish_connection' within an open transaction (#{connection&.open_transactions.to_i}). " \
"Use `with_reestablished_active_record_base` instead or add `:reestablished_active_record_base` to rspec context."
end
# rubocop:enable Database/MultipleDatabases
super
end
end end
end end
RSpec.configure do |config|
config.around(:each, :reestablished_active_record_base) do |example|
with_reestablished_active_record_base(reconnect: example.metadata.fetch(:reconnect, true)) do
example.run
end
end
end
ActiveRecord::Base.singleton_class.prepend(::Database::ActiveRecordBaseEstablishConnection) # rubocop:disable Database/MultipleDatabases
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Database::MultipleDatabases' do
describe '.with_reestablished_active_record_base' do
context 'when doing establish_connection' do
context 'on ActiveRecord::Base' do
it 'raises exception' do
expect { ActiveRecord::Base.establish_connection(:main) }.to raise_error /Cannot re-establish/
end
context 'when using with_reestablished_active_record_base' do
it 'does not raise exception' do
with_reestablished_active_record_base do
expect { ActiveRecord::Base.establish_connection(:main) }.not_to raise_error
end
end
end
end
context 'on Ci::CiDatabaseRecord' do
before do
skip_if_multiple_databases_not_setup
end
it 'raises exception' do
expect { Ci::CiDatabaseRecord.establish_connection(:ci) }.to raise_error /Cannot re-establish/
end
context 'when using with_reestablished_active_record_base' do
it 'does not raise exception' do
with_reestablished_active_record_base do
expect { Ci::CiDatabaseRecord.establish_connection(:main) }.not_to raise_error
end
end
end
end
end
context 'when trying to access connection' do
context 'when reconnect is true' do
it 'does not raise exception' do
with_reestablished_active_record_base(reconnect: true) do
expect { ActiveRecord::Base.connection.execute("SELECT 1") }.not_to raise_error # rubocop:disable Database/MultipleDatabases
end
end
end
context 'when reconnect is false' do
it 'does raise exception' do
with_reestablished_active_record_base(reconnect: false) do
expect { ActiveRecord::Base.connection.execute("SELECT 1") }.to raise_error(ActiveRecord::ConnectionNotEstablished) # rubocop:disable Database/MultipleDatabases
end
end
end
end
end
end
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