Commit 85987c47 authored by James Fargher's avatar James Fargher

Merge branch 'ajk-query-limiting-middleware-logs' into 'master'

Show log of SQL statements in QueryLimiting middleware error messages

See merge request gitlab-org/gitlab!55630
parents 591573d0 2c7b4132
...@@ -6,9 +6,10 @@ module Gitlab ...@@ -6,9 +6,10 @@ module Gitlab
attach_to :active_record attach_to :active_record
def sql(event) def sql(event)
unless event.payload.fetch(:cached, event.payload[:name] == 'CACHE') return if !Transaction.current || event.payload.fetch(:cached, event.payload[:name] == 'CACHE')
Transaction.current&.increment
end Transaction.current.increment
Transaction.current.executed_sql(event.payload[:sql])
end end
end end
end end
......
...@@ -15,6 +15,7 @@ module Gitlab ...@@ -15,6 +15,7 @@ module Gitlab
# the sake of keeping things simple we hardcode this value here, it's not # the sake of keeping things simple we hardcode this value here, it's not
# supposed to be changed very often anyway. # supposed to be changed very often anyway.
THRESHOLD = 100 THRESHOLD = 100
LOG_THRESHOLD = THRESHOLD * 1.5
# Error that is raised whenever exceeding the maximum number of queries. # Error that is raised whenever exceeding the maximum number of queries.
ThresholdExceededError = Class.new(StandardError) ThresholdExceededError = Class.new(StandardError)
...@@ -45,6 +46,7 @@ module Gitlab ...@@ -45,6 +46,7 @@ module Gitlab
@action = nil @action = nil
@count = 0 @count = 0
@whitelisted = false @whitelisted = false
@sql_executed = []
end end
# Sends a notification based on the number of executed SQL queries. # Sends a notification based on the number of executed SQL queries.
...@@ -60,6 +62,10 @@ module Gitlab ...@@ -60,6 +62,10 @@ module Gitlab
@count += 1 unless whitelisted @count += 1 unless whitelisted
end end
def executed_sql(sql)
@sql_executed << sql if @count <= LOG_THRESHOLD
end
def raise_error? def raise_error?
Rails.env.test? Rails.env.test?
end end
...@@ -71,8 +77,11 @@ module Gitlab ...@@ -71,8 +77,11 @@ module Gitlab
def error_message def error_message
header = 'Too many SQL queries were executed' header = 'Too many SQL queries were executed'
header = "#{header} in #{action}" if action header = "#{header} in #{action}" if action
msg = "a maximum of #{THRESHOLD} is allowed but #{count} SQL queries were executed"
log = @sql_executed.each_with_index.map { |sql, i| "#{i}: #{sql}" }.join("\n").presence
ellipsis = '...' if @count > LOG_THRESHOLD
"#{header}: a maximum of #{THRESHOLD} is allowed but #{count} SQL queries were executed" ["#{header}: #{msg}", log, ellipsis].compact.join("\n")
end end
end end
end end
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::QueryLimiting::ActiveSupportSubscriber do RSpec.describe Gitlab::QueryLimiting::ActiveSupportSubscriber do
let(:transaction) { instance_double(Gitlab::QueryLimiting::Transaction, increment: true) } let(:transaction) { instance_double(Gitlab::QueryLimiting::Transaction, executed_sql: true, increment: true) }
before do before do
allow(Gitlab::QueryLimiting::Transaction) allow(Gitlab::QueryLimiting::Transaction)
...@@ -18,6 +18,11 @@ RSpec.describe Gitlab::QueryLimiting::ActiveSupportSubscriber do ...@@ -18,6 +18,11 @@ RSpec.describe Gitlab::QueryLimiting::ActiveSupportSubscriber do
expect(transaction) expect(transaction)
.to have_received(:increment) .to have_received(:increment)
.once .once
expect(transaction)
.to have_received(:executed_sql)
.once
.with(String)
end end
context 'when the query is actually a rails cache hit' do context 'when the query is actually a rails cache hit' do
...@@ -30,6 +35,11 @@ RSpec.describe Gitlab::QueryLimiting::ActiveSupportSubscriber do ...@@ -30,6 +35,11 @@ RSpec.describe Gitlab::QueryLimiting::ActiveSupportSubscriber do
expect(transaction) expect(transaction)
.to have_received(:increment) .to have_received(:increment)
.once .once
expect(transaction)
.to have_received(:executed_sql)
.once
.with(String)
end end
end end
end end
......
...@@ -118,6 +118,30 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do ...@@ -118,6 +118,30 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do
) )
end end
it 'includes a list of executed queries' do
transaction = described_class.new
transaction.count = max = described_class::THRESHOLD
%w[foo bar baz].each { |sql| transaction.executed_sql(sql) }
message = transaction.error_message
expect(message).to start_with(
"Too many SQL queries were executed: a maximum of #{max} " \
"is allowed but #{max} SQL queries were executed"
)
expect(message).to include("0: foo", "1: bar", "2: baz")
end
it 'indicates if the log is truncated' do
transaction = described_class.new
transaction.count = described_class::THRESHOLD * 2
message = transaction.error_message
expect(message).to end_with('...')
end
it 'includes the action name in the error message when present' do it 'includes the action name in the error message when present' do
transaction = described_class.new transaction = described_class.new
transaction.count = max = described_class::THRESHOLD transaction.count = max = described_class::THRESHOLD
......
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