Commit 36ee1edc authored by Alper Akgun's avatar Alper Akgun

Merge branch '337060-raise-statement-invalid-for-hardening-methods' into 'master'

Add track_and_raise_for_dev_exception to usage_data methods

See merge request gitlab-org/gitlab!75182
parents 1642417c ecf93554
...@@ -711,17 +711,34 @@ RSpec.describe Gitlab::UsageData do ...@@ -711,17 +711,34 @@ RSpec.describe Gitlab::UsageData do
) )
end end
it 'has to resort to 0 for counting license scan' do context 'when count fails' do
for_defined_days_back do subject { described_class.usage_activity_by_stage_secure(described_class.monthly_time_range_db_params) }
create(:security_scan)
end
before do
allow(Gitlab::Database::BatchCount).to receive(:batch_distinct_count).and_raise(ActiveRecord::StatementInvalid) allow(Gitlab::Database::BatchCount).to receive(:batch_distinct_count).and_raise(ActiveRecord::StatementInvalid)
allow(Gitlab::Database::BatchCount).to receive(:batch_count).and_raise(ActiveRecord::StatementInvalid) allow(Gitlab::Database::BatchCount).to receive(:batch_count).and_raise(ActiveRecord::StatementInvalid)
allow(Gitlab::Database::PostgresHll::BatchDistinctCounter).to receive(:new).and_raise(ActiveRecord::StatementInvalid) allow(Gitlab::Database::PostgresHll::BatchDistinctCounter).to receive(:new).and_raise(ActiveRecord::StatementInvalid)
allow(::Ci::Build).to receive(:distinct_count_by).and_raise(ActiveRecord::StatementInvalid) allow(::Ci::Build).to receive(:distinct_count_by).and_raise(ActiveRecord::StatementInvalid)
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
end
expect(described_class.usage_activity_by_stage_secure(described_class.monthly_time_range_db_params)).to include( context 'with should_raise_for_dev? true' do
let(:should_raise_for_dev) { true }
it 'raises an error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
it 'has to resort to 0 for counting license scan' do
for_defined_days_back do
create(:security_scan)
end
expect(subject).to include(
user_preferences_group_overview_security_dashboard: -1, user_preferences_group_overview_security_dashboard: -1,
user_api_fuzzing_jobs: -1, user_api_fuzzing_jobs: -1,
user_api_fuzzing_dnd_jobs: -1, user_api_fuzzing_dnd_jobs: -1,
...@@ -749,6 +766,8 @@ RSpec.describe Gitlab::UsageData do ...@@ -749,6 +766,8 @@ RSpec.describe Gitlab::UsageData do
user_unique_users_all_secure_scanners: -1 user_unique_users_all_secure_scanners: -1
) )
end end
end
end
it 'deprecates count for users who have run scans' do it 'deprecates count for users who have run scans' do
expect(described_class.usage_activity_by_stage_secure(described_class.monthly_time_range_db_params)).to include( expect(described_class.usage_activity_by_stage_secure(described_class.monthly_time_range_db_params)).to include(
......
...@@ -406,7 +406,8 @@ module Gitlab ...@@ -406,7 +406,8 @@ module Gitlab
results[:projects_jira_cloud_active] = jira_integration_data_hash[:projects_jira_cloud_active] results[:projects_jira_cloud_active] = jira_integration_data_hash[:projects_jira_cloud_active]
results results
rescue ActiveRecord::StatementInvalid rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
{ projects_jira_server_active: FALLBACK, projects_jira_cloud_active: FALLBACK } { projects_jira_server_active: FALLBACK, projects_jira_cloud_active: FALLBACK }
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -56,7 +56,8 @@ module Gitlab ...@@ -56,7 +56,8 @@ module Gitlab
else else
relation.count relation.count
end end
rescue ActiveRecord::StatementInvalid rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
FALLBACK FALLBACK
end end
...@@ -66,7 +67,8 @@ module Gitlab ...@@ -66,7 +67,8 @@ module Gitlab
else else
relation.distinct_count_by(column) relation.distinct_count_by(column)
end end
rescue ActiveRecord::StatementInvalid rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
FALLBACK FALLBACK
end end
...@@ -78,7 +80,8 @@ module Gitlab ...@@ -78,7 +80,8 @@ module Gitlab
yield buckets if block_given? yield buckets if block_given?
buckets.estimated_distinct_count buckets.estimated_distinct_count
rescue ActiveRecord::StatementInvalid rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
FALLBACK FALLBACK
# catch all rescue should be removed as a part of feature flag rollout issue # catch all rescue should be removed as a part of feature flag rollout issue
# https://gitlab.com/gitlab-org/gitlab/-/issues/285485 # https://gitlab.com/gitlab-org/gitlab/-/issues/285485
...@@ -89,7 +92,8 @@ module Gitlab ...@@ -89,7 +92,8 @@ module Gitlab
def sum(relation, column, batch_size: nil, start: nil, finish: nil) def sum(relation, column, batch_size: nil, start: nil, finish: nil)
Gitlab::Database::BatchCount.batch_sum(relation, column, batch_size: batch_size, start: start, finish: finish) Gitlab::Database::BatchCount.batch_sum(relation, column, batch_size: batch_size, start: start, finish: finish)
rescue ActiveRecord::StatementInvalid rescue ActiveRecord::StatementInvalid => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
FALLBACK FALLBACK
end end
...@@ -155,7 +159,8 @@ module Gitlab ...@@ -155,7 +159,8 @@ module Gitlab
query: query.to_sql, query: query.to_sql,
message: e.message message: e.message
) )
# Raises error for dev env
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
HISTOGRAM_FALLBACK HISTOGRAM_FALLBACK
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -684,24 +684,51 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do ...@@ -684,24 +684,51 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end end
end end
it 'works when queries time out' do context 'when queries time out' do
allow_any_instance_of(ActiveRecord::Relation) let(:metric_method) { :count }
.to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
expect { subject }.not_to raise_error before do
allow_any_instance_of(ActiveRecord::Relation).to receive(metric_method).and_raise(ActiveRecord::StatementInvalid)
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
end end
it 'includes a recording_ce_finished_at timestamp' do context 'with should_raise_for_dev? true' do
expect(subject[:recording_ce_finished_at]).to be_a(Time) let(:should_raise_for_dev) { true }
it 'raises an error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
context 'when metric calls find_in_batches' do
let(:metric_method) { :find_in_batches }
it 'raises an error for jira_usage' do
expect { described_class.jira_usage }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
it 'does not raise an error' do
expect { subject }.not_to raise_error
end end
it 'jira usage works when queries time out' do context 'when metric calls find_in_batches' do
allow_any_instance_of(ActiveRecord::Relation) let(:metric_method) { :find_in_batches }
.to receive(:find_in_batches).and_raise(ActiveRecord::StatementInvalid.new(''))
it 'does not raise an error for jira_usage' do
expect { described_class.jira_usage }.not_to raise_error expect { described_class.jira_usage }.not_to raise_error
end end
end end
end
end
it 'includes a recording_ce_finished_at timestamp' do
expect(subject[:recording_ce_finished_at]).to be_a(Time)
end
end
describe '.system_usage_data_monthly' do describe '.system_usage_data_monthly' do
let_it_be(:project) { create(:project, created_at: 3.days.ago) } let_it_be(:project) { create(:project, created_at: 3.days.ago) }
...@@ -1355,10 +1382,21 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do ...@@ -1355,10 +1382,21 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
context 'when queries time out' do context 'when queries time out' do
before do before do
allow_any_instance_of(ActiveRecord::Relation) allow_any_instance_of(ActiveRecord::Relation).to receive(:count).and_raise(ActiveRecord::StatementInvalid)
.to receive(:count).and_raise(ActiveRecord::StatementInvalid.new('')) allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
end end
context 'with should_raise_for_dev? true' do
let(:should_raise_for_dev) { true }
it 'raises an error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
it 'returns -1 for email campaign data' do it 'returns -1 for email campaign data' do
expected_data = { expected_data = {
"in_product_marketing_email_create_0_sent" => -1, "in_product_marketing_email_create_0_sent" => -1,
...@@ -1397,6 +1435,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do ...@@ -1397,6 +1435,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
expect(subject).to eq(expected_data) expect(subject).to eq(expected_data)
end end
end end
end
context 'when there are entries' do context 'when there are entries' do
before do before do
......
...@@ -5,6 +5,30 @@ require 'spec_helper' ...@@ -5,6 +5,30 @@ require 'spec_helper'
RSpec.describe Gitlab::Utils::UsageData do RSpec.describe Gitlab::Utils::UsageData do
include Database::DatabaseHelpers include Database::DatabaseHelpers
shared_examples 'failing hardening method' do
before do
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
stub_const("Gitlab::Utils::UsageData::FALLBACK", fallback)
allow(failing_class).to receive(failing_method).and_raise(ActiveRecord::StatementInvalid)
end
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
it 'returns the fallback' do
expect(subject).to eq(fallback)
end
end
context 'with should_raise_for_dev? true' do
let(:should_raise_for_dev) { true }
it 'raises an error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
describe '#add_metric' do describe '#add_metric' do
let(:metric) { 'UuidMetric'} let(:metric) { 'UuidMetric'}
...@@ -22,11 +46,14 @@ RSpec.describe Gitlab::Utils::UsageData do ...@@ -22,11 +46,14 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.count(relation, batch: false)).to eq(1) expect(described_class.count(relation, batch: false)).to eq(1)
end end
it 'returns the fallback value when counting fails' do context 'when counting fails' do
stub_const("Gitlab::Utils::UsageData::FALLBACK", 15) subject { described_class.count(relation, batch: false) }
allow(relation).to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
expect(described_class.count(relation, batch: false)).to eq(15) let(:fallback) { 15 }
let(:failing_class) { relation }
let(:failing_method) { :count }
it_behaves_like 'failing hardening method'
end end
end end
...@@ -39,11 +66,14 @@ RSpec.describe Gitlab::Utils::UsageData do ...@@ -39,11 +66,14 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.distinct_count(relation, batch: false)).to eq(1) expect(described_class.distinct_count(relation, batch: false)).to eq(1)
end end
it 'returns the fallback value when counting fails' do context 'when counting fails' do
stub_const("Gitlab::Utils::UsageData::FALLBACK", 15) subject { described_class.distinct_count(relation, batch: false) }
allow(relation).to receive(:distinct_count_by).and_raise(ActiveRecord::StatementInvalid.new(''))
expect(described_class.distinct_count(relation, batch: false)).to eq(15) let(:fallback) { 15 }
let(:failing_class) { relation }
let(:failing_method) { :distinct_count_by }
it_behaves_like 'failing hardening method'
end end
end end
...@@ -155,14 +185,24 @@ RSpec.describe Gitlab::Utils::UsageData do ...@@ -155,14 +185,24 @@ RSpec.describe Gitlab::Utils::UsageData do
stub_const("Gitlab::Utils::UsageData::DISTRIBUTED_HLL_FALLBACK", 4) stub_const("Gitlab::Utils::UsageData::DISTRIBUTED_HLL_FALLBACK", 4)
end end
it 'returns fallback if counter raises WRONG_CONFIGURATION_ERROR' do context 'when counter raises WRONG_CONFIGURATION_ERROR' do
expect(described_class.estimate_batch_distinct_count(relation, 'id', start: 1, finish: 0)).to eq 3 subject { described_class.estimate_batch_distinct_count(relation, 'id', start: 1, finish: 0) }
let(:fallback) { 3 }
let(:failing_class) { Gitlab::Database::PostgresHll::BatchDistinctCounter }
let(:failing_method) { :new }
it_behaves_like 'failing hardening method'
end end
it 'returns default fallback value when counting fails due to database error' do context 'when counting fails due to database error' do
allow(Gitlab::Database::PostgresHll::BatchDistinctCounter).to receive(:new).and_raise(ActiveRecord::StatementInvalid.new('')) subject { described_class.estimate_batch_distinct_count(relation) }
expect(described_class.estimate_batch_distinct_count(relation)).to eq(3) let(:fallback) { 3 }
let(:failing_class) { Gitlab::Database::PostgresHll::BatchDistinctCounter }
let(:failing_method) { :new }
it_behaves_like 'failing hardening method'
end end
it 'logs error and returns DISTRIBUTED_HLL_FALLBACK value when counting raises any error', :aggregate_failures do it 'logs error and returns DISTRIBUTED_HLL_FALLBACK value when counting raises any error', :aggregate_failures do
...@@ -187,13 +227,14 @@ RSpec.describe Gitlab::Utils::UsageData do ...@@ -187,13 +227,14 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.sum(relation, :column, batch_size: 100, start: 2, finish: 3)).to eq(1) expect(described_class.sum(relation, :column, batch_size: 100, start: 2, finish: 3)).to eq(1)
end end
it 'returns the fallback value when counting fails' do context 'when counting fails' do
stub_const("Gitlab::Utils::UsageData::FALLBACK", 15) subject { described_class.sum(relation, :column) }
allow(Gitlab::Database::BatchCount)
.to receive(:batch_sum) let(:fallback) { 15 }
.and_raise(ActiveRecord::StatementInvalid.new('')) let(:failing_class) { Gitlab::Database::BatchCount }
let(:failing_method) { :batch_sum }
expect(described_class.sum(relation, :column)).to eq(15) it_behaves_like 'failing hardening method'
end end
end end
...@@ -273,9 +314,23 @@ RSpec.describe Gitlab::Utils::UsageData do ...@@ -273,9 +314,23 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(histogram).to eq('2' => 1) expect(histogram).to eq('2' => 1)
end end
it 'returns fallback and logs canceled queries' do context 'when query timeout' do
subject do
with_statement_timeout(0.001) do
relation = AlertManagement::HttpIntegration.select('pg_sleep(0.002)')
described_class.histogram(relation, column, buckets: 1..100)
end
end
before do
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(should_raise_for_dev)
create(:alert_management_http_integration, :active, project: project1) create(:alert_management_http_integration, :active, project: project1)
end
context 'with should_raise_for_dev? false' do
let(:should_raise_for_dev) { false }
it 'logs canceled queries' do
expect(Gitlab::AppJsonLogger).to receive(:error).with( expect(Gitlab::AppJsonLogger).to receive(:error).with(
event: 'histogram', event: 'histogram',
relation: relation.table_name, relation: relation.table_name,
...@@ -284,12 +339,20 @@ RSpec.describe Gitlab::Utils::UsageData do ...@@ -284,12 +339,20 @@ RSpec.describe Gitlab::Utils::UsageData do
query: kind_of(String), query: kind_of(String),
message: /PG::QueryCanceled/ message: /PG::QueryCanceled/
) )
subject
end
with_statement_timeout(0.001) do it 'returns fallback' do
relation = AlertManagement::HttpIntegration.select('pg_sleep(0.002)') expect(subject).to eq(fallback)
histogram = described_class.histogram(relation, column, buckets: 1..100) end
end
expect(histogram).to eq(fallback) context 'with should_raise_for_dev? true' do
let(:should_raise_for_dev) { true }
it 'raises error' do
expect { subject }.to raise_error(ActiveRecord::QueryCanceled)
end
end end
end end
end end
......
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