diff options
Diffstat (limited to 'spec/models/project_statistics_spec.rb')
-rw-r--r-- | spec/models/project_statistics_spec.rb | 168 |
1 files changed, 154 insertions, 14 deletions
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb index a6e2bcf1525..ef53de6ad82 100644 --- a/spec/models/project_statistics_spec.rb +++ b/spec/models/project_statistics_spec.rb @@ -455,35 +455,50 @@ RSpec.describe ProjectStatistics do end describe '.increment_statistic' do - shared_examples 'a statistic that increases storage_size' do + shared_examples 'a statistic that increases storage_size synchronously' do + let(:increment) { Gitlab::Counters::Increment.new(amount: 13) } + it 'increases the statistic by that amount' do - expect { described_class.increment_statistic(project, stat, 13) } + expect { described_class.increment_statistic(project, stat, increment) } .to change { statistics.reload.send(stat) || 0 } - .by(13) + .by(increment.amount) end it 'increases also storage size by that amount' do - expect { described_class.increment_statistic(project, stat, 20) } + expect { described_class.increment_statistic(project, stat, increment) } .to change { statistics.reload.storage_size } - .by(20) + .by(increment.amount) end it 'schedules a namespace aggregation worker' do expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async) .with(statistics.project.namespace.id) - described_class.increment_statistic(project, stat, 20) + described_class.increment_statistic(project, stat, increment) + end + + context 'when the project is pending delete' do + before do + project.update_attribute(:pending_delete, true) + end + + it 'does not change the statistics' do + expect { described_class.increment_statistic(project, stat, increment) } + .not_to change { statistics.reload.send(stat) } + end end end shared_examples 'a statistic that increases storage_size asynchronously' do + let(:increment) { Gitlab::Counters::Increment.new(amount: 13) } + it 'stores the increment temporarily in Redis', :clean_gitlab_redis_shared_state do - described_class.increment_statistic(project, stat, 13) + described_class.increment_statistic(project, stat, increment) Gitlab::Redis::SharedState.with do |redis| key = statistics.counter(stat).key - increment = redis.get(key) - expect(increment.to_i).to eq(13) + value = redis.get(key) + expect(value.to_i).to eq(increment.amount) end end @@ -493,9 +508,20 @@ RSpec.describe ProjectStatistics do .with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, described_class.name, statistics.id, stat) .and_call_original - expect { described_class.increment_statistic(project, stat, 20) } - .to change { statistics.reload.send(stat) }.by(20) - .and change { statistics.reload.send(:storage_size) }.by(20) + expect { described_class.increment_statistic(project, stat, increment) } + .to change { statistics.reload.send(stat) }.by(increment.amount) + .and change { statistics.reload.send(:storage_size) }.by(increment.amount) + end + + context 'when the project is pending delete' do + before do + project.update_attribute(:pending_delete, true) + end + + it 'does not change the statistics' do + expect { described_class.increment_statistic(project, stat, increment) } + .not_to change { [statistics.reload.send(stat), statistics.reload.send(:storage_size)] } + end end end @@ -508,7 +534,7 @@ RSpec.describe ProjectStatistics do context 'when adjusting :pipeline_artifacts_size' do let(:stat) { :pipeline_artifacts_size } - it_behaves_like 'a statistic that increases storage_size' + it_behaves_like 'a statistic that increases storage_size synchronously' end context 'when adjusting :packages_size' do @@ -518,9 +544,11 @@ RSpec.describe ProjectStatistics do end context 'when the amount is 0' do + let(:increment) { Gitlab::Counters::Increment.new(amount: 0) } + it 'does not execute a query' do project - expect { described_class.increment_statistic(project, :build_artifacts_size, 0) } + expect { described_class.increment_statistic(project, :build_artifacts_size, increment) } .not_to exceed_query_limit(0) end end @@ -532,4 +560,116 @@ RSpec.describe ProjectStatistics do end end end + + describe '.bulk_increment_statistic' do + let(:increments) { [10, 3].map { |amount| Gitlab::Counters::Increment.new(amount: amount) } } + let(:total_amount) { increments.sum(&:amount) } + + shared_examples 'a statistic that increases storage_size synchronously' do + it 'increases the statistic by that amount' do + expect { described_class.bulk_increment_statistic(project, stat, increments) } + .to change { statistics.reload.send(stat) || 0 } + .by(total_amount) + end + + it 'increases also storage size by that amount' do + expect { described_class.bulk_increment_statistic(project, stat, increments) } + .to change { statistics.reload.storage_size } + .by(total_amount) + end + + it 'schedules a namespace aggregation worker' do + expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async) + .with(statistics.project.namespace.id) + + described_class.bulk_increment_statistic(project, stat, increments) + end + + context 'when the project is pending delete' do + before do + project.update_attribute(:pending_delete, true) + end + + it 'does not change the statistics' do + expect { described_class.bulk_increment_statistic(project, stat, increments) } + .not_to change { statistics.reload.send(stat) } + end + end + end + + shared_examples 'a statistic that increases storage_size asynchronously' do + it 'stores the increment temporarily in Redis', :clean_gitlab_redis_shared_state do + described_class.bulk_increment_statistic(project, stat, increments) + + Gitlab::Redis::SharedState.with do |redis| + key = statistics.counter(stat).key + increment = redis.get(key) + expect(increment.to_i).to eq(total_amount) + end + end + + it 'schedules a worker to update the statistic and storage_size async', :sidekiq_inline do + expect(FlushCounterIncrementsWorker) + .to receive(:perform_in) + .with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, described_class.name, statistics.id, stat) + .and_call_original + + expect { described_class.bulk_increment_statistic(project, stat, increments) } + .to change { statistics.reload.send(stat) }.by(total_amount) + .and change { statistics.reload.send(:storage_size) }.by(total_amount) + end + + context 'when the project is pending delete' do + before do + project.update_attribute(:pending_delete, true) + end + + it 'does not change the statistics' do + expect { described_class.bulk_increment_statistic(project, stat, increments) } + .not_to change { [statistics.reload.send(stat), statistics.reload.send(:storage_size)] } + end + end + end + + context 'when adjusting :build_artifacts_size' do + let(:stat) { :build_artifacts_size } + + it_behaves_like 'a statistic that increases storage_size asynchronously' + + context 'when :project_statistics_bulk_increment flag is disabled' do + before do + stub_feature_flags(project_statistics_bulk_increment: false) + end + + it 'calls increment_statistic on once with the sum of the increments' do + total_amount = increments.sum(&:amount) + expect(statistics) + .to receive(:increment_statistic).with(stat, have_attributes(amount: total_amount)).and_call_original + + described_class.bulk_increment_statistic(project, stat, increments) + end + + it_behaves_like 'a statistic that increases storage_size asynchronously' + end + end + + context 'when adjusting :pipeline_artifacts_size' do + let(:stat) { :pipeline_artifacts_size } + + it_behaves_like 'a statistic that increases storage_size synchronously' + end + + context 'when adjusting :packages_size' do + let(:stat) { :packages_size } + + it_behaves_like 'a statistic that increases storage_size asynchronously' + end + + context 'when using an invalid column' do + it 'raises an error' do + expect { described_class.bulk_increment_statistic(project, :id, increments) } + .to raise_error(ArgumentError, "Cannot increment attribute: id") + end + end + end end |