Commit bd773c54 authored by Tyler Amos's avatar Tyler Amos Committed by Corinna Wiesner

Add method to calculate excess namespace repo size

The method `total_repository_size_excess` calculates the sum of the
repository_size_excess for all the projects in the namespace. It is
calculated by adding the results of two separate queries to increase
query performances.
parent f3715e33
......@@ -194,6 +194,26 @@ module EE
RootStorageSize.new(root_ancestor).above_size_limit?
end
def total_repository_size_excess
strong_memoize(:total_repository_size_excess) do
namespace_size_limit = actual_size_limit
namespace_limit_arel = Arel::Nodes::SqlLiteral.new(namespace_size_limit.to_s.presence || 'NULL')
condition = 'projects.repository_size_limit != 0 AND project_statistics.repository_size > projects.repository_size_limit'
total_excess = total_repository_size_excess_calculation(condition, ::Project.arel_table[:repository_size_limit])
if namespace_size_limit.to_i > 0
condition = 'projects.repository_size_limit IS NULL AND project_statistics.repository_size > :namespace_size_limit'
sanitized_condition = self.class.sanitize_sql_array([condition, namespace_size_limit: namespace_size_limit])
total_excess += total_repository_size_excess_calculation(sanitized_condition, namespace_limit_arel)
end
total_excess
end
end
def actual_size_limit
::Gitlab::CurrentSettings.repository_size_limit
end
......@@ -410,5 +430,15 @@ module EE
def shared_runners_remaining_minutes
[actual_shared_runners_minutes_limit.to_f - shared_runners_minutes.to_f, 0].max
end
def total_repository_size_excess_calculation(condition, limit)
select_sql = Arel::Nodes::NamedFunction.new('SUM', [::ProjectStatistics.arel_table[:repository_size] - limit]).to_sql
all_projects
.joins(:statistics)
.where(condition)
.pluck(Arel.sql(select_sql)) # rubocop:disable Rails/Pick
.first || 0
end
end
end
......@@ -1431,6 +1431,74 @@ RSpec.describe Namespace do
end
end
describe '#total_repository_size_excess' do
let_it_be(:namespace) { create(:namespace) }
def create_project(repository_size:, repository_size_limit:)
create(:project, namespace: namespace, repository_size_limit: repository_size_limit).tap do |project|
create(:project_statistics, project: project, repository_size: repository_size)
end
end
before do
namespace.clear_memoization(:total_repository_size_excess)
end
context 'projects with a variety of repository sizes and limits' do
before_all do
create_project(repository_size: 100, repository_size_limit: nil)
create_project(repository_size: 150, repository_size_limit: nil)
create_project(repository_size: 200, repository_size_limit: nil)
create_project(repository_size: 100, repository_size_limit: 0)
create_project(repository_size: 150, repository_size_limit: 0)
create_project(repository_size: 200, repository_size_limit: 0)
create_project(repository_size: 300, repository_size_limit: 400)
create_project(repository_size: 400, repository_size_limit: 400)
create_project(repository_size: 500, repository_size_limit: 300)
end
context 'when namespace-level repository_size_limit is not set' do
it 'returns the total excess size of projects with repositories that exceed the size limit' do
allow(namespace).to receive(:actual_size_limit).and_return(nil)
expect(namespace.total_repository_size_excess).to eq(200)
end
end
context 'when namespace-level repository_size_limit is 0 (unlimited)' do
it 'returns the total excess size of projects with repositories that exceed the size limit' do
allow(namespace).to receive(:actual_size_limit).and_return(0)
expect(namespace.total_repository_size_excess).to eq(200)
end
end
context 'when namespace-level repository_size_limit is a positive number' do
it 'returns the total excess size of projects with repositories that exceed the size limit' do
allow(namespace).to receive(:actual_size_limit).and_return(150)
expect(namespace.total_repository_size_excess).to eq(250)
end
end
end
context 'when all projects have repository_size_limit of 0 (unlimited)' do
before do
create_project(repository_size: 100, repository_size_limit: 0)
create_project(repository_size: 150, repository_size_limit: 0)
create_project(repository_size: 200, repository_size_limit: 0)
allow(namespace).to receive(:actual_size_limit).and_return(150)
end
it 'returns zero regardless of the namespace or instance-level repository_size_limit' do
expect(namespace.total_repository_size_excess).to eq(0)
end
end
end
describe '#actual_size_limit' do
let(:namespace) { build(:namespace) }
......
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