다중 데이터베이스

GitLab의 더 나은 확장성을 위해 GitLab 애플리케이션 데이터베이스를 여러 데이터베이스로 분해했습니다. 두 데이터베이스는 mainci입니다. GitLab은 하나의 데이터베이스 또는 두 개의 데이터베이스로 실행되는 것을 지원합니다. GitLab.com에서는 두 개의 별도 데이터베이스를 사용하고 있습니다.

Cells 아키텍처를 구축하는 데 목적으로, 데이터베이스를 더 분해하여 gitlab_main_clusterwide 라는 다른 데이터베이스를 도입합니다.

GitLab 스키마

다른 데이터베이스 간 허용된 패턴을 올바르게 찾기 위해 GitLab 애플리케이션은 데이터베이스 사전을 구현합니다.

데이터베이스 사전은 테이블을 gitlab_schema에 가상 분류하여 제공하며, 개념적으로 PostgreSQL 스키마와 유사합니다. 우리는 CI를 분해한 기능을 더 잘 격리하기 위해 데이터베이스 스키마를 활용하기로 결정했는데, 이는 복잡한 마이그레이션 절차로 PostgreSQL 스키마를 사용할 수 없음을 의미합니다. 대신에 응용 프로그램 수준의 분류 개념을 구현했습니다. GitLab의 각 테이블에는 gitlab_schema가 할당되어야 합니다:

데이터베이스 설명 비고
gitlab_main main: 데이터베이스에 저장되는 모든 테이블입니다. 현재 Cells 아키텍처를 구축하는 데 새로운 gitlab_main_cell로 대체되고 있습니다. gitlab_main_cell 스키마는 GitLab 설치의 셀 내에 로컬인 모든 테이블을 설명합니다. 예를 들어 projectsgroups입니다.
gitlab_main_clusterwide GitLab 설치의 클러스터 전체에 저장된 모든 테이블이며, Cells 아키텍처에서 사용됩니다. 예를 들어, usersapplication_settings입니다.  
gitlab_ci ci: 데이터베이스에 저장되는 모든 CI 테이블입니다 (예: ci_pipelines, ci_builds)  
gitlab_geo geo: 데이터베이스에 저장되는 모든 Geo 테이블입니다 (예: project_registry, secondary_usage_data)  
gitlab_shared 모든 분해된 데이터베이스를 통해 데이터를 포함하는 모든 애플리케이션 테이블 (예: loose_foreign_keys_deleted_records) Gitlab::Database::SharedModel에서 상속되는 모델을 위한 것입니다.
gitlab_internal Rails 및 PostgreSQL의 모든 내부 테이블 (예: ar_internal_metadata, schema_migrations, pg_*)  
gitlab_pm package_metadata를 저장하는 모든 테이블입니다 gitlab_main의 별칭입니다.

추가로 분해된 데이터베이스와 함께 도입될 스키마

스키마 사용은 기본 클래스를 사용하는 것을 강제시킵니다:

  • gitlab_main/gitlab_main_cell에 대해 ApplicationRecord
  • gitlab_main_clusterwide에 대해 MainClusterwide::ApplicationRecord
  • gitlab_ci에 대해 Ci::ApplicationRecord
  • gitlab_geo에 대해 Geo::TrackingBase
  • gitlab_shared에 대해 Gitlab::Database::SharedModel
  • gitlab_pm에 대해 PackageMetadata::ApplicationRecord

gitlab_main_cellgitlab_main_clusterwide 스키마 선택에 대한 지침

사용 사례에 따라 특징이 셀 로컬 또는 클러스터 전체적일 수 있으므로 해당 기능에 사용되는 테이블은 적절한 스키마를 사용해야 합니다.

테이블에 적절한 스키마를 선택할 때, 다음 가이드라인을 고려하십시오(Cells 아키텍처의 일부):

  • 기본적으로 gitlab_main_cell을 선택하세요: 대부분의 테이블은 기본적으로 gitlab_main_cell 스키마에 할당되었다고 예상됩니다. 해당 테이블의 데이터가 projects 또는 namespaces와 관련이 있다면 이 스키마를 선택하세요.
  • 테넌트 규모 그룹과 상담하세요: 특정 테이블에 gitlab_main_clusterwide 스키마가 더 적합하다고 생각된다면, 테넌트 규모 그룹의 승인을 받으십시오. 이는 확장성 영향을 미치며 스키마 선택을 재검토할 수 있기 때문에 중요합니다.

기존 테이블을 어떻게 분류하는 지 이해하려면 이 대시보드를 사용할 수 있습니다.

스키마가 할당된 후, 병합 요청 파이프라인이 다음과 같은 이유로 실패할 수 있으며, 이에 대한 지침을 따르면 수정할 수 있습니다:

모든 셀 로컬 테이블에 대한 샤딩 키 정의

다음 gitlab_schema와 함께 모든 테이블은 “셀 로컬”로 간주됩니다:

  • gitlab_main_cell
  • gitlab_ci

새롭게 생성된 셀 로컬 테이블은 해당 테이블의 db/docs/ 파일에 정의된 sharding_key를 가져야 합니다.

샤딩 키의 목적은 조직 격리 블루프린트에 문서화되어 있지만, 간단히 말해서 이 열은 데이터베이스의 특정 행을 소유한 조직을 결정하는 표준 방법을 제공하는 데 사용됩니다. 해당 열은 향후에 데이터가 조직 간 경계를 넘지 못하도록 제약을 강제하는 데 사용될 것입니다. 또한 셀 간 데이터 이관에 대한 통합적인 방법을 제공하는 데도 사용될 것입니다.

실제 외래 키의 이름은 아무 것이나 될 수 있지만, 반드시 projects 또는 groups의 행을 참조해야 합니다. 선택한 sharding_key 열은 null일 수 없어야 합니다.

여러 개의 sharding_key를 설정하는 것도 허용되지만, nullable 열을 사용하는 경우, 테이블에는 열 중 적어도 하나는 null이 아니어야 하는 것을 올바르게 보장하는 확인 제약 조건이 있어야 합니다.

