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/redis/multi_store_spec.rb')
-rw-r--r--spec/lib/gitlab/redis/multi_store_spec.rb421
1 files changed, 124 insertions, 297 deletions
diff --git a/spec/lib/gitlab/redis/multi_store_spec.rb b/spec/lib/gitlab/redis/multi_store_spec.rb
index 1745a745ec3..6b1c0fb2e81 100644
--- a/spec/lib/gitlab/redis/multi_store_spec.rb
+++ b/spec/lib/gitlab/redis/multi_store_spec.rb
@@ -3,7 +3,6 @@
require 'spec_helper'
RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
- using RSpec::Parameterized::TableSyntax
include RedisHelpers
let_it_be(:redis_store_class) { define_helper_redis_store_class }
@@ -56,7 +55,6 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
context 'when primary_store is not a ::Redis instance' do
before do
allow(primary_store).to receive(:is_a?).with(::Redis).and_return(false)
- allow(primary_store).to receive(:is_a?).with(::Redis::Namespace).and_return(false)
end
it 'fails with exception' do
@@ -65,21 +63,9 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
- context 'when primary_store is a ::Redis::Namespace instance' do
- before do
- allow(primary_store).to receive(:is_a?).with(::Redis).and_return(false)
- allow(primary_store).to receive(:is_a?).with(::Redis::Namespace).and_return(true)
- end
-
- it 'fails with exception' do
- expect { described_class.new(primary_store, secondary_store, instance_name) }.not_to raise_error
- end
- end
-
context 'when secondary_store is not a ::Redis instance' do
before do
allow(secondary_store).to receive(:is_a?).with(::Redis).and_return(false)
- allow(secondary_store).to receive(:is_a?).with(::Redis::Namespace).and_return(false)
end
it 'fails with exception' do
@@ -88,130 +74,22 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
- context 'when secondary_store is a ::Redis::Namespace instance' do
- before do
- allow(secondary_store).to receive(:is_a?).with(::Redis).and_return(false)
- allow(secondary_store).to receive(:is_a?).with(::Redis::Namespace).and_return(true)
- end
-
- it 'fails with exception' do
- expect { described_class.new(primary_store, secondary_store, instance_name) }.not_to raise_error
- end
- end
-
# rubocop:disable RSpec/MultipleMemoizedHelpers
context 'with READ redis commands' do
subject do
multi_store.send(name, *args, **kwargs)
end
- let_it_be(:key1) { "redis:{1}:key_a" }
- let_it_be(:key2) { "redis:{1}:key_b" }
- let_it_be(:value1) { "redis_value1" }
- let_it_be(:value2) { "redis_value2" }
- let_it_be(:skey) { "redis:set:key" }
- let_it_be(:skey2) { "redis:set:key2" }
- let_it_be(:smemberargs) { [skey, value1] }
- let_it_be(:hkey) { "redis:hash:key" }
- let_it_be(:hkey2) { "redis:hash:key2" }
- let_it_be(:zkey) { "redis:sortedset:key" }
- let_it_be(:zkey2) { "redis:sortedset:key2" }
- let_it_be(:hitem1) { "item1" }
- let_it_be(:hitem2) { "item2" }
- let_it_be(:keys) { [key1, key2] }
- let_it_be(:values) { [value1, value2] }
- let_it_be(:svalues) { [value2, value1] }
- let_it_be(:hgetargs) { [hkey, hitem1] }
- let_it_be(:hmgetval) { [value1] }
- let_it_be(:mhmgetargs) { [hkey, hitem1] }
- let_it_be(:hvalmapped) { { "item1" => value1 } }
- let_it_be(:sscanargs) { [skey2, 0] }
- let_it_be(:sscanval) { ["0", [value1]] }
- let_it_be(:scanargs) { ["0"] }
- let_it_be(:scankwargs) { { match: '*:set:key2*' } }
- let_it_be(:scanval) { ["0", [skey2]] }
- let_it_be(:sscan_eachval) { [value1] }
- let_it_be(:sscan_each_arg) { { match: '*1*' } }
- let_it_be(:hscan_eachval) { [[hitem1, value1]] }
- let_it_be(:zscan_eachval) { [[value1, 1.0]] }
- let_it_be(:scan_each_arg) { { match: 'redis*' } }
- let_it_be(:scan_each_val) { [key1, key2, skey, skey2, hkey, hkey2, zkey, zkey2] }
-
- # rubocop:disable Layout/LineLength
- where(:case_name, :name, :args, :value, :kwargs, :block) do
- 'execute :get command' | :get | ref(:key1) | ref(:value1) | {} | nil
- 'execute :mget command' | :mget | ref(:keys) | ref(:values) | {} | nil
- 'execute :mget with block' | :mget | ref(:keys) | ref(:values) | {} | ->(value) { value }
- 'execute :smembers command' | :smembers | ref(:skey) | ref(:svalues) | {} | nil
- 'execute :scard command' | :scard | ref(:skey) | 2 | {} | nil
- 'execute :sismember command' | :sismember | ref(:smemberargs) | true | {} | nil
- 'execute :exists command' | :exists | ref(:key1) | 1 | {} | nil
- 'execute :exists? command' | :exists? | ref(:key1) | true | {} | nil
- 'execute :hget command' | :hget | ref(:hgetargs) | ref(:value1) | {} | nil
- 'execute :hlen command' | :hlen | ref(:hkey) | 1 | {} | nil
- 'execute :hgetall command' | :hgetall | ref(:hkey) | ref(:hvalmapped) | {} | nil
- 'execute :hexists command' | :hexists | ref(:hgetargs) | true | {} | nil
- 'execute :hmget command' | :hmget | ref(:hgetargs) | ref(:hmgetval) | {} | nil
- 'execute :mapped_hmget command' | :mapped_hmget | ref(:mhmgetargs) | ref(:hvalmapped) | {} | nil
- 'execute :sscan command' | :sscan | ref(:sscanargs) | ref(:sscanval) | {} | nil
- 'execute :scan command' | :scan | ref(:scanargs) | ref(:scanval) | ref(:scankwargs) | nil
-
- # we run *scan_each here as they are reads too
- 'execute :scan_each command' | :scan_each | nil | ref(:scan_each_val) | ref(:scan_each_arg) | nil
- 'execute :sscan_each command' | :sscan_each | ref(:skey2) | ref(:sscan_eachval) | {} | nil
- 'execute :sscan_each w block' | :sscan_each | ref(:skey) | ref(:sscan_eachval) | ref(:sscan_each_arg) | nil
- 'execute :hscan_each command' | :hscan_each | ref(:hkey) | ref(:hscan_eachval) | {} | nil
- 'execute :hscan_each w block' | :hscan_each | ref(:hkey2) | ref(:hscan_eachval) | ref(:sscan_each_arg) | nil
- 'execute :zscan_each command' | :zscan_each | ref(:zkey) | ref(:zscan_eachval) | {} | nil
- 'execute :zscan_each w block' | :zscan_each | ref(:zkey2) | ref(:zscan_eachval) | ref(:sscan_each_arg) | nil
- end
- # rubocop:enable Layout/LineLength
-
- before do
- primary_store.set(key1, value1)
- primary_store.set(key2, value2)
- primary_store.sadd?(skey, [value1, value2])
- primary_store.sadd?(skey2, [value1])
- primary_store.hset(hkey, hitem1, value1)
- primary_store.hset(hkey2, hitem1, value1, hitem2, value2)
- primary_store.zadd(zkey, 1, value1)
- primary_store.zadd(zkey2, [[1, value1], [2, value2]])
-
- secondary_store.set(key1, value1)
- secondary_store.set(key2, value2)
- secondary_store.sadd?(skey, [value1, value2])
- secondary_store.sadd?(skey2, [value1])
- secondary_store.hset(hkey, hitem1, value1)
- secondary_store.hset(hkey2, hitem1, value1, hitem2, value2)
- secondary_store.zadd(zkey, 1, value1)
- secondary_store.zadd(zkey2, [[1, value1], [2, value2]])
- end
-
- after do
- primary_store.flushdb
- secondary_store.flushdb
- end
-
- RSpec.shared_examples_for 'reads correct value' do
- it 'returns the correct value' do
- if value.is_a?(Array)
- # :smembers does not guarantee the order it will return the values (unsorted set)
- is_expected.to match_array(value)
- else
- is_expected.to eq(value)
- end
- end
- end
+ let(:args) { 'args' }
+ let(:kwargs) { { match: '*:set:key2*' } }
RSpec.shared_examples_for 'secondary store' do
it 'execute on the secondary instance' do
- expect(secondary_store).to receive(name).with(*expected_args).and_call_original
+ expect(secondary_store).to receive(name).with(*expected_args)
subject
end
- include_examples 'reads correct value'
-
it 'does not execute on the primary store' do
expect(primary_store).not_to receive(name)
@@ -219,23 +97,22 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
- with_them do
+ described_class::READ_COMMANDS.each do |name|
describe name.to_s do
- let(:expected_args) { kwargs&.present? ? [*args, { **kwargs }] : Array(args) }
+ let(:expected_args) { [*args, { **kwargs }] }
+ let(:name) { name }
before do
- allow(primary_store).to receive(name).and_call_original
- allow(secondary_store).to receive(name).and_call_original
+ allow(primary_store).to receive(name)
+ allow(secondary_store).to receive(name)
end
context 'when reading from the primary is successful' do
it 'returns the correct value' do
- expect(primary_store).to receive(name).with(*expected_args).and_call_original
+ expect(primary_store).to receive(name).with(*expected_args)
subject
end
-
- include_examples 'reads correct value'
end
context 'when reading from default instance is raising an exception' do
@@ -246,23 +123,13 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
it 'logs the exception and re-raises the error' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
- hash_including(:multi_store_error_message, instance_name: instance_name, command_name: name))
+ hash_including(:multi_store_error_message,
+ instance_name: instance_name, command_name: name))
expect { subject }.to raise_error(an_instance_of(StandardError))
end
end
- context 'when reading from empty default instance' do
- before do
- # this ensures a cache miss without having to stub the default store
- multi_store.default_store.flushdb
- end
-
- it 'does not call the non_default_store' do
- expect(multi_store.non_default_store).not_to receive(name)
- end
- end
-
context 'when the command is executed within pipelined block' do
subject do
multi_store.pipelined do |pipeline|
@@ -276,7 +143,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
2.times do
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
- expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
+ expect(pipeline).to receive(name).with(*expected_args).once
end
end
@@ -284,27 +151,16 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
- if params[:block]
+ context 'when block provided' do
subject do
- multi_store.send(name, *expected_args, &block)
+ multi_store.send(name, expected_args) { nil }
end
- context 'when block is provided' do
- it 'only default store yields to the block' do
- expect(primary_store).to receive(name).and_yield(value)
- expect(secondary_store).not_to receive(name).and_yield(value)
-
- subject
- end
-
- it 'only default store to execute' do
- expect(primary_store).to receive(name).with(*expected_args).and_call_original
- expect(secondary_store).not_to receive(name).with(*expected_args).and_call_original
-
- subject
- end
+ it 'only default store to execute' do
+ expect(primary_store).to receive(:send).with(name, expected_args)
+ expect(secondary_store).not_to receive(:send)
- include_examples 'reads correct value'
+ subject
end
end
@@ -327,8 +183,8 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
it 'executes only on secondary redis store', :aggregate_failures do
- expect(secondary_store).to receive(name).with(*expected_args).and_call_original
- expect(primary_store).not_to receive(name).with(*expected_args).and_call_original
+ expect(secondary_store).to receive(name).with(*expected_args)
+ expect(primary_store).not_to receive(name).with(*expected_args)
subject
end
@@ -336,8 +192,8 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
context 'when using primary store as default' do
it 'executes only on primary redis store', :aggregate_failures do
- expect(primary_store).to receive(name).with(*expected_args).and_call_original
- expect(secondary_store).not_to receive(name).with(*expected_args).and_call_original
+ expect(primary_store).to receive(name).with(*expected_args)
+ expect(secondary_store).not_to receive(name).with(*expected_args)
subject
end
@@ -429,110 +285,24 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
- RSpec.shared_examples_for 'verify that store contains values' do |store|
- it "#{store} redis store contains correct values", :aggregate_failures do
- subject
-
- redis_store = multi_store.send(store)
-
- if expected_value.is_a?(Array)
- # :smembers does not guarantee the order it will return the values
- expect(redis_store.send(verification_name, *verification_args)).to match_array(expected_value)
- else
- expect(redis_store.send(verification_name, *verification_args)).to eq(expected_value)
- end
- end
- end
-
- # rubocop:disable RSpec/MultipleMemoizedHelpers
context 'with WRITE redis commands' do
- let_it_be(:ikey1) { "counter1" }
- let_it_be(:ikey2) { "counter2" }
- let_it_be(:iargs) { [ikey2, 3] }
- let_it_be(:ivalue1) { "1" }
- let_it_be(:ivalue2) { "3" }
- let_it_be(:key1) { "redis:{1}:key_a" }
- let_it_be(:key2) { "redis:{1}:key_b" }
- let_it_be(:key3) { "redis:{1}:key_c" }
- let_it_be(:key4) { "redis:{1}:key_d" }
- let_it_be(:value1) { "redis_value1" }
- let_it_be(:value2) { "redis_value2" }
- let_it_be(:key1_value1) { [key1, value1] }
- let_it_be(:key1_value2) { [key1, value2] }
- let_it_be(:ttl) { 10 }
- let_it_be(:key1_ttl_value1) { [key1, ttl, value1] }
- let_it_be(:skey) { "redis:set:key" }
- let_it_be(:svalues1) { [value2, value1] }
- let_it_be(:svalues2) { [value1] }
- let_it_be(:skey_value1) { [skey, [value1]] }
- let_it_be(:skey_value2) { [skey, [value2]] }
- let_it_be(:script) { %(redis.call("set", "#{key1}", "#{value1}")) }
- let_it_be(:hkey1) { "redis:{1}:hash_a" }
- let_it_be(:hkey2) { "redis:{1}:hash_b" }
- let_it_be(:item) { "item" }
- let_it_be(:hdelarg) { [hkey1, item] }
- let_it_be(:hsetarg) { [hkey2, item, value1] }
- let_it_be(:mhsetarg) { [hkey2, { "item" => value1 }] }
- let_it_be(:hgetarg) { [hkey2, item] }
- let_it_be(:expireargs) { [key3, ttl] }
-
- # rubocop:disable Layout/LineLength
- where(:case_name, :name, :args, :expected_value, :verification_name, :verification_args) do
- 'execute :set command' | :set | ref(:key1_value1) | ref(:value1) | :get | ref(:key1)
- 'execute :setnx command' | :setnx | ref(:key1_value2) | ref(:value1) | :get | ref(:key2)
- 'execute :setex command' | :setex | ref(:key1_ttl_value1) | ref(:ttl) | :ttl | ref(:key1)
- 'execute :sadd command' | :sadd | ref(:skey_value2) | ref(:svalues1) | :smembers | ref(:skey)
- 'execute :sadd? command' | :sadd? | ref(:skey_value2) | ref(:svalues1) | :smembers | ref(:skey)
- 'execute :srem command' | :srem | ref(:skey_value1) | [] | :smembers | ref(:skey)
- 'execute :del command' | :del | ref(:key2) | nil | :get | ref(:key2)
- 'execute :unlink command' | :unlink | ref(:key3) | nil | :get | ref(:key3)
- 'execute :flushdb command' | :flushdb | nil | 0 | :dbsize | nil
- 'execute :eval command' | :eval | ref(:script) | ref(:value1) | :get | ref(:key1)
- 'execute :incr command' | :incr | ref(:ikey1) | ref(:ivalue1) | :get | ref(:ikey1)
- 'execute :incrby command' | :incrby | ref(:iargs) | ref(:ivalue2) | :get | ref(:ikey2)
- 'execute :hset command' | :hset | ref(:hsetarg) | ref(:value1) | :hget | ref(:hgetarg)
- 'execute :hdel command' | :hdel | ref(:hdelarg) | nil | :hget | ref(:hdelarg)
- 'execute :expire command' | :expire | ref(:expireargs) | ref(:ttl) | :ttl | ref(:key3)
- 'execute :mapped_hmset command' | :mapped_hmset | ref(:mhsetarg) | ref(:value1) | :hget | ref(:hgetarg)
- end
- # rubocop:enable Layout/LineLength
-
- before do
- primary_store.flushdb
- secondary_store.flushdb
-
- primary_store.set(key2, value1)
- primary_store.set(key3, value1)
- primary_store.set(key4, value1)
- primary_store.sadd?(skey, value1)
- primary_store.hset(hkey2, item, value1)
-
- secondary_store.set(key2, value1)
- secondary_store.set(key3, value1)
- secondary_store.set(key4, value1)
- secondary_store.sadd?(skey, value1)
- secondary_store.hset(hkey2, item, value1)
- end
-
- with_them do
+ described_class::WRITE_COMMANDS.each do |name|
describe name.to_s do
- let(:expected_args) { args || no_args }
+ let(:args) { "dummy_args" }
+ let(:name) { name }
before do
- allow(primary_store).to receive(name).and_call_original
- allow(secondary_store).to receive(name).and_call_original
+ allow(primary_store).to receive(name)
+ allow(secondary_store).to receive(name)
end
context 'when executing on primary instance is successful' do
it 'executes on both primary and secondary redis store', :aggregate_failures do
- expect(primary_store).to receive(name).with(*expected_args).and_call_original
- expect(secondary_store).to receive(name).with(*expected_args).and_call_original
+ expect(primary_store).to receive(name).with(*args)
+ expect(secondary_store).to receive(name).with(*args)
subject
end
-
- include_examples 'verify that store contains values', :primary_store
- include_examples 'verify that store contains values', :secondary_store
end
context 'when use_primary_and_secondary_stores feature flag is disabled' do
@@ -546,8 +316,8 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
it 'executes only on secondary redis store', :aggregate_failures do
- expect(secondary_store).to receive(name).with(*expected_args).and_call_original
- expect(primary_store).not_to receive(name).with(*expected_args).and_call_original
+ expect(secondary_store).to receive(name).with(*args)
+ expect(primary_store).not_to receive(name).with(*args)
subject
end
@@ -555,8 +325,8 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
context 'when using primary store as default' do
it 'executes only on primary redis store', :aggregate_failures do
- expect(primary_store).to receive(name).with(*expected_args).and_call_original
- expect(secondary_store).not_to receive(name).with(*expected_args).and_call_original
+ expect(primary_store).to receive(name).with(*args)
+ expect(secondary_store).not_to receive(name).with(*args)
subject
end
@@ -565,31 +335,30 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
context 'when executing on the default instance is raising an exception' do
before do
- allow(multi_store.default_store).to receive(name).with(*expected_args).and_raise(StandardError)
+ allow(multi_store.default_store).to receive(name).with(*args).and_raise(StandardError)
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
it 'raises error and does not execute on non default instance', :aggregate_failures do
- expect(multi_store.non_default_store).not_to receive(name).with(*expected_args)
+ expect(multi_store.non_default_store).not_to receive(name).with(*args)
expect { subject }.to raise_error(StandardError)
end
end
context 'when executing on the non default instance is raising an exception' do
before do
- allow(multi_store.non_default_store).to receive(name).with(*expected_args).and_raise(StandardError)
+ allow(multi_store.non_default_store).to receive(name).with(*args).and_raise(StandardError)
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
it 'logs the exception and execute on default instance', :aggregate_failures do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
- hash_including(:multi_store_error_message, command_name: name, instance_name: instance_name))
- expect(multi_store.default_store).to receive(name).with(*expected_args).and_call_original
+ hash_including(:multi_store_error_message,
+ command_name: name, instance_name: instance_name))
+ expect(multi_store.default_store).to receive(name).with(*args)
subject
end
-
- include_examples 'verify that store contains values', :default_store
end
context 'when the command is executed within pipelined block' do
@@ -602,24 +371,35 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
it 'is executed only 1 time on each instance', :aggregate_failures do
expect(primary_store).to receive(:pipelined).and_call_original
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
- expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
+ expect(pipeline).to receive(name).with(*args).once
end
expect(secondary_store).to receive(:pipelined).and_call_original
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
- expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
+ expect(pipeline).to receive(name).with(*args).once
end
subject
end
-
- include_examples 'verify that store contains values', :primary_store
- include_examples 'verify that store contains values', :secondary_store
end
end
end
end
- # rubocop:enable RSpec/MultipleMemoizedHelpers
+
+ RSpec.shared_examples_for 'verify that store contains values' do |store|
+ it "#{store} redis store contains correct values", :aggregate_failures do
+ subject
+
+ redis_store = multi_store.send(store)
+
+ if expected_value.is_a?(Array)
+ # :smembers does not guarantee the order it will return the values
+ expect(redis_store.send(verification_name, *verification_args)).to match_array(expected_value)
+ else
+ expect(redis_store.send(verification_name, *verification_args)).to eq(expected_value)
+ end
+ end
+ end
RSpec.shared_examples_for 'pipelined command' do |name|
let_it_be(:key1) { "redis:{1}:key_a" }
@@ -951,11 +731,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
describe '#close' do
subject { multi_store.close }
- context 'when using both stores' do
- before do
- allow(multi_store).to receive(:use_primary_and_secondary_stores?).and_return(true)
- end
-
+ context 'when using both stores are different' do
it 'closes both stores' do
expect(primary_store).to receive(:close)
expect(secondary_store).to receive(:close)
@@ -964,36 +740,72 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
- context 'when using only one store' do
+ context 'when using identical stores' do
before do
- allow(multi_store).to receive(:use_primary_and_secondary_stores?).and_return(false)
+ allow(multi_store).to receive(:same_redis_store?).and_return(true)
end
- context 'when using primary_store as default store' do
- before do
- allow(multi_store).to receive(:use_primary_store_as_default?).and_return(true)
- end
+ it 'closes secondary store' do
+ expect(secondary_store).to receive(:close)
+ expect(primary_store).not_to receive(:close)
- it 'closes primary store' do
- expect(primary_store).to receive(:close)
- expect(secondary_store).not_to receive(:close)
+ subject
+ end
+ end
+ end
- subject
- end
+ describe '#blpop' do
+ let_it_be(:key) { "mylist" }
+
+ subject { multi_store.blpop(key, timeout: 0.1) }
+
+ shared_examples 'calls blpop on default_store' do
+ it 'calls blpop on default_store' do
+ expect(multi_store.default_store).to receive(:blpop).with(key, { timeout: 0.1 })
+
+ subject
end
+ end
- context 'when using secondary_store as default store' do
+ shared_examples 'does not call lpop on non_default_store' do
+ it 'does not call blpop on non_default_store' do
+ expect(multi_store.non_default_store).not_to receive(:blpop)
+
+ subject
+ end
+ end
+
+ context 'when using both stores' do
+ before do
+ allow(multi_store).to receive(:use_primary_and_secondary_stores?).and_return(true)
+ end
+
+ it_behaves_like 'calls blpop on default_store'
+
+ context "when an element exists in the default_store" do
before do
- allow(multi_store).to receive(:use_primary_store_as_default?).and_return(false)
+ multi_store.default_store.lpush(key, 'abc')
end
- it 'closes secondary store' do
- expect(primary_store).not_to receive(:close)
- expect(secondary_store).to receive(:close)
+ it 'calls lpop on non_default_store' do
+ expect(multi_store.non_default_store).to receive(:blpop).with(key, { timeout: 1 })
subject
end
end
+
+ context 'when the list is empty in default_store' do
+ it_behaves_like 'does not call lpop on non_default_store'
+ end
+ end
+
+ context 'when using one store' do
+ before do
+ allow(multi_store).to receive(:use_primary_and_secondary_stores?).and_return(false)
+ end
+
+ it_behaves_like 'calls blpop on default_store'
+ it_behaves_like 'does not call lpop on non_default_store'
end
end
@@ -1279,4 +1091,19 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
end
+
+ describe '*_COMMANDS' do
+ it 'checks if every command is only defined once' do
+ commands = [
+ described_class::REDIS_CLIENT_COMMANDS,
+ described_class::PUBSUB_SUBSCRIBE_COMMANDS,
+ described_class::READ_COMMANDS,
+ described_class::WRITE_COMMANDS,
+ described_class::PIPELINED_COMMANDS
+ ].inject([], :concat)
+ duplicated_commands = commands.group_by { |c| c }.select { |k, v| v.size > 1 }.map(&:first)
+
+ expect(duplicated_commands).to be_empty, "commands #{duplicated_commands} defined more than once"
+ end
+ end
end