네임스페이스

네임스페이스는 프로젝트와 관련 리소스를 포함하는 컨테이너입니다. NamespaceGroup, ProjectNamespace, 그리고 UserNamespace의 하위 클래스를 통해 생성됩니다.

graph TD Namespace -.- Group Namespace -.- ProjectNamespace Namespace -.- UserNamespace

사용자는 하나의 UserNamespace를 가지며, 여러 네임스페이스의 멤버가 될 수 있습니다.

graph TD 네임스페이스 -.- 그룹 네임스페이스 -.- 프로젝트네임스페이스 네임스페이스 -.- 유저네임스페이스 사용자 -- 하나를 가짐 --- UserNamespace 네임스페이스 --- 멤버 --- 사용자

그룹은 재귀적인 계층적 관계에 있습니다. 그룹은 많은 프로젝트네임스페이스를 가지고 하나의 프로젝트를 상위에 두게 됩니다.

graph TD 그룹 -- 많은 것을 가짐 --- 프로젝트네임스페이스 -- 하나를 가짐 --- 프로젝트 그룹 -- 많은 것을 가짐 --- 그룹

네임스페이스 조회

네임스페이스 계층 구조를 조회하는 데 사용할 수 있는 일련의 메서드가 있습니다. 이러한 메서드는 표준 Rails ActiveRecord::Relation 객체를 생성합니다. 이 메서드들은 하나는 네임스페이스 객체에서 작동하고 다른 하나는 조합 가능한 네임스페이스 스코프로 작동합니다.

이 메서드들은 객체 메서드는 단일 네임스페이스 계층 내에서 작동하며 스코프는 계층을 걸쳐 작동합니다.

다음은 네임스페이스 계층을 조회하는 일부 메서드의 일부 목록입니다.

루트 네임스페이스

루트는 계층 구조에서 가장 상위에 있는 네임스페이스입니다. 루트는 nil parent_id를 가집니다.

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A.B active class A sel
Namespace.where(...).roots

namespace_object.root_ancestor

자손 네임스페이스

네임스페이스의 자손은 해당 네임스페이스의 하위, 그들의 하위 등을 포함합니다.

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A active class A.A.A,A.A.B sel

self_and_descendants를 통해 자신과 자신의 자손을 반환할 수 있습니다.

Namespace.where(...).self_and_descendants

namespace_object.self_and_descendants

우리 자신을 제외한 자손만 반환할 수 있습니다:

Namespace.where(...).self_and_descendants(include_self: false)

namespace_object.descendants

해당 스코프 메서드에 .descendants라는 이름을 지정할 수 없으므로, Object 메서드의 이름을 덮어쓰게 됩니다.

전체 레코드를 반환하는 대신 자손 ID를 반환하는 것이 더 효율적일 수 있습니다:

Namespace.where(...).self_and_descendant_ids
Namespace.where(...).self_and_descendant_ids(include_self: false)

namespace_object.self_and_descendant_ids
namespace_object.descendant_ids

조상 네임스페이스

네임스페이스의 조상은 해당 네임스페이스의 부모, 그들의 부모 등을 포함합니다.

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A active class A sel

self_and_ancestors를 통해 자신과 자신의 조상을 반환할 수 있습니다.

Namespace.where(...).self_and_ancestors

namespace_object.self_and_ancestors

우리 자신을 제외한 조상만 반환할 수 있습니다:

Namespace.where(...).self_and_ancestors(include_self: false)

namespace_object.ancestors

해당 스코프 메서드에 .ancestors라는 이름을 지정할 수 없으므로, Module 메서드의 이름을 덮어쓰게 됩니다.

전체 레코드를 반환하는 대신 조상 ID를 반환하는 것이 더 효율적일 수 있습니다:

Namespace.where(...).self_and_ancestor_ids
Namespace.where(...).self_and_ancestor_ids(include_self: false)

namespace_object.self_and_ancestor_ids
namespace_object.ancestor_ids

계층 구조

네임스페이스 계층 구조는 네임스페이스, 해당 자손, 그리고 해당 조상을 포함합니다.

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A active class A,A.A.A,A.A.B sel

네임스페이스 계층 구조를 조회할 수 있습니다:

Namespace.where(...).self_and_hierarchy

namespace_object.self_and_hierarchy

재귀적 쿼리

위의 쿼리들을 선형 쿼리라고하는데, 이것들은 표준 SQL 쿼리를 수행하기 위해 namespaces.traversal_ids 열을 사용한다는 점에서 재귀 CTE 쿼리 대신에 사용됩니다. 필요한 경우 일련의 레거시 재귀 쿼리도 접근 가능합니다:

Namespace.where(...).recursive_self_and_descendants
Namespace.where(...).recursive_self_and_descendants(include_self: false)
Namespace.where(...).recursive_self_and_descendant_ids
Namespace.where(...).recursive_self_and_descendant_ids(include_self: false)
Namespace.where(...).recursive_self_and_ancestors
Namespace.where(...).recursive_self_and_ancestors(include_self: false)
Namespace.where(...).recursive_self_and_ancestor_ids
Namespace.where(...).recursive_self_and_ancestor_ids(include_self: false)
Namespace.where(...).recursive_self_and_hierarchy

namespace_object.recursive_root_ancestor
namespace_object.recursive_self_and_descendants
namespace_object.recursive_descendants
namespace_object.recursive_self_and_descendant_ids
namespace_object.recursive_descendant_ids
namespace_object.recursive_self_and_ancestors
namespace_object.recursive_ancestors
namespace_object.recursive_self_and_ancestor_ids
namespace_object.recursive_ancestor_ids
namespace_object.recursive_self_and_hierarchy

네임스페이스 쿼리 구현

선형 쿼리는 namespaces.traversal_ids 배열 열을 사용하여 실행됩니다. 각 배열은 루트 Namespace에서 현재 Namespace까지의 정렬된 Namespace ID 세트를 나타냅니다.

시나리오가 주어진 경우:

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A.B active

Namespace A.A.Btraversal_ids[A, A.A, A.A.B]가 될 것입니다.

traversal_ids에는 해당 영역에서 작업하는 경우 유용한 몇 가지 속성이 있습니다.

  • 모든 Namespace의 루트는 traversal_ids[1]에 의해 제공됩니다. PostgreSQL 배열 인덱스는 1부터 시작한다는 점을 유의하십시오.
  • 현재 Namespace의 ID는 traversal_ids[array_length(traversal_ids, 1)]에 의해 제공됩니다.
  • Namespace 조상은 traversal_ids로 나타납니다.
  • Namespacetraversal_ids는 해당 자손의 traversal_ids의 하위 집합입니다. traversal_ids = [1,2,3]Namespace는 모든 하위 항목이 [1,2,3,...]로 시작합니다.
  • PostgreSQL 배열은 [1] < [1,1] < [2]와 같이 순서가 지정됩니다.

이러한 속성을 사용하여 rootancestors가 이미 traversal_ids에 의해 제공된다는 것을 알 수 있습니다.

객체 하위 쿼리를 사용할 때 우리는 @> 배열 연산자를 활용하는데, 이 연산자는 배열 내에 다른 배열이 포함되는지 테스트할 것입니다. @> 연산자는 검색 공간이 커짐에 따라 다소 느리다는 것이 밝혀졌습니다. 검색 공간이 큰 범위 쿼리에는 다른 방법이 사용됩니다. 범위 쿼리에서는 비교 연산자를 배열 정렬 속성과 결합합니다.

traversal_ids = [1,2,3]Namespace의 모든 하위 항목은 traversal_ids[1,2,3]보다 크지만 [1,2,4]보다 작습니다. 이 예에서 [1,2,3][1,2,4]는 형제이며, [1,2,4][1,2,3] 다음의 형제입니다. traversal_ids의 다음 형제를 찾는 SQL 함수인 next_traversal_ids_sibling이 제공됩니다.

gitlabhq_development=# select next_traversal_ids_sibling(ARRAY[1,2,3]);
 next_traversal_ids_sibling
----------------------------
 {1,2,4}
(1 row)

그런 다음 우리는 비교 연산자를 사용하여 하위 선형 쿼리 범위를 구축합니다.

WHERE namespaces.traversal_ids > ARRAY[1,2,3]
  AND namespaces.traversal_ids < next_traversal_ids_sibling(ARRAY[1,2,3])

상위집합

Namespace 쿼리는 중복된 결과를 반환할 가능성이 있습니다. 예를 들어 AA.A의 하위 항목을 찾는 쿼리를 고려해보십시오.

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A,A.A active class A.A.A,A.A.B,A.B,A.B.A,A.B.B sel
namespaces = Namespace.where(name: ['A', 'A.A'])

namespaces.self_and_descendants

=> A.A, A.A.A, A.A.B, A.B, A.B.A, A.B.B

AA.A의 하위 항목을 찾는 것은 불필요합니다. 왜냐하면 A.A는 이미 A의 하위 항목이기 때문입니다. 극단적인 경우에는 이로 인해 과도한 I/O가 발생하여 성능이 저하될 수 있습니다.

중복된 Namespace는 쿼리에서 제거됩니다. 이때 traversal_ids 속성의 Namespace ID가 쿼리되는 Namespaces 집합에 속하는 다른 NamespaceID와 일치하면 해당 조건이 매칭되어 조상이 쿼리되는 Namespaces 집합에 존재하며, 따라서 현재 Namespace가 중복되었음을 의미합니다. 이 최적화는 그렇지 않으면 매우 느릴 수 있는 특수한 경우에 대한 성능을 크게 향상시킵니다.