다음은 유효한 샤딩 키의 예시입니다:

  • 테이블 항목은 프로젝트에 속합니다:

     sharding_key:
       project_id: projects
    
  • 테이블 항목은 프로젝트에 속하고 외래 키가 target_project_id인 경우:

     sharding_key:
       target_project_id: projects
    
  • 테이블 항목은 네임스페이스/그룹에만 속합니다:

     sharding_key:
       namespace_id: namespaces
    
  • 테이블 항목은 네임스페이스/그룹에 속하고 외래 키가 group_id인 경우:

     sharding_key:
       group_id: namespaces
    
  • 테이블 항목은 네임스페이스 또는 프로젝트에 속합니다:

     sharding_key:
       project_id: projects
       namespace_id: namespaces
    

샤딩 키는 변경할 수 없어야 합니다

sharding_key의 선택은 항상 변경할 수 없어야 합니다. 따라서 귀하의 기능이 사용자 경험에 데이터를 프로젝트 또는 그룹/네임스페이스 간에 이동시키는 것을 요구하는 경우, 샤딩 키를 관리하기 위한 옵션을 논의하기 위해 초기에 테넌트 스케일 팀에 문의해야 할 수도 있습니다. 실제로 project_id 열을 기존 이슈 행의 project_id 열을 변경하는 것이 아니라, 새로운 이슈 행을 생성하고 데이터베이스에서 원래 이슈 행으로 링크를 만드는 이슈 이동 기능에 대한 예시가 있습니다. 데이터 이동을 허용해야 하는 특히 어려운 기존 기능이 있는 경우, 샤딩 키를 어떻게 관리할지에 대한 옵션에 대해 이야기할 수도 있습니다.

프로젝트와 네임스페이스에 대해 동일한 샤딩 키 정의

개발자는 또한 해당 테이블이 그룹 및 프로젝트 통합 블루프린트을 따라 개발 중인 프로젝트에 속하는 테이블에 대해 namespace_id만 사용할 수도 있습니다. 이 경우 namespace_id는 해당 네임스페이스가 속한 그룹의 아이디(ID)여야 합니다.

desired_sharding_key를 정의하여 sharding_key를 자동으로 백필하기

sharding_key가 없는 수백 개의 테이블에 sharding_key를 백필해야 합니다. 이 프로세스는 https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136800와 같은 병합 요청을 생성하여 새로운 열을 추가하고, 관련 테이블에서 데이터를 백필한 후 이후의 병합 요청을 생성하여 인덱스, 외래 키, 및 not-null 제약 조건을 추가하는 과정을 포함할 것입니다.

개발자들에게 반복적인 작업량을 최소화하기 위해 해당 특정 테이블에 sharding_key를 백필하는 방법을 간결하게 설명하는 선언적인 방법이 도입되었습니다. 이 내용은 나중에 자동화를 위해 모든 필요한 병합 요청을 생성하는 데 사용될 것입니다.

desired_sharding_key의 예시는 https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139336에 추가되었으며 다음과 같습니다:

--- # db/docs/security_findings.yml
table_name: security_findings
classes:
- Security::Finding

...

desired_sharding_key:
  project_id:
    references: projects
    backfill_via:
      parent:
        foreign_key: scanner_id
        table: vulnerability_scanners
        sharding_key: project_id
        belongs_to: scanner

해당 YAML 데이터가 어떻게 사용될 것인지 가장 잘 이해하기 위해 직접 해보고자 할 것입니다. 수동으로 생성한 병합 요청에 매핑할 수 있으며, 아이디어는 나중에 자동으로 이를 생성하는 것입니다. YAML의 내용은 배치된 백그라운드 마이그레이션에서 sharding_key를 백필할 때 사용됩니다. 또한 before_save에서 sharding_key를 자동으로 채우기 위해 모델에 추가될 belongs_to 관계를 명시합니다.

부모 테이블도 desired_sharding_key를 가지고 있는 경우 desired_sharding_key를 정의하는 방법

기본적으로 desired_sharding_key 구성은 선택한 sharding_key가 부모 테이블에 있는지 검증합니다. 그러나, 만약 부모 테이블도 desired_sharding_key 구성을 가지고 있고, 자체적으로 backfill을 기다리고 있다면 awaiting_backfill_on_parent 필드를 포함해야 합니다. 예를 들어:

desired_sharding_key:
  project_id:
    references: projects
    backfill_via:
      parent:
        foreign_key: package_file_id
        table: packages_package_files
        sharding_key: project_id
        belongs_to: package_file
    awaiting_backfill_on_parent: true

이러한 desired_sharding_key 구조가 sharding_key를 backfill하는 데 적합하지 않은 특수한 경우가 있을 수 있습니다. 이러한 경우에는 테이블을 소유한 팀이 sharding_key를 수동으로 추가하기 위해 필요한 병합 요청을 작성해야 합니다.

일부 테이블에서 sharding 키가 없어도 되도록 설정

특정 테이블은 다음을 추가함으로써 sharding 키가 없어도 됩니다.

exempt_from_sharding: true

테이블의 데이터베이스 사전 파일에 위와 같이 추가함으로써 특정 테이블에서 sharding 키가 필요 없도록 설정할 수 있습니다. 현재 JiHu 특정 테이블에 대해 이러한 경우가 있습니다. 이 테이블들은 .com 데이터베이스에 어떠한 데이터도 포함하고 있지 않기 때문입니다. 이러한 변경은 !145905에서 구현되었습니다.

gitlab_schema의 영향

gitlab_schema의 사용은 응용 프로그램에 상당한 영향을 미칩니다. gitlab_schema의 주요 목적은 서로 다른 데이터 액세스 패턴 간의 장벽을 소개하는 것입니다.

이것은 다음을 위한 주요 분류의 원천으로 사용됩니다:

gitlab_shared의 특수 목적

gitlab_shared는 설계상 모든 분해된 데이터베이스에 걸쳐 있는 데이터를 포함하는 특별한 경우입니다. 이 분류는 응용 프로그램에서 정의된 테이블(예: loose_foreign_keys_deleted_records)을 설명합니다.

데이터에 접근할 때 gitlab_shared를 사용할 때 특별한 처리가 필요하니 주의하세요. gitlab_shared는 구조 뿐만 아니라 데이터도 공유하기 때문에 응용 프로그램은 모든 데이터를 순차적으로 횡단하는 방법으로 작성되어야 합니다.

Gitlab::Database::EachDatabase.each_model_connection([MySharedModel]) do |connection, connection_name|
  MySharedModel.select_all_data...
end

따라서 gitlab_shared 테이블의 데이터를 수정하는 마이그레이션은 모든 분해된 데이터베이스에서 실행될 것으로 예상됩니다.

