summaryrefslogtreecommitdiff
path: root/app/models/namespace
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/namespace')
-rw-r--r--app/models/namespace/root_storage_size.rb31
-rw-r--r--app/models/namespace/root_storage_statistics.rb26
-rw-r--r--app/models/namespace/traversal_hierarchy.rb84
3 files changed, 107 insertions, 34 deletions
diff --git a/app/models/namespace/root_storage_size.rb b/app/models/namespace/root_storage_size.rb
deleted file mode 100644
index d61917e468e..00000000000
--- a/app/models/namespace/root_storage_size.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-class Namespace::RootStorageSize
- def initialize(root_namespace)
- @root_namespace = root_namespace
- end
-
- def above_size_limit?
- return false if limit == 0
-
- usage_ratio > 1
- end
-
- def usage_ratio
- return 0 if limit == 0
-
- current_size.to_f / limit.to_f
- end
-
- def current_size
- @current_size ||= root_namespace.root_storage_statistics&.storage_size
- end
-
- def limit
- @limit ||= Gitlab::CurrentSettings.namespace_storage_size_limit.megabytes
- end
-
- private
-
- attr_reader :root_namespace
-end
diff --git a/app/models/namespace/root_storage_statistics.rb b/app/models/namespace/root_storage_statistics.rb
index ae9b2f14343..2ad6ea59588 100644
--- a/app/models/namespace/root_storage_statistics.rb
+++ b/app/models/namespace/root_storage_statistics.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
class Namespace::RootStorageStatistics < ApplicationRecord
- STATISTICS_ATTRIBUTES = %w(storage_size repository_size wiki_size lfs_objects_size build_artifacts_size packages_size).freeze
+ SNIPPETS_SIZE_STAT_NAME = 'snippets_size'.freeze
+ STATISTICS_ATTRIBUTES = %W(storage_size repository_size wiki_size lfs_objects_size build_artifacts_size packages_size #{SNIPPETS_SIZE_STAT_NAME}).freeze
self.primary_key = :namespace_id
@@ -13,11 +14,15 @@ class Namespace::RootStorageStatistics < ApplicationRecord
delegate :all_projects, to: :namespace
def recalculate!
- update!(attributes_from_project_statistics)
+ update!(merged_attributes)
end
private
+ def merged_attributes
+ attributes_from_project_statistics.merge!(attributes_from_personal_snippets) { |key, v1, v2| v1 + v2 }
+ end
+
def attributes_from_project_statistics
from_project_statistics
.take
@@ -34,7 +39,22 @@ class Namespace::RootStorageStatistics < ApplicationRecord
'COALESCE(SUM(ps.wiki_size), 0) AS wiki_size',
'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size',
'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size',
- 'COALESCE(SUM(ps.packages_size), 0) AS packages_size'
+ 'COALESCE(SUM(ps.packages_size), 0) AS packages_size',
+ "COALESCE(SUM(ps.snippets_size), 0) AS #{SNIPPETS_SIZE_STAT_NAME}"
)
end
+
+ def attributes_from_personal_snippets
+ # Return if the type of namespace does not belong to a user
+ return {} unless namespace.type.nil?
+
+ from_personal_snippets.take.slice(SNIPPETS_SIZE_STAT_NAME)
+ end
+
+ def from_personal_snippets
+ PersonalSnippet
+ .joins('INNER JOIN snippet_statistics s ON s.snippet_id = snippets.id')
+ .where(author: namespace.owner_id)
+ .select("COALESCE(SUM(s.repository_size), 0) AS #{SNIPPETS_SIZE_STAT_NAME}")
+ end
end
diff --git a/app/models/namespace/traversal_hierarchy.rb b/app/models/namespace/traversal_hierarchy.rb
new file mode 100644
index 00000000000..cfb6cfdde74
--- /dev/null
+++ b/app/models/namespace/traversal_hierarchy.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+#
+# A Namespace::TraversalHierarchy is the collection of namespaces that descend
+# from a root Namespace as defined by the Namespace#traversal_ids attributes.
+#
+# This class provides operations to be performed on the hierarchy itself,
+# rather than individual namespaces.
+#
+# This includes methods for synchronizing traversal_ids attributes to a correct
+# state. We use recursive methods to determine the correct state so we don't
+# have to depend on the integrity of the traversal_ids attribute values
+# themselves.
+#
+class Namespace
+ class TraversalHierarchy
+ attr_accessor :root
+
+ def self.for_namespace(namespace)
+ new(recursive_root_ancestor(namespace))
+ end
+
+ def initialize(root)
+ raise StandardError.new('Must specify a root node') if root.parent_id
+
+ @root = root
+ end
+
+ # Update all traversal_ids in the current namespace hierarchy.
+ def sync_traversal_ids!
+ # An issue in Rails since 2013 prevents this kind of join based update in
+ # ActiveRecord. https://github.com/rails/rails/issues/13496
+ # Ideally it would be:
+ # `incorrect_traversal_ids.update_all('traversal_ids = cte.traversal_ids')`
+ sql = """
+ UPDATE namespaces
+ SET traversal_ids = cte.traversal_ids
+ FROM (#{recursive_traversal_ids}) as cte
+ WHERE namespaces.id = cte.id
+ AND namespaces.traversal_ids <> cte.traversal_ids
+ """
+ Namespace.connection.exec_query(sql)
+ end
+
+ # Identify all incorrect traversal_ids in the current namespace hierarchy.
+ def incorrect_traversal_ids
+ Namespace
+ .joins("INNER JOIN (#{recursive_traversal_ids}) as cte ON namespaces.id = cte.id")
+ .where('namespaces.traversal_ids <> cte.traversal_ids')
+ end
+
+ private
+
+ # Determine traversal_ids for the namespace hierarchy using recursive methods.
+ # Generate a collection of [id, traversal_ids] rows.
+ #
+ # Note that the traversal_ids represent a calculated traversal path for the
+ # namespace and not the value stored within the traversal_ids attribute.
+ def recursive_traversal_ids
+ root_id = Integer(@root.id)
+
+ """
+ WITH RECURSIVE cte(id, traversal_ids, cycle) AS (
+ VALUES(#{root_id}, ARRAY[#{root_id}], false)
+ UNION ALL
+ SELECT n.id, cte.traversal_ids || n.id, n.id = ANY(cte.traversal_ids)
+ FROM namespaces n, cte
+ WHERE n.parent_id = cte.id AND NOT cycle
+ )
+ SELECT id, traversal_ids FROM cte
+ """
+ end
+
+ # This is essentially Namespace#root_ancestor which will soon be rewritten
+ # to use traversal_ids. We replicate here as a reliable way to find the
+ # root using recursive methods.
+ def self.recursive_root_ancestor(namespace)
+ Gitlab::ObjectHierarchy
+ .new(Namespace.where(id: namespace))
+ .base_and_ancestors
+ .reorder(nil)
+ .find_by(parent_id: nil)
+ end
+ end
+end