e3bd674e81
Using Sentry, while useful, poses two problems you have to choose from: 1. All errors are reported separately, making it easy to create issues but also making it next to impossible to see other errors (due to the sheer volume of threshold errors). 2. Errors can be grouped or merged together, reducing the noise. This however also means it's (as far as I can tell) much harder to automatically create GitLab issues from Sentry for the offending controllers. Since both solutions are terrible I decided to go with a third option: not using Sentry for this at all. Instead we'll investigate using Prometheus alerts and Grafana dashboards for this, which has the added benefit of being able to more accurately measure the behaviour over time. Note that throwing errors in test environments is still enabled, and whitelisting is still necessary to prevent that from happening (and that in turn still requires that developers create issues).
132 lines
3.5 KiB
Ruby
132 lines
3.5 KiB
Ruby
require 'spec_helper'
|
|
|
|
describe Gitlab::QueryLimiting::Transaction do
|
|
after do
|
|
Thread.current[described_class::THREAD_KEY] = nil
|
|
end
|
|
|
|
describe '.current' do
|
|
it 'returns nil when there is no transaction' do
|
|
expect(described_class.current).to be_nil
|
|
end
|
|
|
|
it 'returns the transaction when present' do
|
|
Thread.current[described_class::THREAD_KEY] = described_class.new
|
|
|
|
expect(described_class.current).to be_an_instance_of(described_class)
|
|
end
|
|
end
|
|
|
|
describe '.run' do
|
|
it 'runs a transaction and returns it and its return value' do
|
|
trans, ret = described_class.run do
|
|
10
|
|
end
|
|
|
|
expect(trans).to be_an_instance_of(described_class)
|
|
expect(ret).to eq(10)
|
|
end
|
|
|
|
it 'removes the transaction from the current thread upon completion' do
|
|
described_class.run do
|
|
10
|
|
end
|
|
|
|
expect(Thread.current[described_class::THREAD_KEY]).to be_nil
|
|
end
|
|
end
|
|
|
|
describe '#act_upon_results' do
|
|
context 'when the query threshold is not exceeded' do
|
|
it 'does nothing' do
|
|
trans = described_class.new
|
|
|
|
expect(trans).not_to receive(:raise)
|
|
|
|
trans.act_upon_results
|
|
end
|
|
end
|
|
|
|
context 'when the query threshold is exceeded' do
|
|
let(:transaction) do
|
|
trans = described_class.new
|
|
trans.count = described_class::THRESHOLD + 1
|
|
|
|
trans
|
|
end
|
|
|
|
it 'raises an error when this is enabled' do
|
|
expect { transaction.act_upon_results }
|
|
.to raise_error(described_class::ThresholdExceededError)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#increment' do
|
|
it 'increments the number of executed queries' do
|
|
transaction = described_class.new
|
|
|
|
expect(transaction.count).to be_zero
|
|
|
|
transaction.increment
|
|
|
|
expect(transaction.count).to eq(1)
|
|
end
|
|
end
|
|
|
|
describe '#raise_error?' do
|
|
it 'returns true in a test environment' do
|
|
transaction = described_class.new
|
|
|
|
expect(transaction.raise_error?).to eq(true)
|
|
end
|
|
|
|
it 'returns false in a production environment' do
|
|
transaction = described_class.new
|
|
|
|
expect(Rails.env)
|
|
.to receive(:test?)
|
|
.and_return(false)
|
|
|
|
expect(transaction.raise_error?).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe '#threshold_exceeded?' do
|
|
it 'returns false when the threshold is not exceeded' do
|
|
transaction = described_class.new
|
|
|
|
expect(transaction.threshold_exceeded?).to eq(false)
|
|
end
|
|
|
|
it 'returns true when the threshold is exceeded' do
|
|
transaction = described_class.new
|
|
transaction.count = described_class::THRESHOLD + 1
|
|
|
|
expect(transaction.threshold_exceeded?).to eq(true)
|
|
end
|
|
end
|
|
|
|
describe '#error_message' do
|
|
it 'returns the error message to display when the threshold is exceeded' do
|
|
transaction = described_class.new
|
|
transaction.count = max = described_class::THRESHOLD
|
|
|
|
expect(transaction.error_message).to eq(
|
|
"Too many SQL queries were executed: a maximum of #{max} " \
|
|
"is allowed but #{max} SQL queries were executed"
|
|
)
|
|
end
|
|
|
|
it 'includes the action name in the error message when present' do
|
|
transaction = described_class.new
|
|
transaction.count = max = described_class::THRESHOLD
|
|
transaction.action = 'UsersController#show'
|
|
|
|
expect(transaction.error_message).to eq(
|
|
"Too many SQL queries were executed in UsersController#show: " \
|
|
"a maximum of #{max} is allowed but #{max} SQL queries were executed"
|
|
)
|
|
end
|
|
end
|
|
end
|