gitlab_internal의 특수 목적

gitlab_internal은 Rails에서 정의된 테이블(예: schema_migrations 또는 ar_internal_metadata) 뿐만 아니라 내부 PostgreSQL 테이블(예: pg_attribute)을 설명합니다. 그 주된 목적은 다른 데이터베이스를 지원하는 것이며, 이는 gitlab_shared와 같은 애플리케이션에서 정의된 테이블(예: loose_foreign_keys_deleted_records)이 누락될 수 있는 Geo와 같은 데이터베이스에서 유효한 Rails 데이터베이스입니다.

gitlab_pm의 특수 목적

gitlab_pm은 공개 저장소에 대한 패키지 메타데이터를 저장합니다. 이 데이터는 라이선스 컴플라이언스 및 종속성 스캔 제품 범주에 사용되며 Composition Analysis 그룹에 의해 유지됩니다. 이는 미래에 다른 데이터베이스로 경로를 지정하기 쉽도록 하는 gitlab_main의 별칭입니다.

마이그레이션

Migrations for Multiple Databases를 참조하세요.

CI/CD 데이터베이스

단일 데이터베이스 구성

기본적으로 GDK는 여러 개의 데이터베이스로 실행되도록 구성되어 있습니다.

경고: 동일한 개발 인스턴스에서 단일 데이터베이스와 다중 데이터베이스를 번갈아가며 사용하는 것은 권장하지 않습니다. ci 데이터베이스의 데이터는 단일 데이터베이스 모드에서 접근할 수 없습니다. 단일 데이터베이스를 사용하려면 별도의 개발 인스턴스를 사용해야 합니다.

GDK를 단일 데이터베이스로 구성하려면:

  1. GDK 루트 디렉토리에서 다음을 실행합니다:

    gdk config set gitlab.rails.databases.ci.enabled false
    
  2. GDK를 다시 구성합니다:

    gdk reconfigure
    

다중 데이터베이스를 사용하려면 gitlab.rails.databases.ci.enabledtrue로 설정하고 gdk reconfigure를 실행하세요.

cici가 아닌 테이블 간 조인 제거

데이터베이스 간에 조인하는 쿼리는 오류를 발생시킵니다. GitLab 14.3에서 새로운 쿼리에 대해서만 도입되었습니다. 기존 쿼리는 오류를 발생시키지 않습니다.

여러 개의 별도 데이터베이스에서 GitLab을 실행할 수 있기 때문에 단일 쿼리에서 ci 테이블과 ci가 아닌 테이블을 참조하는 것은 불가능합니다. 따라서 SQL 쿼리에서 어떤 종류의 JOIN을 사용하는 것도 작동하지 않습니다.

데이터베이스 간 조인 제거에 대한 제안

다음 섹션은 데이터베이스를 가로지르는 조인을 식별한 실제 예제와 이를 수정하는 방법에 대한 가능한 제안입니다.

코드 제거

지금까지 본 가장 간단한 해결책은 사용되지 않는 기존 스코프입니다. 이것은 가장 쉽게 수정할 수 있는 예제입니다. 따라서 첫 번째 단계는 코드가 사용되지 않는지 조사한 후 제거하는 것입니다. 다음은 몇 가지 실제 예입니다:

코드가 사용되는 경우가 더 있을 수 있지만, 필요한지 또는 기능이 이렇게 동작해야 하는지를 평가할 수 있습니다. 새로운 열과 테이블을 추가하여 복잡성을 높이기 전에 솔루션을 단순화하고 요구 사항을 충족시킬 수 있는지 고려해보세요. 조인 쿼리를 제거하는 것이 필요한 경우가 있는데, 이는 특정 UsageData의 계산 방식을 변경하여 평가 중입니다. https://gitlab.com/gitlab-org/gitlab/-/issues/336170 여기에서 UsageData에 중점을 두는 경우 평가할 수 있는 좋은 후보 사례입니다. 또는 이러한 메트릭을 사용하는 사람이 아무도 없을 수 있으므로 그런 메트릭을 제거할 수 있을 것입니다.

includes 대신 preload 사용

Rails의 includespreload 메소드는 모두 N+1 쿼리를 피하는 방법입니다. Rails의 includes 메소드는 휴리스틱 접근 방식을 사용하여 테이블에 조인해야 하는지 또는 별도의 쿼리에서 모든 레코드를 로드할 수 있는지를 결정합니다. 이 메소드는 때로는 필요 없는 경우에도 조인을 실행할 수 있으므로 명확하게 따로 쿼리에서 데이터를 로드하는 preload를 사용하여 조인을 피할 수 있습니다. 이 경우 실제 예시는 다음과 같습니다: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67655.

중복 조인 제거

가끔씩 쿼리가 과도하게(또는 중복으로) 조인하는 경우가 있습니다.

일반적 예는 쿼리가 A에서 B를 거쳐 C로 조인하는 경우입니다. C에 있는 행의 수를 세고 B의 외래 키와 NOT NULL 제약 조건이 있는지 여부를 신경 쓰는 경우, 해당 행을 세는 데 충분할 수 있습니다. 예를 들어 이전에는 MR 71811에서 project.runners.count를 수행하는 쿼리가 있었는데, 이는 다음과 같은 쿼리를 생성했습니다: sql select count(*) from projects inner join ci_runner_projects on ci_runner_projects.project_id = projects.id where ci_runner_projects.runner_id IN (1, 2, 3) 이는 코드가 project.runner_projects.count로 변경되어 교차 조인을 피할 수 있습니다. 이는 다음과 같은 쿼리를 통해 동일한 결과를 생성합니다: sql select count(*) from ci_runner_projects where ci_runner_projects.runner_id IN (1, 2, 3)

다른 일반적인 중복 조인은 다른 테이블로 조인해 모두 필터링한 다음 기본 키로 필터링해야 할 경우입니다. 이것을 외래 키에 필터링할 수 있는데까지, 더불어 이미 security_scans에 외래 키 build_id가 있기 때문에 코드를 joins(:scan).where(security_scans: { build_id: build_ids })로 변경할 수 있으며, 이는 다음과 같은 쿼리를 생성합니다: sql select ... inner join security_scans where security_scans.build_id IN (1, 2, 3)

이러한 중복 조인을 제거하는 예에서는 교차 조인을 제거할 뿐만 아니라 더 단순하고 빠른 쿼리가 생성되는 추가 혜택이 있습니다.

