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/instrumentation')
-rw-r--r--spec/lib/gitlab/instrumentation/connection_pool_spec.rb69
-rw-r--r--spec/lib/gitlab/instrumentation/redis_base_spec.rb14
-rw-r--r--spec/lib/gitlab/instrumentation/redis_helper_spec.rb136
-rw-r--r--spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb19
4 files changed, 238 insertions, 0 deletions
diff --git a/spec/lib/gitlab/instrumentation/connection_pool_spec.rb b/spec/lib/gitlab/instrumentation/connection_pool_spec.rb
new file mode 100644
index 00000000000..b7cab2e9900
--- /dev/null
+++ b/spec/lib/gitlab/instrumentation/connection_pool_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'support/helpers/rails_helpers'
+
+RSpec.describe Gitlab::Instrumentation::ConnectionPool, feature_category: :redis do
+ let(:option) { { name: 'test', size: 5 } }
+ let(:pool) { ConnectionPool.new(option) { 'nothing' } }
+
+ let_it_be(:size_gauge_args) { [:gitlab_connection_pool_size, 'Size of connection pool', {}, :all] }
+ let_it_be(:available_gauge_args) do
+ [:gitlab_connection_pool_available_count,
+ 'Number of available connections in the pool', {}, :all]
+ end
+
+ subject(:checkout_pool) { pool.checkout }
+
+ describe '.checkout' do
+ let(:size_gauge_double) { instance_double(::Prometheus::Client::Gauge) }
+
+ context 'when tracking for the first time' do
+ it 'initialises gauges' do
+ expect(::Gitlab::Metrics).to receive(:gauge).with(*size_gauge_args).and_call_original
+ expect(::Gitlab::Metrics).to receive(:gauge).with(*available_gauge_args).and_call_original
+
+ checkout_pool
+ end
+ end
+
+ it 'sets the size gauge only once' do
+ expect(::Gitlab::Metrics.gauge(*size_gauge_args)).to receive(:set).with(
+ { pool_name: 'test', pool_key: anything, connection_class: "String" }, 5).once
+
+ checkout_pool
+ checkout_pool
+ end
+
+ context 'when tracking on subsequent calls' do
+ before do
+ pool.checkout # initialise instance variables
+ end
+
+ it 'uses memoized gauges' do
+ expect(::Gitlab::Metrics).not_to receive(:gauge).with(*size_gauge_args)
+ expect(::Gitlab::Metrics).not_to receive(:gauge).with(*available_gauge_args)
+
+ expect(pool.instance_variable_get(:@size_gauge)).not_to receive(:set)
+ .with({ pool_name: 'test', pool_key: anything, connection_class: "String" }, 5)
+ expect(pool.instance_variable_get(:@available_gauge)).to receive(:set)
+ .with({ pool_name: 'test', pool_key: anything, connection_class: "String" }, 4)
+
+ checkout_pool
+ end
+
+ context 'when pool name is omitted' do
+ let(:option) { {} }
+
+ it 'uses unknown name' do
+ expect(pool.instance_variable_get(:@size_gauge)).not_to receive(:set)
+ .with({ pool_name: 'unknown', pool_key: anything, connection_class: "String" }, 5)
+ expect(pool.instance_variable_get(:@available_gauge)).to receive(:set)
+ .with({ pool_name: 'unknown', pool_key: anything, connection_class: "String" }, 4)
+
+ checkout_pool
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/instrumentation/redis_base_spec.rb b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
index 426997f6e86..f0854b38353 100644
--- a/spec/lib/gitlab/instrumentation/redis_base_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
@@ -222,4 +222,18 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
instrumentation_class_a.log_exception(StandardError.new)
end
end
+
+ describe '.instance_count_connection_exception' do
+ before do
+ # initialise connection_exception_counter
+ instrumentation_class_a.instance_count_connection_exception(StandardError.new)
+ end
+
+ it 'counts connection exception' do
+ expect(instrumentation_class_a.instance_variable_get(:@connection_exception_counter)).to receive(:increment)
+ .with({ storage: instrumentation_class_a.storage_key, exception: 'Redis::ConnectionError' })
+
+ instrumentation_class_a.instance_count_connection_exception(Redis::ConnectionError.new)
+ end
+ end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_helper_spec.rb b/spec/lib/gitlab/instrumentation/redis_helper_spec.rb
new file mode 100644
index 00000000000..54659ca2c02
--- /dev/null
+++ b/spec/lib/gitlab/instrumentation/redis_helper_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Instrumentation::RedisHelper, :request_store, feature_category: :scalability do
+ include RedisHelpers
+
+ let(:minimal_test_class) do
+ Class.new do
+ include Gitlab::Instrumentation::RedisHelper
+ def initialize
+ @instrumentation_class = Gitlab::Instrumentation::Redis::Cache
+ end
+
+ def check_command(commands, pipelined)
+ instrument_call(commands, @instrumentation_class, pipelined) { 'empty block' }
+ end
+
+ def test_read(result)
+ measure_read_size(result, @instrumentation_class)
+ end
+
+ def test_write(command)
+ measure_write_size(command, @instrumentation_class)
+ end
+
+ def test_exclusion(commands)
+ exclude_from_apdex?(commands)
+ end
+ end
+ end
+
+ before do
+ stub_const("MinimalTestClass", minimal_test_class)
+ end
+
+ subject(:minimal_test_class_instance) { MinimalTestClass.new }
+
+ describe '.instrument_call' do
+ it 'instruments request count' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_request).with(1)
+ expect(Gitlab::Instrumentation::Redis::Cache).not_to receive(:instance_count_pipelined_request)
+
+ minimal_test_class_instance.check_command([[:set, 'foo', 'bar']], false)
+ end
+
+ it 'performs cluster validation' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:redis_cluster_validate!).once
+
+ minimal_test_class_instance.check_command([[:set, 'foo', 'bar']], false)
+ end
+
+ context 'when command is not valid for Redis Cluster' do
+ before do
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:redis_cluster_validate!).and_return(false)
+ end
+
+ it 'reports cross slot request' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_cross_slot_request_count).once
+
+ minimal_test_class_instance.check_command([[:mget, 'foo', 'bar']], false)
+ end
+ end
+
+ context 'when an error is raised' do
+ # specific error behaviours are tested in spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb
+ # this spec tests for the generic behaviour to verify that `ensure` works for any general error types
+ before do
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_request)
+ .and_raise(StandardError)
+ end
+
+ it 'ensures duration is tracked' do
+ commands = [[:set, 'foo', 'bar']]
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_observe_duration).once
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_request_count).with(1).once
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:add_duration).once
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:add_call_details).with(anything, commands).once
+
+ expect { minimal_test_class_instance.check_command(commands, false) }.to raise_error(StandardError)
+ end
+ end
+
+ context 'when pipelined' do
+ it 'instruments pipelined request count' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_pipelined_request)
+
+ minimal_test_class_instance.check_command([[:get, '{user1}:bar'], [:get, '{user1}:foo']], true)
+ end
+ end
+ end
+
+ describe '.measure_read_size' do
+ it 'reads array' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_read_bytes).with(3).exactly(3).times
+
+ minimal_test_class_instance.test_read(%w[bar foo buz])
+ end
+
+ it 'reads Integer' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_read_bytes).with(4)
+
+ minimal_test_class_instance.test_read(1234)
+ end
+
+ it 'reads String' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_read_bytes).with(3)
+
+ minimal_test_class_instance.test_read('bar')
+ end
+ end
+
+ describe '.measure_write_size' do
+ it 'measures command size' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_write_bytes).with(9)
+
+ minimal_test_class_instance.test_write([:set, 'foo', 'bar'])
+ end
+
+ it 'accept array input' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_write_bytes).with((9 + 12))
+
+ minimal_test_class_instance.test_write([[:set, 'foo', 'bar'], [:lpush, 'que', 'item']])
+ end
+ end
+
+ describe '.exclude_from_apdex?' do
+ it 'returns false if all commands are allowed' do
+ expect(minimal_test_class_instance.test_exclusion([[:set, 'foo', 'bar'], [:lpush, 'que', 'item']])).to eq(false)
+ end
+
+ it 'returns true if any commands are banned' do
+ expect(minimal_test_class_instance.test_exclusion([[:brpop, 'foo', 2], [:lpush, 'que', 'item']])).to eq(true)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
index 4168fdf5425..e9bd0056e5f 100644
--- a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
@@ -72,6 +72,25 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :request_store, featur
end
end
+ context 'when encountering connection exceptions within process' do
+ before do
+ redis_store_class.with do |redis|
+ allow(redis._client).to receive(:write).and_call_original
+ end
+ end
+
+ it 'counts connection exceptions' do
+ redis_store_class.with do |redis|
+ expect(redis._client).to receive(:write).with([:get, 'foobar']).and_raise(::Redis::ConnectionError)
+ end
+
+ expect(instrumentation_class).to receive(:instance_count_connection_exception)
+ .with(instance_of(Redis::ConnectionError)).and_call_original
+
+ redis_store_class.with { |redis| redis.call(:get, 'foobar') }
+ end
+ end
+
context 'when encountering exceptions' do
where(:case_name, :exception, :exception_counter) do
'generic exception' | Redis::CommandError | :instance_count_exception