Skip to content
代码片段 群组 项目
提交 a57d7dc2 编辑于 作者: Dylan Griffith's avatar Dylan Griffith
浏览文件

Merge branch 'fix-specs-with-query-analyzers' into 'master'

Allow nesting Query Analyzers

See merge request gitlab-org/gitlab!85531
No related branches found
No related tags found
无相关合并请求
...@@ -30,52 +30,25 @@ def hook! ...@@ -30,52 +30,25 @@ def hook!
end end
end end
def within(user_analyzers = nil) def within(analyzers = all_analyzers)
# Due to singleton nature of analyzers newly_enabled_analyzers = begin!(analyzers)
# only an outer invocation of the `.within`
# is allowed to initialize them
if already_within?
raise 'Query analyzers are already defined, cannot re-define them.' if user_analyzers
return yield
end
begin!(user_analyzers || all_analyzers)
begin begin
yield yield
ensure ensure
end! end!(newly_enabled_analyzers)
end end
end end
def already_within? # Enable query analyzers (only the ones that were not yet enabled)
# If analyzers are set they are already configured # Returns a list of newly enabled analyzers
!enabled_analyzers.nil? def begin!(analyzers)
end analyzers.select do |analyzer|
next if enabled_analyzers.include?(analyzer)
def process_sql(sql, connection)
analyzers = enabled_analyzers
return unless analyzers&.any?
parsed = parse(sql, connection)
return unless parsed
analyzers.each do |analyzer|
next if analyzer.suppressed? && !analyzer.requires_tracking?(parsed)
analyzer.analyze(parsed)
rescue StandardError, ::Gitlab::Database::QueryAnalyzers::Base::QueryAnalyzerError => e
# We catch all standard errors to prevent validation errors to introduce fatal errors in production
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
end
end
# Enable query analyzers
def begin!(analyzers = all_analyzers)
analyzers = analyzers.select do |analyzer|
if analyzer.enabled? if analyzer.enabled?
analyzer.begin! analyzer.begin!
enabled_analyzers.append(analyzer)
true true
end end
...@@ -84,25 +57,40 @@ def begin!(analyzers = all_analyzers) ...@@ -84,25 +57,40 @@ def begin!(analyzers = all_analyzers)
false false
end end
Thread.current[:query_analyzer_enabled_analyzers] = analyzers
end end
# Disable enabled query analyzers # Disable enabled query analyzers (only the ones that were enabled previously)
def end! def end!(analyzers)
enabled_analyzers.select do |analyzer| analyzers.each do |analyzer|
next unless enabled_analyzers.delete(analyzer)
analyzer.end! analyzer.end!
rescue StandardError, ::Gitlab::Database::QueryAnalyzers::Base::QueryAnalyzerError => e rescue StandardError, ::Gitlab::Database::QueryAnalyzers::Base::QueryAnalyzerError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e) Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
end end
Thread.current[:query_analyzer_enabled_analyzers] = nil
end end
private private
def enabled_analyzers def enabled_analyzers
Thread.current[:query_analyzer_enabled_analyzers] Thread.current[:query_analyzer_enabled_analyzers] ||= []
end
def process_sql(sql, connection)
analyzers = enabled_analyzers
return unless analyzers&.any?
parsed = parse(sql, connection)
return unless parsed
analyzers.each do |analyzer|
next if analyzer.suppressed? && !analyzer.requires_tracking?(parsed)
analyzer.analyze(parsed)
rescue StandardError, ::Gitlab::Database::QueryAnalyzers::Base::QueryAnalyzerError => e
# We catch all standard errors to prevent validation errors to introduce fatal errors in production
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
end
end end
def parse(sql, connection) def parse(sql, connection)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do
let(:analyzer) { double(:query_analyzer) } let(:analyzer) { double(:query_analyzer) }
let(:user_analyzer) { double(:query_analyzer) } let(:user_analyzer) { double(:user_query_analyzer) }
let(:disabled_analyzer) { double(:disabled_query_analyzer) } let(:disabled_analyzer) { double(:disabled_query_analyzer) }
before do before do
...@@ -49,14 +49,36 @@ ...@@ -49,14 +49,36 @@
end end
end end
it 'does not evaluate enabled? again do yield block' do it 'does initialize analyzer only once' do
expect(analyzer).not_to receive(:enabled?) expect(analyzer).to receive(:enabled?).once
expect(analyzer).to receive(:begin!).once
expect(analyzer).to receive(:end!).once
expect { |b| described_class.instance.within(&b) }.to yield_control expect { |b| described_class.instance.within(&b) }.to yield_control
end end
it 'raises exception when trying to re-define analyzers' do it 'does initialize user analyzer when enabled' do
expect { |b| described_class.instance.within([user_analyzer], &b) }.to raise_error /Query analyzers are already defined, cannot re-define them/ expect(user_analyzer).to receive(:enabled?).and_return(true)
expect(user_analyzer).to receive(:begin!)
expect(user_analyzer).to receive(:end!)
expect { |b| described_class.instance.within([user_analyzer], &b) }.to yield_control
end
it 'does initialize user analyzer only once' do
expect(user_analyzer).to receive(:enabled?).and_return(false, true)
expect(user_analyzer).to receive(:begin!).once
expect(user_analyzer).to receive(:end!).once
expect { |b| described_class.instance.within([user_analyzer, user_analyzer, user_analyzer], &b) }.to yield_control
end
it 'does not initializer user analyzer when disabled' do
expect(user_analyzer).to receive(:enabled?).and_return(false)
expect(user_analyzer).not_to receive(:begin!)
expect(user_analyzer).not_to receive(:end!)
expect { |b| described_class.instance.within([user_analyzer], &b) }.to yield_control
end end
end end
...@@ -162,7 +184,7 @@ ...@@ -162,7 +184,7 @@
def process_sql(sql) def process_sql(sql)
described_class.instance.within do described_class.instance.within do
ApplicationRecord.load_balancer.read_write do |connection| ApplicationRecord.load_balancer.read_write do |connection|
described_class.instance.process_sql(sql, connection) described_class.instance.send(:process_sql, sql, connection)
end end
end end
end end
......
...@@ -140,7 +140,7 @@ ...@@ -140,7 +140,7 @@
def process_sql(model, sql) def process_sql(model, sql)
Gitlab::Database::QueryAnalyzer.instance.within do Gitlab::Database::QueryAnalyzer.instance.within do
# Skip load balancer and retrieve connection assigned to model # Skip load balancer and retrieve connection assigned to model
Gitlab::Database::QueryAnalyzer.instance.process_sql(sql, model.retrieve_connection) Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection)
end end
end end
end end
...@@ -155,7 +155,7 @@ def process_sql(sql, model = ActiveRecord::Base) ...@@ -155,7 +155,7 @@ def process_sql(sql, model = ActiveRecord::Base)
yield if block_given? yield if block_given?
# Skip load balancer and retrieve connection assigned to model # Skip load balancer and retrieve connection assigned to model
Gitlab::Database::QueryAnalyzer.instance.process_sql(sql, model.retrieve_connection) Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection)
end end
end end
end end
...@@ -6,13 +6,17 @@ ...@@ -6,13 +6,17 @@
RSpec.configure do |config| RSpec.configure do |config|
config.before do |example| config.before do |example|
if example.metadata.fetch(:query_analyzers, true) if example.metadata.fetch(:query_analyzers, true)
::Gitlab::Database::QueryAnalyzer.instance.begin! ::Gitlab::Database::QueryAnalyzer.instance.begin!(
::Gitlab::Database::QueryAnalyzer.instance.all_analyzers
)
end end
end end
config.after do |example| config.after do |example|
if example.metadata.fetch(:query_analyzers, true) if example.metadata.fetch(:query_analyzers, true)
::Gitlab::Database::QueryAnalyzer.instance.end! ::Gitlab::Database::QueryAnalyzer.instance.end!(
::Gitlab::Database::QueryAnalyzer.instance.all_analyzers
)
end end
end end
end end
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册