제한된 pluck를 따른 find

pluck 또는 pick을 사용하여 id의 배열을 가져오는 것은 반환된 배열이 유한하다는 것이 보장된 경우를 제외하고 권장되지 않습니다. 보통 이는 결과가 최대 1개일 것을 알고 있는 경우에 좋은 패턴입니다. 또는 메모리에 있는 id(또는 사용자 이름)의 목록을 다른 동일한 크기의 목록에 매핑해야 하는 경우에도 좋은 패턴입니다. 결과가 무제한으로 생성되는 일대다 관계의 id 목록을 매핑할 때는 적합하지 않습니다. 그러면 반환된 id를 사용하여 관련 레코드를 얻을 수 있습니다:

allowed_user_id = board_user_finder
  .where(user_id: params['assignee_id'])
  .pick(:user_id)

User.find_by(id: allowed_user_id)

이를 사용한 예제를 다음에서 확인할 수 있습니다. https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126856

가끔 조인을 pluck으로 변환하는 것이 쉬워 보일 수 있지만, 이로 인해 메모리로 무제한 수의 id를 로드한 뒤 이를 다시 Postgres로 쿼리하는 것이 종종 발생합니다. 이러한 경우는 확장되지 않으며, 다른 옵션을 시도할 것을 권장합니다. pluck 데이터에 limit를 적용하여 메모리에 제한을 둘 수 있을 것 같지만, 이는 사용자에 대한 예측할 수 없는 결과를 가져오며 가장 큰 고객(우리 자신 포함)에게 가장 문제가 많이 발생하는 경우가 많습니다. 따라서 이를 권장하지 않습니다.

테이블에 외래 키 비정규화

비정규화는 특정 쿼리를 간소화하거나 성능을 향상시키기 위해 중복된 미리 계산된 데이터를 테이블에 추가하는 것을 의미합니다. 이 경우, 특정 중간 테이블을 거치는 조인을 수행할 때 유용할 수 있습니다.

일반적으로 데이터베이스 스키마를 모델링할 때 중복 데이터를 사용하는 것은 추가 저장 공간을 사용합니다. 중복 데이터를 동기화 유지해야 합니다. 때로는 정규화된 데이터가 성능이 덜 나아서 GitLab에서는 데이터베이스 쿼리의 성능을 향상시키기 위해 비정규화를 사용해왔습니다. 위 문제들은 다음 조건이 충족될 때 완화됩니다.

  1. 데이터가 많지 않을 때 (예: 정수 열만 있는 경우).
  2. 데이터가 자주 업데이트되지 않을 때 (예: 대부분의 테이블에 대해 project_id 열이 거의 업데이트되지 않음).

찾은 한 가지 예는 security_scans 테이블입니다. 이 테이블에는 조인할 수 있는 외래 키 security_scans.build_id가 있어 프로젝트에 조인할 수 있습니다. 따라서 다음과 같이 프로젝트에 조인할 수 있습니다.

select projects.* from security_scans
inner join ci_builds on security_scans.build_id = ci_builds.id
inner join projects on ci_builds.project_id = projects.id

이 쿼리의 문제는 ci_builds가 다른 두 테이블과는 다른 데이터베이스에 있다는 것입니다.

이 경우의 해결책은 security_scansproject_id 열을 추가하는 것입니다. 이는 추가 저장 공간을 별로 사용하지 않을 뿐더러 이러한 기능의 작동 방식으로 인해 업데이트되지 않습니다(빌드는 프로젝트를 거의 옮기지 않음).

이로써 쿼리가 간단해졌습니다.

select projects.* from security_scans
inner join projects on security_scans.project_id = projects.id

이는 또한 추가 테이블을 통해 조인할 필요가 없어져 성능도 향상됩니다.

이 접근 방식은 https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66963에서 구현된 것을 확인할 수 있습니다. 이 MR은 비슷한 쿼리를 해결하기 위해 pipeline_id를 비정규화합니다.

추가 테이블로 비정규화

이전의 비정규화(추가 열 추가)가 특정 경우에 작동하지 않는 경우가 있습니다. 이는 데이터가 1:1이 아니거나 테이블이 이미 너무 넓은 경우(예: projects 테이블에 열을 더 추가하지 말아야 하는 경우)일 수 있습니다.

이 경우 별도의 테이블에 추가 데이터를 저장하기로 결정할 수 있습니다.

이 접근 방식이 사용된 예제 중 하나는 Project.with_code_coverage 스코프를 구현하는 데 사용되었습니다. 이 스코프는 기본적으로 한 번이라도 코드 커버리지 기능을 사용한 프로젝트 리스트를 좁히는 데 사용되었습니다. 이 쿼리(간소화)는 다음과 같습니다.

select projects.* from projects
inner join ci_daily_build_group_report_results on ci_daily_build_group_report_results.project_id = projects.id
where ((data->'coverage') is not null)
and ci_daily_build_group_report_results.default_branch = true
group by projects.id

이 작업은 아직 진행 중이지만 현재 계획은 projects_with_ci_feature_usage라는 새 테이블을 도입하는 것입니다. 이 테이블에는 project_idci_feature 두 개의 열이 있습니다. 이 테이블은 ci_daily_build_group_report_results를 사용하여 코드 커버리지를 처음으로 만드는 경우에 기록될 것입니다. 따라서 새 쿼리는 다음과 같습니다.

select projects.* from projects
inner join projects_with_ci_feature_usage on projects_with_ci_feature_usage.project_id = projects.id
where projects_with_ci_feature_usage.ci_feature = 'code_coverage'

위 예제에서는 텍스트 열을 사용했지만 공간을 절약하기 위해 열거를 사용하는 것이 좋습니다.

이 새로운 설계의 단점은 이전에 ci_daily_build_group_report_results가 삭제된 경우에 업데이트해야 할 수도 있다는 것입니다. 그러나 도메인에 따라 삭제가 불가능하거나 불가능한 경우가 많거나 목록 페이지에서 프로젝트를 표시하는 것이 문제가 되지 않을 수 있으므로 이 작업이 필요하지 않을 수도 있습니다. 또한 도메인에서 필요할 때나 언제든 필요한 경우에 이러한 행을 삭제하는 로직을 구현할 수도 있습니다.

마지막으로, 이러한 비정규화 및 새로운 쿼리는 더 적은 조인을 하고 더 적은 필터링이 필요하기 때문에 성능도 향상됩니다.

