InfoGrab DocsInfoGrab Docs

CI 미러 테이블

요약

데이터베이스 분리 작업의 일환으로, GitLab이 사용하는 단일 데이터베이스를 main과 ci라는 두 개의 데이터베이스로 분리하는 것이 목표였으며, 이 과정에서 main 테이블과 ci 테이블 간의 조인을 모두 제거해야 하는 큰 과제가 생겼습니다.

문제 정의#

데이터베이스 분리 작업의 일환으로, GitLab이 사용하는 단일 데이터베이스를 mainci라는 두 개의 데이터베이스로 분리하는 것이 목표였으며, 이 과정에서 main 테이블과 ci 테이블 간의 조인을 모두 제거해야 하는 큰 과제가 생겼습니다. 이는 PostgreSQL이 서로 다른 데이터베이스에 속한 테이블 간의 조인을 지원하지 않기 때문입니다. 그러나 main 데이터베이스의 일부 핵심 애플리케이션 모델은 CI 측에서 매우 자주 조회됩니다. 예를 들어:

  • namespaces 테이블의 Namespace.

  • projects 테이블의 Project.

이러한 테이블에 대해 조인을 수행할 수 없다는 점은 큰 과제입니다. 팀은 이 테이블들을 main 데이터베이스에서 CI 데이터베이스로 논리적으로 복제하여 다음과 같은 새 테이블을 만들기로 결정했습니다:

  • namespaces 테이블의 미러인 ci_namespace_mirrors

  • projects 테이블의 미러인 ci_project_mirrors

이 논리적 복제는 두 가지를 의미합니다:

  • main 데이터베이스 테이블은 namespacesprojects 테이블과 조인하여 조회할 수 있습니다.

  • ci 데이터베이스 테이블은 ci_namespace_mirrorsci_project_mirrors 테이블과 조인할 수 있습니다.

graph LR

subgraph "Main database (tables)"
    A[namespaces] -->|updates| B[namespaces_sync_events]
    A -->|deletes| C[loose_foreign_keys_deleted_records]
    D[projects] -->|deletes| C
    D -->|updates| E[projects_sync_events]
end

B --> F
C --> G
E --> H

subgraph "Sidekiq worker jobs" F[Namespaces::ProcessSyncEventsWorker] G[LooseForeignKeys::CleanupWorker] H[Projects::ProcessSyncEventsWorker] end

F -->|do update| I
G -->|delete records| I
G -->|delete records| J
H -->|do update| J

subgraph "CI database (tables)"
    I[ci_namespace_mirrors]
    J[ci_project_mirrors]
end

이 복제는 각 모델에서 필요한 몇 가지 속성에만 제한됩니다:

  • Namespace에서는 traversal_ids를 복제합니다.

  • Project에서는 프로젝트가 속한 그룹을 나타내는 namespace_id만 복제합니다.

CI 미러 테이블을 소스 테이블과 동기화 유지#

소스 테이블과 타깃 테이블을 동기화 상태로 유지하려면 다음과 같은 세 가지 유형의 이벤트를 처리해야 합니다:

  • 새로운 네임스페이스 또는 프로젝트 생성.

  • 네임스페이스 또는 프로젝트 업데이트.

  • 네임스페이스/프로젝트 삭제.

graph LR subgraph CI["CI Tables"] E[other CI tables] F{queries with joins allowed} G[ci_project_mirrors] H[ci_namespace_mirrors]

    E---F
    F---G
    F---H
end

Main["Main Tables"]---L["⛔ ← Joins are not allowed → ⛔"]
L---CI

subgraph Main["Main Tables"]
    A[other main tables]
    B{queries with joins allowed}
    C[projects]
    D[namespaces]

    A---B
    B---C
    B---D
end

생성 및 업데이트#

새로 생성되거나 업데이트된 네임스페이스 또는 프로젝트의 데이터 동기화는 다음 순서로 이루어집니다:

  • main 데이터베이스에서: namespaces 또는 projects 테이블에 대한 INSERT 또는 UPDATE가 발생하면 namespaces_sync_eventsprojects_sync_events 테이블에 항목이 추가됩니다. 이 테이블들도 main 데이터베이스에 존재합니다. 이 항목들은 두 테이블 모두에 설정된 트리거에 의해 추가됩니다.

  • 모델 수준에서: 소스 모델 Namespace 또는 Project에서 커밋이 발생하면, 해당하는 Sidekiq job인 Namespaces::ProcessSyncEventsWorker 또는 Projects::ProcessSyncEventsWorker가 실행되도록 예약됩니다.

  • 이 워커들은 다음을 수행합니다:

main 데이터베이스에서 (namespaces/project)_sync_events 테이블의 항목을 읽어, 어떤 네임스페이스 또는 프로젝트를 동기화할지 확인합니다.

  • 업데이트된 레코드의 데이터를 타깃 테이블인 ci_namespace_mirrors, ci_project_mirrors로 복사합니다.

삭제#

namespaces 또는 projects가 삭제될 때, CI 미러 테이블의 타깃 레코드는 느슨한 외래 키(LFK) 메커니즘을 사용하여 삭제됩니다.

config/gitlab_loose_foreign_keys.yml에 이러한 항목들을 정의해 두면, LFK 메커니즘이 예상대로 작동합니다. 즉, main 데이터베이스에서 삭제된 namespaces 또는 projects에 매핑된 CI 미러 테이블의 레코드가 삭제됩니다.

ci_namespace_mirrors:
  - table: namespaces
    column: namespace_id
    on_delete: async_delete
ci_project_mirrors:
  - table: projects
    column: project_id
    on_delete: async_delete

일관성 검사#

두 동기화 메커니즘이 예상대로 작동하는지 확인하기 위해, cron job으로 몇 분마다 트리거되는 두 개의 추가 워커 job을 배포합니다:

  • Database::CiNamespaceMirrorsConsistencyCheckWorker

  • Database::CiProjectMirrorsConsistencyCheckWorker

이 job들은 다음을 수행합니다:

  • 커서를 사용하여 main 데이터베이스의 소스 테이블 두 개를 스캔합니다.

  • ci 데이터베이스의 타깃 테이블과 namespacesprojects 항목을 비교합니다.

  • 동기화되지 않은 항목을 Kibana 및 Prometheus에 보고합니다.

  • 불일치를 수정합니다.

CI 미러 테이블

GitLab v19.1
원문 보기
요약

데이터베이스 분리 작업의 일환으로, GitLab이 사용하는 단일 데이터베이스를 main과 ci라는 두 개의 데이터베이스로 분리하는 것이 목표였으며, 이 과정에서 main 테이블과 ci 테이블 간의 조인을 모두 제거해야 하는 큰 과제가 생겼습니다.

문제 정의#

데이터베이스 분리 작업의 일환으로, GitLab이 사용하는 단일 데이터베이스를 mainci라는 두 개의 데이터베이스로 분리하는 것이 목표였으며, 이 과정에서 main 테이블과 ci 테이블 간의 조인을 모두 제거해야 하는 큰 과제가 생겼습니다. 이는 PostgreSQL이 서로 다른 데이터베이스에 속한 테이블 간의 조인을 지원하지 않기 때문입니다. 그러나 main 데이터베이스의 일부 핵심 애플리케이션 모델은 CI 측에서 매우 자주 조회됩니다. 예를 들어:

  • namespaces 테이블의 Namespace.

  • projects 테이블의 Project.

이러한 테이블에 대해 조인을 수행할 수 없다는 점은 큰 과제입니다. 팀은 이 테이블들을 main 데이터베이스에서 CI 데이터베이스로 논리적으로 복제하여 다음과 같은 새 테이블을 만들기로 결정했습니다:

  • namespaces 테이블의 미러인 ci_namespace_mirrors

  • projects 테이블의 미러인 ci_project_mirrors

이 논리적 복제는 두 가지를 의미합니다:

  • main 데이터베이스 테이블은 namespacesprojects 테이블과 조인하여 조회할 수 있습니다.

  • ci 데이터베이스 테이블은 ci_namespace_mirrorsci_project_mirrors 테이블과 조인할 수 있습니다.

graph LR

subgraph "Main database (tables)"
    A[namespaces] -->|updates| B[namespaces_sync_events]
    A -->|deletes| C[loose_foreign_keys_deleted_records]
    D[projects] -->|deletes| C
    D -->|updates| E[projects_sync_events]
end

