diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-25 15:09:19 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-25 15:09:19 +0300 |
commit | a156fc95eb8499fec9cd081d30629f0faf18bfe9 (patch) | |
tree | ff59d44794ba9d8084e4d59057ec9507b3ba8e2f /app/models/namespaces/traversal/linear.rb | |
parent | 618be8f52d6349533c709a1d702e45b84338c36a (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/models/namespaces/traversal/linear.rb')
-rw-r--r-- | app/models/namespaces/traversal/linear.rb | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/app/models/namespaces/traversal/linear.rb b/app/models/namespaces/traversal/linear.rb index 8c042e5409d..294ef83b9b4 100644 --- a/app/models/namespaces/traversal/linear.rb +++ b/app/models/namespaces/traversal/linear.rb @@ -38,15 +38,31 @@ module Namespaces module Linear extend ActiveSupport::Concern + UnboundedSearch = Class.new(StandardError) + included do after_create :sync_traversal_ids, if: -> { sync_traversal_ids? } after_update :sync_traversal_ids, if: -> { sync_traversal_ids? && saved_change_to_parent_id? } + + scope :traversal_ids_contains, ->(ids) { where("traversal_ids @> (?)", ids) } end def sync_traversal_ids? Feature.enabled?(:sync_traversal_ids, root_ancestor, default_enabled: :yaml) end + def use_traversal_ids? + Feature.enabled?(:use_traversal_ids, root_ancestor, default_enabled: :yaml) + end + + def self_and_descendants + if use_traversal_ids? + lineage(self) + else + super + end + end + private # Update the traversal_ids for the full hierarchy. @@ -58,6 +74,38 @@ module Namespaces Namespace::TraversalHierarchy.for_namespace(root_ancestor).sync_traversal_ids! end + + # Make sure we drop the STI `type = 'Group'` condition for better performance. + # Logically equivalent so long as hierarchies remain homogeneous. + def without_sti_condition + self.class.unscope(where: :type) + end + + # Search this namespace's lineage. Bound inclusively by top node. + def lineage(top) + raise UnboundedSearch.new('Must bound search by a top') unless top + + without_sti_condition + .traversal_ids_contains(latest_traversal_ids(top)) + end + + # traversal_ids are a cached value. + # + # The traversal_ids value in a loaded object can become stale when compared + # to the database value. For example, if you load a hierarchy and then move + # a group, any previously loaded descendant objects will have out of date + # traversal_ids. + # + # To solve this problem, we never depend on the object's traversal_ids + # value. We always query the database first with a sub-select for the + # latest traversal_ids. + # + # Note that ActiveRecord will cache query results. You can avoid this by + # using `Model.uncached { ... }` + def latest_traversal_ids(namespace = self) + without_sti_condition.where('id = (?)', namespace) + .select('traversal_ids as latest_traversal_ids') + end end end end |