has_one 또는 has_many through: 관계에 disable_joins 사용하기

가끔 서로 다른 데이터베이스를 거치는 테이블 간에 has_one ... through: 또는 has_many ... through:를 사용하면 조인 쿼리가 발생합니다. 이러한 조인은 때로는 disable_joins:true를 추가함으로써 해결할 수 있습니다. 이 기능은 Rails의 특징이며, 우리는 이 기능을 백포팅했습니다. 또한 disable_joins를 활성화하는 람다 구문을 지원하기 위해 이 기능을 확장했습니다. 이 기능을 사용하는 경우 심각한 성능 하락이 있을 경우를 대비하여 피처 플래그를 사용하는 것을 권장합니다.

이를 사용하는 예는 https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66709/diffs에서 확인할 수 있습니다.

DB 쿼리에 대한 변경사항이 있는 경우 변경 전후로 SQL을 분석하고 비교하는 것이 중요합니다. disable_joins는 실제 has_many 또는 has_one 관계의 로직에 따라 매우 나쁜 성능을 낳을 수 있습니다. 확인해야 할 주요 사항은 최종 결과 집합을 구성하는 데 사용되는 중간 결과 집합 중에서 데이터가 제한되지 않고 로드되는지 여부입니다. SQL을 확인하고 각각이 어떤 방식으로 제한되는지 확인하여 판단할 수 있습니다. LIMIT 1 절이 있는지 또는 고유한 열을 기준으로 한 제한이 있는지 확인할 수 있습니다. 제한되지 않는 중간 데이터 집합은 메모리에 너무 많은 ID를 로드할 수 있기 때문에 매우 나쁜 성능으로 이어질 수 있습니다.

매우 나쁜 성능을 볼 수 있는 예는 다음 가상의 코드입니다.

class Project
  has_many :pipelines
  has_many :builds, through: :pipelines
end

class Pipeline
  has_many :builds
end

class Build
  belongs_to :pipeline
end

def some_action
  @builds = Project.find(5).builds.order(created_at: :desc).limit(10)
end

위의 예에서 some_action은 다음과 같은 쿼리를 생성합니다.

select * from builds
inner join pipelines on builds.pipeline_id = pipelines.id
where pipelines.project_id = 5
order by builds.created_at desc
limit 10

그러나 관계를 다음과 같이 변경하면:

class Project
  has_many :pipelines
  has_many :builds, through: :pipelines, disable_joins: true

다음의 2개의 쿼리를 얻을 수 있습니다.

select id from pipelines where project_id = 5;

select * from builds where pipeline_id in (...)
order by created_at desc
limit 10;

첫 번째 쿼리는 어떤 고유 열로 제한되지 않거나 LIMIT 절이 없기 때문에 제한 없이 메모리에 무제한으로 많은 파이프라인 ID를 로드할 수 있으며, 이후의 쿼리로 전송됩니다. 이는 Rails 애플리케이션 및 데이터베이스에서 매우 나쁜 성능을 낳을 수 있습니다. 이와 같은 경우에는 쿼리를 다시 작성하거나 교차 조인을 제거하는 데 사용되는 위에서 설명한 다른 패턴을 살펴보아야 할 수도 있습니다.

교차 조인을 올바르게 제거했는지 검증 방법

RSpec는 자동으로 모든 SQL 쿼리가 데이터베이스 간에 조인되지 않도록 유효성을 검증하도록 구성되어 있습니다. 이 유효성 검증이 spec/support/database/cross-join-allowlist.yml에서 비활성화된 경우에도 with_cross_joins_prevented를 사용하여 격리된 코드 블록을 유효성을 검증할 수 있습니다.

다음과 같이 이 방법을 사용할 수 있습니다:

it 'does not join across databases' do
  with_cross_joins_prevented do
    ::Ci::Build.joins(:project).to_a
  end
end

이 방법을 사용하면 쿼리가 두 개의 데이터베이스를 교차해서 조인하는 경우 예외가 발생합니다. 이전의 예제는 다음과 같이 조인을 제거하여 해결됩니다:

it 'does not join across databases' do
  with_cross_joins_prevented do
    ::Ci::Build.preload(:project).to_a
  end
end

이 방법을 사용하여 교차 조인을 해결한 실제 예제는 https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67655에서 확인할 수 있습니다.

기존 교차 조인을 위한 허용 목록

교차 조인을 식별하는 가장 쉬운 방법은 실패한 파이프라인을 통해서입니다.

예를 들어 !130038에서 notification_settings 테이블을 gitlab_main_cell 스키마로 이동했을 때, db/docs/notification_settings.yml 파일에서 해당 스키마로 표시함으로써 파이프라인이 실패했습니다.

다음과 같은 에러가 발생했습니다:

Database::PreventCrossJoins::CrossJoinAcrossUnsupportedTablesError:

Unsupported cross-join across 'users, notification_settings' querying 'gitlab_main_clusterwide, gitlab_main_cell' discovered when executing query 'SELECT "users".* FROM "users" WHERE "users"."id" IN (SELECT "notification_settings"."user_id" FROM ((SELECT "notification_settings"."user_id" FROM "notification_settings" WHERE "notification_settings"."source_id" = 119 AND "notification_settings"."source_type" = 'Project' AND (("notification_settings"."level" = 3 AND EXISTS (SELECT true FROM "notification_settings" "notification_settings_2" WHERE "notification_settings_2"."user_id" = "notification_settings"."user_id" AND "notification_settings_2"."source_id" IS NULL AND "notification_settings_2"."source_type" IS NULL AND "notification_settings_2"."level" = 2)) OR "notification_settings"."level" = 2))) notification_settings)'

이러한 교차조인 쿼리를 허용 목록으로 지정하여 파이프라인을 성공시키려면 다음을 사용해야 합니다.

데이터베이스를 교차 조인을 허용하도록 명시적으로 허용하는 것은 ::Gitlab::Database.allow_cross_joins_across_databases 도우미 메서드로 코드를 래핑합니다. 다른 방법은 relation.allow_cross_joins_across_databases로 특정 관계를 지정하는 것입니다.

이 방법은 다음과 같이 사용할 수 있습니다:

# 데이터베이스 객체에서 블록을 범위로 한정화
::Gitlab::Database.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336590') do
  subject.perform(1, 4)