B --> F
C --> G
E --> H

subgraph "Sidekiq worker jobs" F[Namespaces::ProcessSyncEventsWorker] G[LooseForeignKeys::CleanupWorker] H[Projects::ProcessSyncEventsWorker] end

F -->|do update| I
G -->|delete records| I
G -->|delete records| J
H -->|do update| J

subgraph "CI database (tables)"
    I[ci_namespace_mirrors]
    J[ci_project_mirrors]
end

이 복제는 각 모델에서 필요한 몇 가지 속성에만 제한됩니다:

  • Namespace에서는 traversal_ids를 복제합니다.

  • Project에서는 프로젝트가 속한 그룹을 나타내는 namespace_id만 복제합니다.

CI 미러 테이블을 소스 테이블과 동기화 유지#

소스 테이블과 타깃 테이블을 동기화 상태로 유지하려면 다음과 같은 세 가지 유형의 이벤트를 처리해야 합니다:

  • 새로운 네임스페이스 또는 프로젝트 생성.

  • 네임스페이스 또는 프로젝트 업데이트.

  • 네임스페이스/프로젝트 삭제.

graph LR subgraph CI["CI Tables"] E[other CI tables] F{queries with joins allowed} G[ci_project_mirrors] H[ci_namespace_mirrors]

    E---F
    F---G
    F---H
end

Main["Main Tables"]---L["⛔ ← Joins are not allowed → ⛔"]
L---CI

subgraph Main["Main Tables"]
    A[other main tables]
    B{queries with joins allowed}
    C[projects]
    D[namespaces]

    A---B
    B---C
    B---D
end

생성 및 업데이트#

새로 생성되거나 업데이트된 네임스페이스 또는 프로젝트의 데이터 동기화는 다음 순서로 이루어집니다:

  • main 데이터베이스에서: namespaces 또는 projects 테이블에 대한 INSERT 또는 UPDATE가 발생하면 namespaces_sync_eventsprojects_sync_events 테이블에 항목이 추가됩니다. 이 테이블들도 main 데이터베이스에 존재합니다. 이 항목들은 두 테이블 모두에 설정된 트리거에 의해 추가됩니다.

  • 모델 수준에서: 소스 모델 Namespace 또는 Project에서 커밋이 발생하면, 해당하는 Sidekiq job인 Namespaces::ProcessSyncEventsWorker 또는 Projects::ProcessSyncEventsWorker가 실행되도록 예약됩니다.

  • 이 워커들은 다음을 수행합니다:

main 데이터베이스에서 (namespaces/project)_sync_events 테이블의 항목을 읽어, 어떤 네임스페이스 또는 프로젝트를 동기화할지 확인합니다.

  • 업데이트된 레코드의 데이터를 타깃 테이블인 ci_namespace_mirrors, ci_project_mirrors로 복사합니다.

삭제#

namespaces 또는 projects가 삭제될 때, CI 미러 테이블의 타깃 레코드는 느슨한 외래 키(LFK) 메커니즘을 사용하여 삭제됩니다.

config/gitlab_loose_foreign_keys.yml에 이러한 항목들을 정의해 두면, LFK 메커니즘이 예상대로 작동합니다. 즉, main 데이터베이스에서 삭제된 namespaces 또는 projects에 매핑된 CI 미러 테이블의 레코드가 삭제됩니다.

ci_namespace_mirrors:
  - table: namespaces
    column: namespace_id
    on_delete: async_delete
ci_project_mirrors:
  - table: projects
    column: project_id
    on_delete: async_delete

일관성 검사#

두 동기화 메커니즘이 예상대로 작동하는지 확인하기 위해, cron job으로 몇 분마다 트리거되는 두 개의 추가 워커 job을 배포합니다:

  • Database::CiNamespaceMirrorsConsistencyCheckWorker

  • Database::CiProjectMirrorsConsistencyCheckWorker

이 job들은 다음을 수행합니다:

  • 커서를 사용하여 main 데이터베이스의 소스 테이블 두 개를 스캔합니다.

  • ci 데이터베이스의 타깃 테이블과 namespacesprojects 항목을 비교합니다.

  • 동기화되지 않은 항목을 Kibana 및 Prometheus에 보고합니다.

  • 불일치를 수정합니다.