CI 거울 테이블

문제 설명

GitLab이 사용하는 단일 데이터베이스를 두 개의 데이터베이스(main 및 ci)로 분할하는 작업 분해 작업의 일부로, 다른 데이터베이스에 속하는 테이블 간 조인을 모두 제거하는 일이 있었습니다. PostgreSQL은 서로 다른 데이터베이스에 속하는 테이블 간의 조인을 지원하지 않기 때문입니다. 그러나 main 데이터베이스의 일부 중요한 응용 프로그램 모델은 CI 쪽에서 매우 자주 쿼리됩니다. 예를 들면:

  • namespaces 테이블의 Namespace
  • projects 테이블의 Project

이러한 테이블에서 조인을 할 수 없는 것은 큰 도전입니다. 팀은 이러한 테이블을 main 데이터베이스에서 CI 데이터베이스로 논리적으로 복제하기로 결정했고, 이를 새로운 테이블에 반영했습니다.

  • namespaces 테이블의 거울인 ci_namespace_mirrors
  • projects 테이블의 거울인 ci_project_mirrors

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

  1. main 데이터베이스 테이블은 namespacesprojects 테이블에 조인하여 쿼리 가능합니다.
  2. ci 데이터베이스 테이블은 ci_namespace_mirrorsci_project_mirrors 테이블과 조인할 수 있습니다.
graph LR subgraph "Main database (tables)" A[namespaces] -->|업데이트| B[namespaces_sync_events] A -->|삭제| C[loose_foreign_keys_deleted_records] D[projects] -->|삭제| C D -->|업데이트| 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 -->|업데이트 수행| I G -->|레코드 삭제| I G -->|레코드 삭제| J H -->|업데이트 수행| J subgraph "CI database (tables)" I[ci_namespace_mirrors] J[ci_project_mirrors] end

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

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

소스 테이블과 대상 테이블의 동기화 유지

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

  1. 새로운 namespaces 또는 projects를 만드는 경우.
  2. namespaces 또는 projects를 업데이트하는 경우.
  3. namespaces/projects를 삭제하는 경우.
graph LR subgraph CI["CI 테이블"] E[다른 CI 테이블] F{조인이 허용된 쿼리} G[ci_project_mirrors] H[ci_namespace_mirrors] E---F F---G F---H end Main["Main 테이블"]---L["⛔ ← 조인 불가능 → ⛔"] L---CI subgraph Main["Main 테이블"] A[다른 main 테이블] B{조인이 허용된 쿼리} C[projects] D[namespaces] A---B B---C B---D end

만들고 업데이트하기

새롭게 생성되거나 업데이트된 namespaces 또는 projects의 데이터를 동기화하는 방법은 다음과 같습니다:

  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 테이블에서 항목을 읽어 동기화할 namespaces 또는 projects를 확인합니다.
    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_namespace_mirrors:
  - table: namespaces
    column: namespace_id
    on_delete: async_delete
ci_project_mirrors:
  - table: projects
    column: project_id
    on_delete: async_delete

일관성 확인

동기화 메커니즘이 의도한 대로 작동하는지 확인하려면 몇 분마다 크론 작업에 의해 트리거되는 추가적인 워커 작업 두 개를 배포합니다:

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

이러한 작업은 다음과 같습니다:

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