end
# 특정 관계를 데이터베이스를 허용하는 방식으로 표시
def find_actual_head_pipeline
  all_pipelines
    .allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336891')
    .for_sha_or_source_sha(diff_head_sha)
    .first
end

모델 관계나 스코프에서 이 방법을 사용하는 것은 다음과 같이 표현됩니다:

class Group < Namespace
 has_many :users, -> {
    allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/422405")
  }, through: :group_members
end

경고: 관계를 무시하면 의도치 않은 결과가 발생할 수 있고, 때로는 데이터 손실로 이어질 수 있습니다. 이슈 424307에서 알 수 있듯이, 기존의 ActiveRecord 관계를 변경하여 교차 조인을 허용했다면 문제가 발생할 수 있으니 이런 식으로 하지 마십시오.

url 매개변수는 교차 조인을 수정할 마일스톤이 있는 문제를 가리켜야 합니다. 교차 조인이 마이그레이션에서 사용되는 경우 코드를 수정할 필요가 없습니다. 자세한 내용은 https://gitlab.com/gitlab-org/gitlab/-/issues/340017을 참조하십시오.

교차 데이터베이스 트랜잭션 제거

여러 데이터베이스를 다룰 때 둘 이상의 데이터베이스에 영향을 주는 데이터 수정에 주의하는 것이 중요합니다. 소개됨 GitLab 14.4에는 자동화된 확인이 있어 교차 데이터베이스 수정을 방지합니다.

최소한 두 개의 다른 데이터베이스가 하나의 데이터베이스 서버에서 시작된 트랜잭션 중에 수정되면 애플리케이션은 교차 데이터베이스 수정 오류를 발생시킵니다(테스트 환경에서만).

예시:

# 메인 DB에서 트랜잭션 열기
ApplicationRecord.transaction do
  ci_build.update!(updated_at: Time.current) # CI DB에서 UPDATE
  ci_build.project.update!(updated_at: Time.current) # 메인 DB에서 UPDATE
end
# 오류가 발생합니다: 교차 데이터베이스의 데이터 수정 'main, ci'이 발생했습니다
# 'ci_build, projects' 테이블을 수정하는 트랜잭션 내부에서

위의 코드 예시는 트랜잭션 내에서 두 레코드의 타임스탬프를 업데이트합니다. CI 데이터베이스 분해 작업이 진행 중이므로 데이터베이스 트랜잭션의 스키마를 보장할 수 없습니다. 두 번째 업데이트 쿼리가 실패하면 첫 번째 업데이트 쿼리가 실행되지 않습니다. 왜냐하면 ci_build 레코드는 다른 데이터베이스 서버에 위치하기 때문입니다. 자세한 정보는 트랜잭션 가이드라인 페이지를 참조하세요.

교차 데이터베이스 트랜잭션 수정

데이터베이스 간 트랜잭션은 코드를 Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.temporary_ignore_tables_in_transaction 메서드로 감싸면 명시적으로 허용할 수 있습니다.

Rails 콜백에서 교차 데이터베이스 트랜잭션을 위해 cross_database_ignore_tables 메서드를 사용할 수 있습니다.

이러한 메서드는 기존 코드에만 사용해야 합니다.

temporary_ignore_tables_in_transaction 도우미 메서드는 다음과 같이 사용할 수 있습니다:

class GroupMember < Member
   def update_two_factor_requirement
     return unless user

     # 멤버와 사용자/사용자_상세정보/사용자_설정을 포함하는 교차 데이터베이스 트랜잭션을 표시하고 무시하기
     Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.temporary_ignore_tables_in_transaction(
       %w[users user_details user_preferences], url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424288'
     ) do
       user.update_two_factor_requirement
     end
   end
end

cross_database_ignore_tables 메서드는 다음과 같이 사용할 수 있습니다:

class Namespace < ApplicationRecord
  include CrossDatabaseIgnoredTables

  # 네임스페이스와 라우트/리디렉트_라우트 간 라우트 및 리디렉트_라우트를 포함하는 교차 데이터베이스 트랜잭션 표시 및 무시
  cross_database_ignore_tables %w[routes redirect_routes], url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424277'
end
트랜잭션 블록 제거

열린 트랜잭션이 없으면 교차 데이터베이스 수정 확인이 오류를 발생시킬 수 없습니다. 이 변경으로 일관성을 희생합니다. 첫 번째 UPDATE 쿼리 후 어플리케이션 오류가 발생하면 두 번째 UPDATE 쿼리는 절대 실행되지 않습니다.

transaction 블록 없이 동일한 코드:

ci_build.update!(updated_at: Time.current) # CI DB
ci_build.project.update!(updated_at: Time.current) # 메인 DB
비동기 처리

작업이 일관되게 완료되는 것에 대해 더 많은 보장이 필요하다면 백그라운드 작업 내에서 실행할 수 있습니다. 백그라운드 작업은 비동기로 예약되어 오류 발생시 여러 번 다시 시도됩니다. 일관성을 도입할 때에도 약간의 불일치가 여전히 존재할 수 있습니다.

예시:

current_time = Time.current

MyAsyncConsistencyJob.perform_async(cu_build.id)

ci_build.update!(updated_at: current_time)
ci_build.project.update!(updated_at: current_time)

MyAsyncConsistencyJob은 타임스탬프도 업데이트를 시도할 것입니다.

완벽한 일관성을 목표로

우리는 현재 하나의 데이터베이스에서 가진 것과 유사한 일관성 특성을 보장하는 도구(필요하지 않을 수도 있음)가 없습니다. 작업 중인 코드가 이러한 특성을 필요로 한다고 생각된다면 해당 코드를 테스트할 때 교차 데이터베이스 수정 확인을 비활성화하여 예외 처리된 테스트 코드를 작성하고 후속 이슈를 생성할 수 있습니다.

allow_cross_database_modification_within_transaction(url: 'gitlab issue URL') do
  ApplicationRecord.transaction do
    ci_build.update!(updated_at: Time.current) # CI DB에서 UPDATE
    ci_build.project.update!(updated_at: Time.current) # 메인 DB에서 UPDATE
  end
end

도움이 필요하다면 언제든 Pods 그룹에 문의하세요.

dependent: :nullifydependent: :destroy 교차 데이터베이스에서 피하세요

