Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/database/load_balancing/sticking_spec.rb')
-rw-r--r--spec/lib/gitlab/database/load_balancing/sticking_spec.rb443
1 files changed, 229 insertions, 214 deletions
diff --git a/spec/lib/gitlab/database/load_balancing/sticking_spec.rb b/spec/lib/gitlab/database/load_balancing/sticking_spec.rb
index 1e316c55786..ff31a5cd6cb 100644
--- a/spec/lib/gitlab/database/load_balancing/sticking_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/sticking_spec.rb
@@ -11,304 +11,319 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do
Gitlab::Database::LoadBalancing::Session.clear_session
end
- describe '#stick_or_unstick_request' do
- it 'sticks or unsticks a single object and updates the Rack environment' do
- expect(sticking)
- .to receive(:unstick_or_continue_sticking)
- .with(:user, 42)
-
- env = {}
-
- sticking.stick_or_unstick_request(env, :user, 42)
-
- expect(env[Gitlab::Database::LoadBalancing::RackMiddleware::STICK_OBJECT].to_a)
- .to eq([[sticking, :user, 42]])
+ shared_examples 'sticking' do
+ before do
+ allow(ActiveRecord::Base.load_balancer)
+ .to receive(:primary_write_location)
+ .and_return('foo')
end
- it 'sticks or unsticks multiple objects and updates the Rack environment' do
- expect(sticking)
- .to receive(:unstick_or_continue_sticking)
- .with(:user, 42)
- .ordered
+ it 'sticks an entity to the primary', :aggregate_failures do
+ allow(ActiveRecord::Base.load_balancer)
+ .to receive(:primary_only?)
+ .and_return(false)
- expect(sticking)
- .to receive(:unstick_or_continue_sticking)
- .with(:runner, '123456789')
- .ordered
+ ids.each do |id|
+ expect(sticking)
+ .to receive(:set_write_location_for)
+ .with(:user, id, 'foo')
+ end
- env = {}
+ expect(Gitlab::Database::LoadBalancing::Session.current)
+ .to receive(:use_primary!)
- sticking.stick_or_unstick_request(env, :user, 42)
- sticking.stick_or_unstick_request(env, :runner, '123456789')
+ subject
+ end
- expect(env[Gitlab::Database::LoadBalancing::RackMiddleware::STICK_OBJECT].to_a).to eq(
- [
- [sticking, :user, 42],
- [sticking, :runner,
- '123456789']
- ])
+ it 'does not update the write location when no replicas are used' do
+ expect(sticking).not_to receive(:set_write_location_for)
+
+ subject
end
end
- describe '#stick_if_necessary' do
- it 'does not stick if no write was performed' do
- allow(Gitlab::Database::LoadBalancing::Session.current)
- .to receive(:performed_write?)
- .and_return(false)
+ shared_examples 'tracking status in redis' do
+ describe '#stick_or_unstick_request' do
+ it 'sticks or unsticks a single object and updates the Rack environment' do
+ expect(sticking)
+ .to receive(:unstick_or_continue_sticking)
+ .with(:user, 42)
- expect(sticking).not_to receive(:stick)
+ env = {}
- sticking.stick_if_necessary(:user, 42)
- end
+ sticking.stick_or_unstick_request(env, :user, 42)
- it 'sticks to the primary if a write was performed' do
- allow(Gitlab::Database::LoadBalancing::Session.current)
- .to receive(:performed_write?)
- .and_return(true)
+ expect(env[Gitlab::Database::LoadBalancing::RackMiddleware::STICK_OBJECT].to_a)
+ .to eq([[sticking, :user, 42]])
+ end
- expect(sticking)
- .to receive(:stick)
- .with(:user, 42)
+ it 'sticks or unsticks multiple objects and updates the Rack environment' do
+ expect(sticking)
+ .to receive(:unstick_or_continue_sticking)
+ .with(:user, 42)
+ .ordered
- sticking.stick_if_necessary(:user, 42)
- end
- end
+ expect(sticking)
+ .to receive(:unstick_or_continue_sticking)
+ .with(:runner, '123456789')
+ .ordered
- describe '#all_caught_up?' do
- let(:lb) { ActiveRecord::Base.load_balancer }
- let(:last_write_location) { 'foo' }
+ env = {}
- before do
- allow(ActiveSupport::Notifications).to receive(:instrument).and_call_original
+ sticking.stick_or_unstick_request(env, :user, 42)
+ sticking.stick_or_unstick_request(env, :runner, '123456789')
- allow(sticking)
- .to receive(:last_write_location_for)
- .with(:user, 42)
- .and_return(last_write_location)
+ expect(env[Gitlab::Database::LoadBalancing::RackMiddleware::STICK_OBJECT].to_a).to eq(
+ [
+ [sticking, :user, 42],
+ [sticking, :runner,
+ '123456789']
+ ])
+ end
end
- context 'when no write location could be found' do
- let(:last_write_location) { nil }
+ describe '#stick_if_necessary' do
+ it 'does not stick if no write was performed' do
+ allow(Gitlab::Database::LoadBalancing::Session.current)
+ .to receive(:performed_write?)
+ .and_return(false)
- it 'returns true' do
- expect(lb).not_to receive(:select_up_to_date_host)
+ expect(sticking).not_to receive(:stick)
- expect(sticking.all_caught_up?(:user, 42)).to eq(true)
+ sticking.stick_if_necessary(:user, 42)
end
- end
- context 'when all secondaries have caught up' do
- before do
- allow(lb).to receive(:select_up_to_date_host).with('foo').and_return(true)
- end
+ it 'sticks to the primary if a write was performed' do
+ allow(Gitlab::Database::LoadBalancing::Session.current)
+ .to receive(:performed_write?)
+ .and_return(true)
- it 'returns true, and unsticks' do
expect(sticking)
- .to receive(:unstick)
+ .to receive(:stick)
.with(:user, 42)
- expect(sticking.all_caught_up?(:user, 42)).to eq(true)
- end
-
- it 'notifies with the proper event payload' do
- expect(ActiveSupport::Notifications)
- .to receive(:instrument)
- .with('caught_up_replica_pick.load_balancing', { result: true })
- .and_call_original
-
- sticking.all_caught_up?(:user, 42)
+ sticking.stick_if_necessary(:user, 42)
end
end
- context 'when the secondaries have not yet caught up' do
+ describe '#all_caught_up?' do
+ let(:lb) { ActiveRecord::Base.load_balancer }
+ let(:last_write_location) { 'foo' }
+
before do
- allow(lb).to receive(:select_up_to_date_host).with('foo').and_return(false)
- end
+ allow(ActiveSupport::Notifications).to receive(:instrument).and_call_original
- it 'returns false' do
- expect(sticking.all_caught_up?(:user, 42)).to eq(false)
+ allow(sticking)
+ .to receive(:last_write_location_for)
+ .with(:user, 42)
+ .and_return(last_write_location)
end
- it 'notifies with the proper event payload' do
- expect(ActiveSupport::Notifications)
- .to receive(:instrument)
- .with('caught_up_replica_pick.load_balancing', { result: false })
- .and_call_original
+ context 'when no write location could be found' do
+ let(:last_write_location) { nil }
+
+ it 'returns true' do
+ expect(lb).not_to receive(:select_up_to_date_host)
- sticking.all_caught_up?(:user, 42)
+ expect(sticking.all_caught_up?(:user, 42)).to eq(true)
+ end
end
- end
- end
- describe '#unstick_or_continue_sticking' do
- let(:lb) { ActiveRecord::Base.load_balancer }
+ context 'when all secondaries have caught up' do
+ before do
+ allow(lb).to receive(:select_up_to_date_host).with('foo').and_return(true)
+ end
- it 'simply returns if no write location could be found' do
- allow(sticking)
- .to receive(:last_write_location_for)
- .with(:user, 42)
- .and_return(nil)
+ it 'returns true, and unsticks' do
+ expect(sticking)
+ .to receive(:unstick)
+ .with(:user, 42)
- expect(lb).not_to receive(:select_up_to_date_host)
+ expect(sticking.all_caught_up?(:user, 42)).to eq(true)
+ end
- sticking.unstick_or_continue_sticking(:user, 42)
- end
+ it 'notifies with the proper event payload' do
+ expect(ActiveSupport::Notifications)
+ .to receive(:instrument)
+ .with('caught_up_replica_pick.load_balancing', { result: true })
+ .and_call_original
- it 'unsticks if all secondaries have caught up' do
- allow(sticking)
- .to receive(:last_write_location_for)
- .with(:user, 42)
- .and_return('foo')
+ sticking.all_caught_up?(:user, 42)
+ end
+ end
- allow(lb).to receive(:select_up_to_date_host).with('foo').and_return(true)
+ context 'when the secondaries have not yet caught up' do
+ before do
+ allow(lb).to receive(:select_up_to_date_host).with('foo').and_return(false)
+ end
- expect(sticking)
- .to receive(:unstick)
- .with(:user, 42)
+ it 'returns false' do
+ expect(sticking.all_caught_up?(:user, 42)).to eq(false)
+ end
- sticking.unstick_or_continue_sticking(:user, 42)
+ it 'notifies with the proper event payload' do
+ expect(ActiveSupport::Notifications)
+ .to receive(:instrument)
+ .with('caught_up_replica_pick.load_balancing', { result: false })
+ .and_call_original
+
+ sticking.all_caught_up?(:user, 42)
+ end
+ end
end
- it 'continues using the primary if the secondaries have not yet caught up' do
- allow(sticking)
- .to receive(:last_write_location_for)
- .with(:user, 42)
- .and_return('foo')
+ describe '#unstick_or_continue_sticking' do
+ let(:lb) { ActiveRecord::Base.load_balancer }
- allow(lb).to receive(:select_up_to_date_host).with('foo').and_return(false)
+ it 'simply returns if no write location could be found' do
+ allow(sticking)
+ .to receive(:last_write_location_for)
+ .with(:user, 42)
+ .and_return(nil)
- expect(Gitlab::Database::LoadBalancing::Session.current)
- .to receive(:use_primary!)
+ expect(lb).not_to receive(:select_up_to_date_host)
- sticking.unstick_or_continue_sticking(:user, 42)
- end
- end
+ sticking.unstick_or_continue_sticking(:user, 42)
+ end
- RSpec.shared_examples 'sticking' do
- before do
- allow(ActiveRecord::Base.load_balancer)
- .to receive(:primary_write_location)
- .and_return('foo')
- end
+ it 'unsticks if all secondaries have caught up' do
+ allow(sticking)
+ .to receive(:last_write_location_for)
+ .with(:user, 42)
+ .and_return('foo')
- it 'sticks an entity to the primary', :aggregate_failures do
- allow(ActiveRecord::Base.load_balancer)
- .to receive(:primary_only?)
- .and_return(false)
+ allow(lb).to receive(:select_up_to_date_host).with('foo').and_return(true)
- ids.each do |id|
expect(sticking)
- .to receive(:set_write_location_for)
- .with(:user, id, 'foo')
+ .to receive(:unstick)
+ .with(:user, 42)
+
+ sticking.unstick_or_continue_sticking(:user, 42)
end
- expect(Gitlab::Database::LoadBalancing::Session.current)
- .to receive(:use_primary!)
+ it 'continues using the primary if the secondaries have not yet caught up' do
+ allow(sticking)
+ .to receive(:last_write_location_for)
+ .with(:user, 42)
+ .and_return('foo')
- subject
- end
+ allow(lb).to receive(:select_up_to_date_host).with('foo').and_return(false)
- it 'does not update the write location when no replicas are used' do
- expect(sticking).not_to receive(:set_write_location_for)
+ expect(Gitlab::Database::LoadBalancing::Session.current)
+ .to receive(:use_primary!)
- subject
+ sticking.unstick_or_continue_sticking(:user, 42)
+ end
end
- end
- describe '#stick' do
- it_behaves_like 'sticking' do
- let(:ids) { [42] }
- subject { sticking.stick(:user, ids.first) }
+ describe '#stick' do
+ it_behaves_like 'sticking' do
+ let(:ids) { [42] }
+ subject { sticking.stick(:user, ids.first) }
+ end
end
- end
- describe '#bulk_stick' do
- it_behaves_like 'sticking' do
- let(:ids) { [42, 43] }
- subject { sticking.bulk_stick(:user, ids) }
+ describe '#bulk_stick' do
+ it_behaves_like 'sticking' do
+ let(:ids) { [42, 43] }
+ subject { sticking.bulk_stick(:user, ids) }
+ end
end
- end
- describe '#mark_primary_write_location' do
- it 'updates the write location with the load balancer' do
- allow(ActiveRecord::Base.load_balancer)
- .to receive(:primary_write_location)
- .and_return('foo')
+ describe '#mark_primary_write_location' do
+ it 'updates the write location with the load balancer' do
+ allow(ActiveRecord::Base.load_balancer)
+ .to receive(:primary_write_location)
+ .and_return('foo')
- allow(ActiveRecord::Base.load_balancer)
- .to receive(:primary_only?)
- .and_return(false)
+ allow(ActiveRecord::Base.load_balancer)
+ .to receive(:primary_only?)
+ .and_return(false)
+
+ expect(sticking)
+ .to receive(:set_write_location_for)
+ .with(:user, 42, 'foo')
+
+ sticking.mark_primary_write_location(:user, 42)
+ end
- expect(sticking)
- .to receive(:set_write_location_for)
- .with(:user, 42, 'foo')
+ it 'does nothing when no replicas are used' do
+ expect(sticking).not_to receive(:set_write_location_for)
- sticking.mark_primary_write_location(:user, 42)
+ sticking.mark_primary_write_location(:user, 42)
+ end
end
- it 'does nothing when no replicas are used' do
- expect(sticking).not_to receive(:set_write_location_for)
+ describe '#unstick' do
+ it 'removes the sticking data from Redis' do
+ sticking.set_write_location_for(:user, 4, 'foo')
+ sticking.unstick(:user, 4)
- sticking.mark_primary_write_location(:user, 42)
+ expect(sticking.last_write_location_for(:user, 4)).to be_nil
+ end
end
- end
- describe '#unstick' do
- it 'removes the sticking data from Redis' do
- sticking.set_write_location_for(:user, 4, 'foo')
- sticking.unstick(:user, 4)
+ describe '#last_write_location_for' do
+ it 'returns the last WAL write location for a user' do
+ sticking.set_write_location_for(:user, 4, 'foo')
- expect(sticking.last_write_location_for(:user, 4)).to be_nil
+ expect(sticking.last_write_location_for(:user, 4)).to eq('foo')
+ end
end
- end
- describe '#last_write_location_for' do
- it 'returns the last WAL write location for a user' do
- sticking.set_write_location_for(:user, 4, 'foo')
+ describe '#select_caught_up_replicas' do
+ let(:lb) { ActiveRecord::Base.load_balancer }
+
+ context 'with no write location' do
+ before do
+ allow(sticking)
+ .to receive(:last_write_location_for)
+ .with(:project, 42)
+ .and_return(nil)
+ end
+
+ it 'returns false and does not try to find caught up hosts' do
+ expect(lb).not_to receive(:select_up_to_date_host)
+ expect(sticking.select_caught_up_replicas(:project, 42)).to be false
+ end
+ end
- expect(sticking.last_write_location_for(:user, 4)).to eq('foo')
+ context 'with write location' do
+ before do
+ allow(sticking)
+ .to receive(:last_write_location_for)
+ .with(:project, 42)
+ .and_return('foo')
+ end
+
+ it 'returns true, selects hosts, and unsticks if any secondary has caught up' do
+ expect(lb).to receive(:select_up_to_date_host).and_return(true)
+ expect(sticking)
+ .to receive(:unstick)
+ .with(:project, 42)
+ expect(sticking.select_caught_up_replicas(:project, 42)).to be true
+ end
+ end
end
end
- describe '#redis_key_for' do
- it 'returns a String' do
- expect(sticking.redis_key_for(:user, 42))
- .to eq('database-load-balancing/write-location/main/user/42')
- end
+ context 'with multi-store feature flags turned on' do
+ it_behaves_like 'tracking status in redis'
end
- describe '#select_caught_up_replicas' do
- let(:lb) { ActiveRecord::Base.load_balancer }
-
- context 'with no write location' do
- before do
- allow(sticking)
- .to receive(:last_write_location_for)
- .with(:project, 42)
- .and_return(nil)
- end
-
- it 'returns false and does not try to find caught up hosts' do
- expect(lb).not_to receive(:select_up_to_date_host)
- expect(sticking.select_caught_up_replicas(:project, 42)).to be false
- end
+ context 'when both multi-store feature flags are off' do
+ before do
+ stub_feature_flags(use_primary_and_secondary_stores_for_db_load_balancing: false)
+ stub_feature_flags(use_primary_store_as_default_for_db_load_balancing: false)
end
- context 'with write location' do
- before do
- allow(sticking)
- .to receive(:last_write_location_for)
- .with(:project, 42)
- .and_return('foo')
- end
+ it_behaves_like 'tracking status in redis'
+ end
- it 'returns true, selects hosts, and unsticks if any secondary has caught up' do
- expect(lb).to receive(:select_up_to_date_host).and_return(true)
- expect(sticking)
- .to receive(:unstick)
- .with(:project, 42)
- expect(sticking.select_caught_up_replicas(:project, 42)).to be true
- end
+ describe '#redis_key_for' do
+ it 'returns a String' do
+ expect(sticking.redis_key_for(:user, 42))
+ .to eq('database-load-balancing/write-location/main/user/42')
end
end
end