CI 미러링 테이블

문제 설명

mainci 두 개의 데이터베이스로 GitLab이 사용하던 단일 데이터베이스를 분리하는 목표를 가진 분해 작업의 일환으로 mainci 테이블 간의 조인을 모두 제거하는 일이 있었습니다. 이는 PostgreSQL이 서로 다른 데이터베이스에 속하는 테이블 간의 조인을 지원하지 않기 때문입니다. 그러나 main 데이터베이스의 일부 핵심 애플리케이션 모델은 CI 쪽에서 매우 자주 쿼리됩니다. 예를 들면:

  • namespaces 테이블에 있는 Namespace
  • projects 테이블에 있는 Project

이러한 테이블에서 join을 할 수 없는 것은 큰 도전입니다. 팀은 main 데이터베이스의 해당 테이블을 ci 데이터베이스로 논리적으로 복제하기로 선택했으며, 새로운 테이블은 다음과 같습니다:

  • namespaces 테이블의 미러로 ci_namespace_mirrors
  • projects 테이블의 미러로 ci_project_mirrors

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

  1. main 데이터베이스의 테이블을 namespacesprojects 테이블에 조인하여 쿼리할 수 있습니다.
  2. ci 데이터베이스의 테이블을 ci_namespace_mirrorsci_project_mirrors 테이블과 조인할 수 있습니다.
graph LR subgraph "메인 데이터베이스 (테이블)" A[명칭 공간] -->|업데이트| B[명칭 공간 동기화 이벤트] A -->|삭제| C[외래 키 제약 조건이 삭제된 레코드] D[프로젝트] -->|삭제| C D -->|업데이트| E[프로젝트 동기화 이벤트] end B --> F C --> G E --> H subgraph "Sidekiq 워커 작업" F[Namespaces::ProcessSyncEventsWorker] G[LooseForeignKeys::CleanupWorker] H[Projects::ProcessSyncEventsWorker] end F -->|업데이트 실행| I G -->|레코드 삭제| I G -->|레코드 삭제| J H -->|업데이트 실행| J subgraph "CI 데이터베이스 (테이블)" I[ci_namespace_mirrors] J[ci_project_mirrors] end

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

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

소스 테이블과 대상 테이블 사이의 CI 미러링 테이블 동기화 유지

소스 테이블과 대상 테이블을 동기화하려면 두 가지 유형의 이벤트에 유의해야 합니다:

  1. 새로운 네임스페이스 또는 프로젝트 생성
  2. 네임스페이스 또는 프로젝트 업데이트
  3. 네임스페이스/프로젝트 삭제
graph LR subgraph CI["CI 테이블"] E[다른 CI 테이블] F{조인이 허용된 쿼리} G[ci_project_mirrors] H[ci_namespace_mirrors] E---F F---G F---H end Main["메인 테이블"]---L["⛔ ← 조인을 허용하지 않음 → ⛔"] L---CI subgraph Main["메인 테이블"] A[다른 메인 테이블] B{조인이 허용된 쿼리} C[프로젝트] D[네임스페이스] A---B B---C B---D end

생성 및 업데이트

새로 생성되거나 업데이트된 네임스페이스나 프로젝트의 데이터를 동기화하는 방법은 다음과 같습니다:

  1. main 데이터베이스에서: namespaces 또는 projects 테이블에 대한 어떠한 INSERT 또는 UPDATEnamespaces_sync_eventsprojects_sync_events 테이블에 항목을 추가합니다. 이러한 테이블은 main 데이터베이스에도 존재합니다. 이러한 항목들은 각각의 테이블에 트리거에 의해 추가됩니다.
  2. 모델 단계에서: Namespace 또는 Project 중 하나에서 커밋이 일어나면, 해당하는 Sidekiq 작업 Namespaces::ProcessSyncEventsWorker 또는 Projects::ProcessSyncEventsWorker가 예약됩니다.
  3. 이러한 워커는:
    1. main 데이터베이스의 (namespaces/project)_sync_events 테이블에서 항목을 읽어 어떤 네임스페이스나 프로젝트를 동기화할지 확인합니다.
    2. 변경된 레코드의 데이터를 대상 테이블인 ci_namespace_mirrors, ci_project_mirrors로 복사합니다.

삭제

namespaces 또는 projects 중 하나가 삭제되면, 미러링된 CI 테이블의 대상 레코드는 loose foreign keys (LFK) 메커니즘을 사용하여 삭제됩니다.

config/gitlab_loose_foreign_keys.yml에 이러한 항목들을 두어, LFK 메커니즘이 기대한대로 작동했습니다. 이는 main 데이터베이스에서 삭제된 namespaces 또는 projects에 매핑된 CI 미러링된 테이블의 모든 레코드를 삭제했습니다.

ci_namespace_mirrors:
  - 테이블: namespaces
    : namespace_id
    삭제 시: 비동기적으로 삭제
ci_project_mirrors:
  - 테이블: projects
    : project_id
    삭제 시: 비동기적으로 삭제

일관성 확인

동기화 메커니즘이 예상대로 작동하는지 확인하기 위해, 몇 분마다 크론 작업에 의해 트리거된 두 개의 추가적인 워커 작업을 배포합니다:

  1. Database::CiNamespaceMirrorsConsistencyCheckWorker
  2. Database::CiProjectMirrorsConsistencyCheckWorker

이러한 작업들은:

  1. main 데이터베이스의 소스 테이블들을 커서를 사용하여 스캔합니다.
  2. ci 데이터베이스의 대상 테이블과 namespacesprojects를 비교합니다.
  3. 동기화되지 않은 항목들을 Kibana와 Prometheus에 보고합니다.
  4. 어떠한 불일치도 정정합니다.