교차 데이터베이스를 대상으로 dependent: :nullify 또는 dependent: :destroy를 사용해야 하는 경우가 있을 수 있습니다. 이것은 기술적으로 가능하지만, 외부 트랜잭션에서 실행되는 이유로 문제가 발생할 수 있습니다. 이 방법으로 발생하는 교차 데이터베이스 트랜잭션은 혼란스러운 결과로 이어져 데이터베이스 분해 시에 예상치 못한 버그를 발생시킬 수 있습니다.

데이터베이스 외부에서(예: 객체 저장소) 데이터를 정리해야 하는 비교적 복잡한 객체의 경우, 우리는 dependent: :restrict_with_error 설정을 권장합니다. 이러한 객체들은 명시적으로 미리 제거되어야 합니다. dependent: :restrict_with_error를 사용하면 부모 객체를 삭제하는 것을 금지하여 정리되지 않은 것이 있을 경우를 방지할 수 있습니다.

PostgreSQL에서 자식 레코드 자체를 정리해야 하는 경우 완화된 외래 키를 사용하는 것을 고려해보세요.

여러 데이터베이스를 건너는 외부 키

두 개의 서로 다른 PostgreSQL 데이터베이스 간을 참조하는 외부 키를 사용하는 많은 곳이 있습니다. 이는 두 개의 별도 PostgreSQL 데이터베이스에서는 불가능하므로 PostgreSQL에서 얻는 동작을 성능적으로 복제해야 합니다. PostgreSQL이 제공하는 데이터 보증으로 인해 잘못된 참조를 만드는 것을 방지하지만, 고아 데이터가 생기지 않도록 하거나 아이디어가 없는 레코드가 생기지 않도록 하는 카스케이딩 삭제를 대체할 방법이 필요합니다. 따라서 “느슨한 외부 키” 라는 고아 레코드를 정리하는 비동기적 과정을 만들었습니다.

기존 서로 다른 데이터베이스의 외부 키를 위한 화이트리스트

서로 다른 데이터베이스의 외부 키를 식별하는 가장 쉬운 방법은 실패한 파이프라인을 통해 확인하는 것입니다.

예를 들어, !130038 에서 notification_settings 테이블을 gitlab_main_cell 스키마로 옮겼으며 이를 db/docs/notification_settings.yml 파일에서 표시했습니다.

notification_settings.user_idusers를 가리키는 열인데, users 테이블은 다른 데이터베이스에 속하기 때문에 이제 이는 서로 다른 데이터베이스를 가리키는 외부 키로 처리됩니다.

우리는 이러한 서로 다른 데이터베이스의 외부 키의 경우를 캡처하기 위해 no_cross_db_foreign_keys_spec.rb 스펙을 가지고 있으며, 이 경우 해당 스펙이 실패하게 될 것입니다.

파이프라인이 성공하게 하기 위해 이러한 서로 다른 데이터베이스의 외부 키를 화이트리스트로 지정해야 합니다.

이를 위해 이러한 외부 키를 동일한 스펙의 예외로 추가하여 명시적으로 존재할 수 있도록 합니다 (이 예시 와 같이). 이렇게 함으로써 스펙은 실패하지 않게 됩니다.

나중에, 이 외부 키를 !130080 에서 한 것처럼 느슨한 외부 키로 변환할 수 있습니다.

여러 데이터베이스에 대한 테스트

테스트 CI 파이프라인에서 기본적으로 우리는 mainci 두 개의 데이터베이스를 사용하여 GitLab을 테스트합니다. 그러나 MR(병합 요청)에서는 예를 들어 일부 데이터베이스 관련 코드를 수정하거나 MR에 ~"pipeline:run-single-db" 라벨을 추가할 때와 같이 우리의 테스트를 추가로 single-dbsingle-db-ci-connection 두 가지 다른 데이터베이스 모드에서 실행합니다.

테스트가 특정 데이터베이스 모드에서 실행되어야 하는 상황을 처리하기 위해 우리는 일부 RSpec 도우미가 있습니다. 이 도우미는 테스트가 실행될 수 있는 모드를 제한하고 다른 모드에서는 건너뛸 수 있도록 합니다.

도우미명 테스트 실행됨
skip_if_shared_database(:ci) 여러 데이터베이스 에서만
skip_if_database_exists(:ci) single-dbsingle-db-ci-connection 에서만
skip_if_multiple_databases_are_setup(:ci) single-db 에서만
skip_if_multiple_databases_not_setup(:ci) single-db-ci-connection여러 데이터베이스 에서만

main_clusterwide를 포함한 여러 데이터베이스의 테스트

기본적으로 CI 파이프라인에서는 main_clusterwide 연결을 설정하지 않습니다. 그러나 ~"pipeline:run-clusterwide-db" 라벨을 추가하면 파이프라인은 3개의 연결, main, cimain_clusterwide와 함께 실행됩니다.

참고: 이 설정은 아직 완전히 준비되지 않았으며, 이 설정으로 파이프라인을 실행하면 일부 작업에 실패할 수 있습니다. 2023년 7월 현재, 이는 Cells를 구축하는 동안 변경 사항을 테스트하기 위해 group::tenant scale 에 의해 사용되고 있습니다.

데이터베이스 스키마에 속하지 않는 테이블에 대한 쓰기 잠금

CI 데이터베이스가 프로모트되고 두 데이터베이스가 완전히 분할된 경우, split brain 상황을 방지하기 위한 추가적인 안전장치로 gitlab:db:lock_writes Rake 태스크를 실행합니다. 이 명령은 다음에 대해 쓰기를 잠금 처리합니다:

  • CI 데이터베이스의 gitlab_main 테이블
  • Main 데이터베이스의 gitlab_ci 테이블

이 Rake 태스크는 모든 테이블에 트리거를 추가하여 잠겨야 하는 테이블에 대해 INSERT, UPDATE, DELETE, 또는 TRUNCATE 문이 실행되는 것을 방지합니다.

이 작업이 단일 데이터베이스만 사용하는 GitLab 설정에서 실행된 경우 어떤 테이블도 잠겨지지 않습니다.

이 작업을 취소하기 위해 반대의 Rake 태스크인 gitlab:db:unlock_writes를 실행합니다.

모니터링

테이블 락 상태는 Database::MonitorLockedTablesWorker를 사용하여 확인합니다. 필요에 따라 테이블을 락 상태로 전환할 수 있습니다.

이 스크립트의 결과는 Kibana에서 확인할 수 있습니다. 카운트가 0이 아니면 락 상태로 전환되어야 할 몇 개의 테이블이 락되어 있지 않을 수 있습니다. json.extra.database_monitor_locked_tables_worker.results.ci.tables_need_locksjson.extra.database_monitor_locked_tables_worker.results.main.tables_need_locks 필드에는 잘못된 상태의 테이블 목록이 포함되어야 합니다.

Elasticsearch Watcher를 사용하여 로깅을 모니터링합니다. Watcher의 이름은 table_locks_needed이며 소스 코드는 GitLab Runbook 저장소에 있습니다. 경고는 #g_tenant-scale Slack 채널로 전송됩니다.

자동화

자동으로 테이블을 락 상태로 전환하는 두 가지 프로세스가 있습니다:

수동으로 테이블 락 상태로 전환

수동으로 테이블을 락 상태로 전환해야 하는 경우 데이터베이스 마이그레이션을 사용하세요. 일반적인 마이그레이션을 만들고 테이블 락을 설정하는 코드를 추가하세요. 예를 들어, CI 데이터베이스의 shards 테이블에 쓰기 락을 설정하려면 다음과 같이 합니다:

class EnableWriteLocksOnShards < Gitlab::Database::Migration[2.2]
  def up
    # 주 데이터베이스에서는 마이그레이션을 건너뛰어야 합니다.
    # DDL 마이그레이션에서 restrict_gitlab_migration을 사용할 수 없습니다.
    return if Gitlab::Database.db_config_name(connection) != 'ci'

    Gitlab::Database::LockWritesManager.new(
      table_name: 'shards',
      connection: connection,
      database_name: :ci,
      with_retries: false
    ).lock_writes
  end

  def down
    # 아무것도 실행하지 않음
  end
end

테이블 잘라내기

mainci 데이터베이스가 완전히 분할되면 테이블을 잘라내어 디스크 공간을 확보할 수 있습니다. 이렇게 하면 더 작은 데이터 세트가 됩니다. 예를 들어, CI 데이터베이스의 users 테이블의 데이터는 더 이상 읽히지 않으며 업데이트되지도 않습니다. 따라서 이러한 데이터를 잘라내고 테이블을 비울 수 있습니다.

이를 위해 GitLab은 각 데이터베이스에 대해 하나씩 두 개의 Rake 작업을 제공합니다:

  • gitlab:db:truncate_legacy_tables:main은 메인 데이터베이스에서 CI 테이블을 잘라냅니다.
  • gitlab:db:truncate_legacy_tables:ci는 CI 데이터베이스에서 메인 테이블을 잘라냅니다.

참고: 이러한 작업은 해당 데이터베이스의 테이블이 쓰기 락된 경우에만 실행할 수 있습니다.

경고: 이 섹션의 예제는 DRY_RUN=true을 사용합니다. 이렇게 하면 실제로 데이터가 잘려나가지 않습니다. GitLab은 이러한 작업을 실행하기 전에 반드시 백업이 있어야 한다고 권장합니다.

이러한 작업은 실제로 데이터를 변경하지 않으면서 이러한 작업이 무엇을 하는지 볼 수 있는 옵션이 있습니다:

$ sudo DRY_RUN=true gitlab-rake gitlab:db:truncate_legacy_tables:main
I, [2023-07-14T17:08:06.665151 #92505]  INFO -- : DRY RUN:
I, [2023-07-14T17:08:06.761586 #92505]  INFO -- : Truncating legacy tables for the database main
I, [2023-07-14T17:08:06.761709 #92505]  INFO -- : SELECT set_config('lock_writes.ci_build_needs', 'false', false)
I, [2023-07-14T17:08:06.765272 #92505]  INFO -- : SELECT set_config('lock_writes.ci_build_pending_states', 'false', false)
I, [2023-07-14T17:08:06.768220 #92505]  INFO -- : SELECT set_config('lock_writes.ci_build_report_results', 'false', false)
[...]
I, [2023-07-14T17:08:06.957294 #92505]  INFO -- : TRUNCATE TABLE ci_build_needs, ci_build_pending_states, ci_build_report_results, ci_build_trace_chunks, ci_build_trace_metadata, ci_builds, ci_builds_metadata, ci_builds_runner_session, ci_cost_settings, ci_daily_build_group_report_results, ci_deleted_objects, ci_freeze_periods, ci_group_variables, ci_instance_variables, ci_job_artifact_states, ci_job_artifacts, ci_job_token_project_scope_links, ci_job_variables, ci_minutes_additional_packs, ci_namespace_mirrors, ci_namespace_monthly_usages, ci_partitions, ci_pending_builds, ci_pipeline_artifacts, ci_pipeline_chat_data, ci_pipeline_messages, ci_pipeline_metadata, ci_pipeline_schedule_variables, ci_pipeline_schedules, ci_pipeline_variables, ci_pipelines, ci_pipelines_config, ci_platform_metrics, ci_project_mirrors, ci_project_monthly_usages, ci_refs, ci_resource_groups, ci_resources, ci_runner_machines, ci_runner_namespaces, ci_runner_projects, ci_runner_versions, ci_runners, ci_running_builds, ci_secure_file_states, ci_secure_files, ci_sources_pipelines, ci_sources_projects, ci_stages, ci_subscriptions_projects, ci_trigger_requests, ci_triggers, ci_unit_test_failures, ci_unit_tests, ci_variables, external_pull_requests, p_ci_builds, p_ci_builds_metadata, p_ci_job_annotations, p_ci_runner_machine_builds, taggings, tags RESTRICT

이러한 작업은 먼저 잘라내어야 하는 테이블을 찾습니다. 잘라내기는 데이터베이스 트랜잭션에서 제거하는 데이터 양을 제한해야 하므로 여러 단계로 이루어집니다. 테이블은 외래 키의 정의에 따라 특정 순서로 처리되며 한 번에 처리되는 테이블 수는 작업을 호출할 때 숫자를 추가함으로써 변경할 수 있습니다. 기본값은 5입니다:

sudo DRY_RUN=true gitlab-rake gitlab:db:truncate_legacy_tables:main\[10\]

또한 UNTIL_TABLE 변수를 설정하여 잘라내어야 하는 테이블의 수를 제한할 수 있습니다. 예를 들어, 이 경우에는 ci_unit_test_failures가 잘라내어진 후에 프로세스가 중지됩니다:

sudo DRY_RUN=true UNTIL_TABLE=ci_unit_test_failures gitlab-rake gitlab:db:truncate_legacy_tables:main