InfoGrab DocsInfoGrab Docs

다중 데이터베이스 마이그레이션

요약

이 문서는 다중 데이터베이스를 사용하는 분해된 GitLab 애플리케이션을 위해 데이터베이스 마이그레이션을 올바르게 작성하는 방법을 설명합니다. 다중 데이터베이스 설계(Geo 데이터베이스 제외)는 분해된 모든 데이터베이스가 동일한 구조(예: 스키마)를 가지지만, 데이터는 각 데이터베이스마다 다르다고 가정합니다.

이 문서는 다중 데이터베이스를 사용하는 분해된 GitLab 애플리케이션을 위해 데이터베이스 마이그레이션을 올바르게 작성하는 방법을 설명합니다. 자세한 내용은 다중 데이터베이스를 참조하세요.

다중 데이터베이스 설계(Geo 데이터베이스 제외)는 분해된 모든 데이터베이스가 동일한 구조(예: 스키마)를 가지지만, 데이터는 각 데이터베이스마다 다르다고 가정합니다. 즉, 일부 테이블은 각 데이터베이스에 데이터를 포함하지 않을 수 있습니다.

작업 유형#

사용하는 구문에 따라 마이그레이션을 다음과 같이 분류할 수 있습니다:

Gitlab::Database::Migration[2.0] 사용 시 마이그레이션은 항상 단일 목적이어야 합니다. 애플리케이션은 db/structure.sql에 기술된 구조가 모든 분해된 데이터베이스에서 완전히 동일하도록 요구하기 때문에, 마이그레이션에서 DDLDML 변경을 혼용할 수 없습니다.

Data Definition Language (DDL)#

DDL 마이그레이션은 다음을 수행하는 모든 마이그레이션입니다:

  • 테이블 생성 또는 삭제 (예: create_table)

  • 인덱스 추가 또는 삭제 (예: add_index, add_concurrent_index)

  • 외래 키 추가 또는 삭제 (예: add_foreign_key, add_concurrent_foreign_key)

  • 기본값 유무와 관계없이 칼럼 추가 또는 삭제 (예: add_column)

  • 트리거 함수 생성 또는 삭제 (예: create_trigger_function)

  • 테이블에 트리거 연결 또는 분리 (예: track_record_deletions, untrack_record_deletions)

  • 비동기 인덱스 준비 또는 취소 (예: prepare_async_index, unprepare_async_index_by_name)

  • 테이블 비우기 (예: truncate_tables! 헬퍼 메서드 사용)

따라서 DDL 마이그레이션은 다음을 할 수 없습니다:

  • SQL 구문이나 ActiveRecord 모델을 통해 어떤 형태로든 데이터를 읽거나 수정

  • 칼럼 값 업데이트 (예: update_column_in_batches)

  • 백그라운드 마이그레이션 예약 (예: queue_batched_background_migration)

  • 피처 플래그 상태 읽기 - 피처 플래그는 main: (featuresfeature_gates 테이블)에 저장됨

  • 애플리케이션 설정 읽기 (설정은 main:에 저장됨)

GitLab 코드베이스의 대부분의 마이그레이션은 DDL 유형이므로, 이것이 기본 작동 모드이며 마이그레이션 파일에 추가 변경이 필요하지 않습니다.

예시: 모든 데이터베이스에서 DDL 수행#

구조 변경(DDL)으로 취급되어 구성된 모든 데이터베이스에서 실행되는 동시 인덱스 추가 마이그레이션 예시입니다.

class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  INDEX_NAME = 'index_on_merge_request_reviewers_user_id_and_state'

  def up
    add_concurrent_index :merge_request_reviewers, [:user_id, :state], where: 'state = 2', name: INDEX_NAME
  end

  def down
    remove_concurrent_index_by_name :merge_request_reviewers, INDEX_NAME
  end
end

예시: 단일 데이터베이스에 저장할 새 테이블 추가#

db/docs/데이터베이스 사전에 테이블을 추가합니다:

table_name: ssh_signatures
description: Description example
introduced_by_url: Merge request link
milestone: Milestone example
feature_categories:
- Feature category example
classes:
- Class example
gitlab_schema: gitlab_main_org

스키마 마이그레이션으로 테이블을 생성합니다:

class CreateSshSignatures < Gitlab::Database::Migration[2.1]
  def change
    create_table :ssh_signatures do |t|
      t.timestamps_with_timezone null: false
      t.bigint :project_id, null: false, index: true
      t.bigint :key_id, null: false, index: true
      t.integer :verification_status, default: 0, null: false, limit: 2
      t.binary :commit_sha, null: false, index: { unique: true }
    end
  end
end

Data Manipulation Language (DML)#

DML 마이그레이션은 다음을 수행하는 모든 마이그레이션입니다:

  • SQL 구문으로 데이터 읽기 (예: SELECT * FROM projects WHERE id=1)

  • ActiveRecord 모델로 데이터 읽기 (예: User < MigrationRecord)

  • ActiveRecord 모델로 데이터 생성, 수정 또는 삭제 (예: User.create!(...))

  • SQL 구문으로 데이터 생성, 수정 또는 삭제 (예: DELETE FROM projects WHERE id=1)

  • 배치로 칼럼 업데이트 (예: update_column_in_batches(:projects, :archived, true))

  • 백그라운드 마이그레이션 예약 (예: queue_batched_background_migration)

  • 애플리케이션 설정 접근 (예: main: 데이터베이스에서 실행 시 ApplicationSetting.last)

  • main: 데이터베이스에서 실행 시 피처 플래그 읽기 및 수정

DML 마이그레이션은 다음을 할 수 없습니다:

  • 모든 분해된 데이터베이스에서 structure.sql의 일관성을 유지해야 하는 규칙을 위반하는 DDL 변경

  • 다른 데이터베이스의 데이터 읽기

DML 마이그레이션 유형을 지정하려면, 마이그레이션 클래스에서 restrict_gitlab_migration gitlab_schema: 구문을 사용해야 합니다. 이는 해당 마이그레이션을 DML로 표시하고 접근을 제한합니다.

예시: 지정된 gitlab_schema를 포함하는 데이터베이스 컨텍스트에서만 DML 수행#

gitlab_main 스키마를 포함하는 데이터베이스에서만 실행되는 projectsarchived 칼럼 업데이트 마이그레이션 예시입니다.

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  def up
    update_column_in_batches(:projects, :archived, true) do |table, query|
      query.where(table[:archived].eq(false)) # rubocop:disable CodeReuse/ActiveRecord
    end
  end

  def down
    # no-op
  end
end

예시: ActiveRecord 클래스 사용#

데이터 조작을 수행하기 위해 ActiveRecord 클래스를 사용하는 마이그레이션은 MigrationRecord 클래스를 사용해야 합니다. 이 클래스는 주어진 마이그레이션의 컨텍스트에서 올바른 연결을 제공하도록 보장됩니다.

내부적으로 MigrationRecord == ActiveRecord::Base이며, db:migrate가 실행되면 ActiveRecord::Base.establish_connection :ci의 활성 연결이 전환됩니다. ActiveRecord::Base 사용으로 인한 혼란을 방지하기 위해 MigrationRecord가 요구됩니다.

이는 DML 마이그레이션이 다른 데이터베이스의 데이터를 읽는 것을 금지함을 의미합니다. 예를 들어, ci: 컨텍스트에서 마이그레이션을 실행하면서 main:에서 피처 플래그를 읽는 것은 불가능합니다. 다른 데이터베이스로의 연결이 설정되지 않기 때문입니다.

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  class Project < MigrationRecord
  end

  def up
    Project.where(archived: false).each_batch of |batch|
      batch.update_all(archived: true)
    end
  end

  def down
  end
end

gitlab_shared의 특수 목적#

gitlab_schema에 설명된 바와 같이, gitlab_shared 테이블은 모든 데이터베이스에 걸쳐 데이터를 포함할 수 있습니다. 이는 이러한 마이그레이션이 구조를 변경(DDL)하거나 데이터를 수정(DML)하기 위해 모든 데이터베이스에서 실행되어야 함을 의미합니다.

gitlab_shared에 접근하는 마이그레이션은 restrict_gitlab_migration gitlab_schema:를 사용할 필요가 없습니다. 제한이 없는 마이그레이션은 모든 데이터베이스에서 실행되며 각 데이터베이스의 데이터를 수정할 수 있습니다. restrict_gitlab_migration gitlab_schema:가 지정된 경우, DML 마이그레이션은 지정된 gitlab_schema를 포함하는 데이터베이스 컨텍스트에서만 실행됩니다.

예시: 모든 데이터베이스에서 DML gitlab_shared 마이그레이션 실행#

lib/gitlab/database/gitlab_schemas.yml에서 gitlab_shared로 표시된 loose_foreign_keys_deleted_records 테이블을 업데이트하는 마이그레이션 예시입니다.

이 마이그레이션은 구성된 모든 데이터베이스에서 실행됩니다.

class DeleteAllLooseForeignKeyRecords < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    execute("DELETE FROM loose_foreign_keys_deleted_records")
  end

  def down
    # no-op
  end
end

예시: 지정된 gitlab_schema를 포함하는 데이터베이스에서만 DML gitlab_shared 실행#

db/docs/loose_foreign_keys_deleted_records.yml에서 gitlab_shared로 표시된 loose_foreign_keys_deleted_records 테이블을 업데이트하는 마이그레이션 예시입니다.

이 마이그레이션은 gitlab_ci 제한이 구성되어 있으므로 gitlab_ci 스키마를 포함하는 데이터베이스 컨텍스트에서만 실행됩니다.

class DeleteCiBuildsLooseForeignKeyRecords < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  restrict_gitlab_migration gitlab_schema: :gitlab_ci

  def up
    execute("DELETE FROM loose_foreign_keys_deleted_records WHERE fully_qualified_table_name='ci_builds'")
  end

  def down
    # no-op
  end
end

마이그레이션 건너뛰기 동작#

건너뛰는 마이그레이션은 DML 변경을 수행하는 마이그레이션뿐입니다. DDL 마이그레이션은 항상 조건 없이 실행됩니다.

구현된 솔루션database_tasks:를 사용하여 config/database.yml의 추가 데이터베이스 구성 중 동일한 기본 데이터베이스를 공유하는 항목을 표시합니다. database_tasks: false로 표시된 데이터베이스 구성은 해당 데이터베이스 구성에 대해 db:migrate를 실행하는 것이 면제됩니다.

데이터베이스 구성이 데이터베이스를 공유하지 않는 경우(모두 database_tasks: true인 경우), 각 마이그레이션은 모든 데이터베이스 구성에 대해 실행됩니다:

  • DDL 마이그레이션은 모든 데이터베이스에 모든 구조 변경을 적용합니다.

  • DML 마이그레이션은 지정된 gitlab_schema:를 포함하는 데이터베이스 컨텍스트에서만 실행됩니다.

  • DML 마이그레이션이 실행 자격이 없는 경우 건너뜁니다. 해당 마이그레이션은 여전히 schema_migrations에 실행된 것으로 표시됩니다. db:migrate를 실행하는 동안 건너뛴 마이그레이션은 Current migration is skipped since it modifies 'gitlab_ci' which is outside of 'gitlab_main, gitlab_shared를 출력합니다.

database_tasks: false가 구성된 경우 마이그레이션 손실을 방지하기 위해, 전용 Rake 태스크 gitlab:db:validate_config가 사용됩니다. gitlab:db:validate_config는 각 기본 데이터베이스 구성의 데이터베이스 식별자를 확인하여 database_tasks:의 정확성을 검증합니다. 동일한 데이터베이스를 공유하는 구성은 database_tasks: false가 설정되어야 합니다. gitlab:db:validate_config는 항상 db:migrate 전에 실행됩니다.

유효성 검사#

유효성 검사는 간단히 말해 pg_query를 사용하여 각 쿼리를 분석하고 db/docs/의 정보로 테이블을 분류합니다. 지정된 gitlab_schema가 특정 데이터베이스 연결에서 관리되는 스키마 목록(Gitlab::Database::gitlab_schemas_for_connection) 외부에 있으면 마이그레이션을 건너뜁니다.

Gitlab::Database::Migration[2.0]에는 #migrate 메서드를 확장하는 Gitlab::Database::MigrationHelpers::RestrictGitlabSchema가 포함됩니다. 마이그레이션 실행 중에 restrict_gitlab_migration:으로 정의된 허용된 스키마 목록을 받는 전용 쿼리 분석기 Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas가 설치됩니다. 실행된 쿼리가 허용된 스키마 외부에 있으면 예외가 발생합니다.

예외#

restrict_gitlab_migration의 오용 또는 누락에 따라 마이그레이션 실행 중 다양한 예외가 발생할 수 있으며 마이그레이션 완료를 막을 수 있습니다.

예외 1: DDL 모드에서 실행되는 마이그레이션이 DML select를 수행하는 경우#

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  # Missing:
  # restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  def up
    update_column_in_batches(:projects, :archived, true) do |table, query|
      query.where(table[:archived].eq(false)) # rubocop:disable CodeReuse/ActiveRecord
    end
  end

  def down
    # no-op
  end
end
Select/DML queries (SELECT/UPDATE/DELETE) are disallowed in the DDL (structure) mode
Modifying of 'projects' (gitlab_main) with 'SELECT * FROM projects...

현재 마이그레이션은 restrict_gitlab_migration을 사용하지 않습니다. 이 누락은 DDL 모드로 실행 중인 마이그레이션을 나타내지만, 실행된 페이로드는 projects에서 데이터를 읽으려 합니다.

해결 방법restrict_gitlab_migration gitlab_schema: :gitlab_main_org를 추가하는 것입니다.

예외 2: DML 모드에서 실행되는 마이그레이션이 구조를 변경하는 경우#

class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  # restrict_gitlab_migration if defined indicates DML, it should be removed
  restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  INDEX_NAME = 'index_on_merge_request_reviewers_user_id_and_state'

  def up
    add_concurrent_index :merge_request_reviewers, [:user_id, :state], where: 'state = 2', name: INDEX_NAME
  end

  def down
    remove_concurrent_index_by_name :merge_request_reviewers, INDEX_NAME
  end
end
DDL queries (structure) are disallowed in the Select/DML (SELECT/UPDATE/DELETE) mode.
Modifying of 'merge_request_reviewers' with 'CREATE INDEX...

현재 마이그레이션은 restrict_gitlab_migration을 사용합니다. 이 존재는 DML 모드를 나타내지만, 실행된 페이로드는 구조 변경(DDL)을 수행하려 합니다.

해결 방법restrict_gitlab_migration gitlab_schema: :gitlab_main_org를 제거하는 것입니다.

예외 3: DML 모드에서 실행되는 마이그레이션이 다른 스키마의 테이블 데이터에 접근하는 경우#

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  # Since it modifies `projects` it should use `gitlab_main`
  restrict_gitlab_migration gitlab_schema: :gitlab_ci

  def up
    update_column_in_batches(:projects, :archived, true) do |table, query|
      query.where(table[:archived].eq(false)) # rubocop:disable CodeReuse/ActiveRecord
    end
  end

  def down
    # no-op
  end
end
Select/DML queries (SELECT/UPDATE/DELETE) do access 'projects' (gitlab_main) " \
which is outside of list of allowed schemas: 'gitlab_ci'

현재 마이그레이션은 gitlab_ci로 마이그레이션을 제한하지만, gitlab_main의 데이터를 수정하려 합니다.

해결 방법restrict_gitlab_migration gitlab_schema: :gitlab_ci를 변경하는 것입니다.

예외 4: DDL과 DML 모드 혼용#

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  # This migration is invalid regardless of specification
  # as it cannot modify structure and data at the same time
  restrict_gitlab_migration gitlab_schema: :gitlab_ci

  def up
    add_concurrent_index :merge_request_reviewers, [:user_id, :state], where: 'state = 2', name: 'index_on_merge_request_reviewers'
    update_column_in_batches(:projects, :archived, true) do |table, query|
      query.where(table[:archived].eq(false)) # rubocop:disable CodeReuse/ActiveRecord
    end
  end

  def down
    # no-op
  end
end

DDLDML을 혼용하는 마이그레이션은 작업 순서에 따라 앞서 설명한 예외 중 하나가 발생합니다.

다중 데이터베이스 마이그레이션의 향후 변경 사항#

gitlab_schema:를 사용하는 restrict_gitlab_migration은 컨텍스트에 따라 선택적으로 마이그레이션을 실행하는 이 기능의 첫 번째 반복으로 간주됩니다. 추후 변경이 없을 때까지 구조 일관성이 유지될 가능성이 높으므로, DML 전용 마이그레이션에 추가 제한을 둬 실행 시기를 제한할 수 있습니다.

잠재적인 확장으로는 DML 마이그레이션 실행을 특정 환경으로만 제한하는 것이 있습니다:

restrict_gitlab_migration gitlab_schema: :gitlab_main_org, gitlab_env: :gitlab_com

백그라운드 마이그레이션#

다음을 사용하는 경우:

  • track_jobstrue로 설정된 백그라운드 마이그레이션, 또는

  • 배치 백그라운드 마이그레이션

마이그레이션은 jobs 테이블에 써야 합니다. 백그라운드 마이그레이션에서 사용하는 모든 jobs 테이블은 gitlab_shared로 표시됩니다. 어떤 데이터베이스의 테이블을 마이그레이션할 때도 이 마이그레이션을 사용할 수 있습니다.

그러나 배치를 큐에 넣을 때는 반복하는 테이블을 기반으로 restrict_gitlab_migration을 설정해야 합니다. 예를 들어, 모든 projects를 업데이트하는 경우 restrict_gitlab_migration gitlab_schema: :gitlab_main_org로 설정합니다. 반면 모든 ci_pipelines를 업데이트하는 경우에는 restrict_gitlab_migration gitlab_schema: :gitlab_ci로 설정합니다.

모든 DML 마이그레이션과 마찬가지로, restrict_gitlab_migration 또는 gitlab_shared 외부의 다른 데이터베이스를 쿼리할 수 없습니다. 다른 데이터베이스를 쿼리해야 하는 경우 마이그레이션을 분리하세요.

백그라운드 마이그레이션의 실제 마이그레이션 로직(큐잉 단계 제외)은 Sidekiq 워커에서 실행되므로, 일반적인 Sidekiq 워커처럼 모든 데이터베이스의 테이블에 대해 DML 쿼리를 수행할 수 있습니다.

주어진 테이블에 대한 gitlab_schema 결정 방법#

데이터베이스 사전을 참조하세요.

다중 데이터베이스 마이그레이션

GitLab v19.1
원문 보기
요약

이 문서는 다중 데이터베이스를 사용하는 분해된 GitLab 애플리케이션을 위해 데이터베이스 마이그레이션을 올바르게 작성하는 방법을 설명합니다. 다중 데이터베이스 설계(Geo 데이터베이스 제외)는 분해된 모든 데이터베이스가 동일한 구조(예: 스키마)를 가지지만, 데이터는 각 데이터베이스마다 다르다고 가정합니다.

이 문서는 다중 데이터베이스를 사용하는 분해된 GitLab 애플리케이션을 위해 데이터베이스 마이그레이션을 올바르게 작성하는 방법을 설명합니다. 자세한 내용은 다중 데이터베이스를 참조하세요.

다중 데이터베이스 설계(Geo 데이터베이스 제외)는 분해된 모든 데이터베이스가 동일한 구조(예: 스키마)를 가지지만, 데이터는 각 데이터베이스마다 다르다고 가정합니다. 즉, 일부 테이블은 각 데이터베이스에 데이터를 포함하지 않을 수 있습니다.

작업 유형#

사용하는 구문에 따라 마이그레이션을 다음과 같이 분류할 수 있습니다:

Gitlab::Database::Migration[2.0] 사용 시 마이그레이션은 항상 단일 목적이어야 합니다. 애플리케이션은 db/structure.sql에 기술된 구조가 모든 분해된 데이터베이스에서 완전히 동일하도록 요구하기 때문에, 마이그레이션에서 DDLDML 변경을 혼용할 수 없습니다.

Data Definition Language (DDL)#

DDL 마이그레이션은 다음을 수행하는 모든 마이그레이션입니다:

  • 테이블 생성 또는 삭제 (예: create_table)

  • 인덱스 추가 또는 삭제 (예: add_index, add_concurrent_index)

  • 외래 키 추가 또는 삭제 (예: add_foreign_key, add_concurrent_foreign_key)

  • 기본값 유무와 관계없이 칼럼 추가 또는 삭제 (예: add_column)

  • 트리거 함수 생성 또는 삭제 (예: create_trigger_function)

  • 테이블에 트리거 연결 또는 분리 (예: track_record_deletions, untrack_record_deletions)

  • 비동기 인덱스 준비 또는 취소 (예: prepare_async_index, unprepare_async_index_by_name)

  • 테이블 비우기 (예: truncate_tables! 헬퍼 메서드 사용)

따라서 DDL 마이그레이션은 다음을 할 수 없습니다:

  • SQL 구문이나 ActiveRecord 모델을 통해 어떤 형태로든 데이터를 읽거나 수정

  • 칼럼 값 업데이트 (예: update_column_in_batches)

  • 백그라운드 마이그레이션 예약 (예: queue_batched_background_migration)

  • 피처 플래그 상태 읽기 - 피처 플래그는 main: (featuresfeature_gates 테이블)에 저장됨

  • 애플리케이션 설정 읽기 (설정은 main:에 저장됨)

GitLab 코드베이스의 대부분의 마이그레이션은 DDL 유형이므로, 이것이 기본 작동 모드이며 마이그레이션 파일에 추가 변경이 필요하지 않습니다.

예시: 모든 데이터베이스에서 DDL 수행#

구조 변경(DDL)으로 취급되어 구성된 모든 데이터베이스에서 실행되는 동시 인덱스 추가 마이그레이션 예시입니다.

class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  INDEX_NAME = 'index_on_merge_request_reviewers_user_id_and_state'

  def up
    add_concurrent_index :merge_request_reviewers, [:user_id, :state], where: 'state = 2', name: INDEX_NAME
  end

  def down
    remove_concurrent_index_by_name :merge_request_reviewers, INDEX_NAME
  end
end

예시: 단일 데이터베이스에 저장할 새 테이블 추가#

db/docs/데이터베이스 사전에 테이블을 추가합니다:

table_name: ssh_signatures
description: Description example
introduced_by_url: Merge request link
milestone: Milestone example
feature_categories:
- Feature category example
classes:
- Class example
gitlab_schema: gitlab_main_org

스키마 마이그레이션으로 테이블을 생성합니다:

class CreateSshSignatures < Gitlab::Database::Migration[2.1]
  def change
    create_table :ssh_signatures do |t|
      t.timestamps_with_timezone null: false
      t.bigint :project_id, null: false, index: true
      t.bigint :key_id, null: false, index: true
      t.integer :verification_status, default: 0, null: false, limit: 2
      t.binary :commit_sha, null: false, index: { unique: true }
    end
  end
end

Data Manipulation Language (DML)#

DML 마이그레이션은 다음을 수행하는 모든 마이그레이션입니다:

  • SQL 구문으로 데이터 읽기 (예: SELECT * FROM projects WHERE id=1)

  • ActiveRecord 모델로 데이터 읽기 (예: User < MigrationRecord)

  • ActiveRecord 모델로 데이터 생성, 수정 또는 삭제 (예: User.create!(...))

  • SQL 구문으로 데이터 생성, 수정 또는 삭제 (예: DELETE FROM projects WHERE id=1)

  • 배치로 칼럼 업데이트 (예: update_column_in_batches(:projects, :archived, true))

  • 백그라운드 마이그레이션 예약 (예: queue_batched_background_migration)

  • 애플리케이션 설정 접근 (예: main: 데이터베이스에서 실행 시 ApplicationSetting.last)

  • main: 데이터베이스에서 실행 시 피처 플래그 읽기 및 수정

DML 마이그레이션은 다음을 할 수 없습니다:

  • 모든 분해된 데이터베이스에서 structure.sql의 일관성을 유지해야 하는 규칙을 위반하는 DDL 변경

  • 다른 데이터베이스의 데이터 읽기

DML 마이그레이션 유형을 지정하려면, 마이그레이션 클래스에서 restrict_gitlab_migration gitlab_schema: 구문을 사용해야 합니다. 이는 해당 마이그레이션을 DML로 표시하고 접근을 제한합니다.

예시: 지정된 gitlab_schema를 포함하는 데이터베이스 컨텍스트에서만 DML 수행#

gitlab_main 스키마를 포함하는 데이터베이스에서만 실행되는 projectsarchived 칼럼 업데이트 마이그레이션 예시입니다.

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  def up
    update_column_in_batches(:projects, :archived, true) do |table, query|
      query.where(table[:archived].eq(false)) # rubocop:disable CodeReuse/ActiveRecord
    end
  end

  def down
    # no-op
  end
end

예시: ActiveRecord 클래스 사용#

데이터 조작을 수행하기 위해 ActiveRecord 클래스를 사용하는 마이그레이션은 MigrationRecord 클래스를 사용해야 합니다. 이 클래스는 주어진 마이그레이션의 컨텍스트에서 올바른 연결을 제공하도록 보장됩니다.

내부적으로 MigrationRecord == ActiveRecord::Base이며, db:migrate가 실행되면 ActiveRecord::Base.establish_connection :ci의 활성 연결이 전환됩니다. ActiveRecord::Base 사용으로 인한 혼란을 방지하기 위해 MigrationRecord가 요구됩니다.

이는 DML 마이그레이션이 다른 데이터베이스의 데이터를 읽는 것을 금지함을 의미합니다. 예를 들어, ci: 컨텍스트에서 마이그레이션을 실행하면서 main:에서 피처 플래그를 읽는 것은 불가능합니다. 다른 데이터베이스로의 연결이 설정되지 않기 때문입니다.

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  class Project < MigrationRecord
  end

  def up
    Project.where(archived: false).each_batch of |batch|
      batch.update_all(archived: true)
    end
  end

  def down
  end
end

gitlab_shared의 특수 목적#

gitlab_schema에 설명된 바와 같이, gitlab_shared 테이블은 모든 데이터베이스에 걸쳐 데이터를 포함할 수 있습니다. 이는 이러한 마이그레이션이 구조를 변경(DDL)하거나 데이터를 수정(DML)하기 위해 모든 데이터베이스에서 실행되어야 함을 의미합니다.

gitlab_shared에 접근하는 마이그레이션은 restrict_gitlab_migration gitlab_schema:를 사용할 필요가 없습니다. 제한이 없는 마이그레이션은 모든 데이터베이스에서 실행되며 각 데이터베이스의 데이터를 수정할 수 있습니다. restrict_gitlab_migration gitlab_schema:가 지정된 경우, DML 마이그레이션은 지정된 gitlab_schema를 포함하는 데이터베이스 컨텍스트에서만 실행됩니다.

예시: 모든 데이터베이스에서 DML gitlab_shared 마이그레이션 실행#

lib/gitlab/database/gitlab_schemas.yml에서 gitlab_shared로 표시된 loose_foreign_keys_deleted_records 테이블을 업데이트하는 마이그레이션 예시입니다.

이 마이그레이션은 구성된 모든 데이터베이스에서 실행됩니다.

class DeleteAllLooseForeignKeyRecords < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    execute("DELETE FROM loose_foreign_keys_deleted_records")
  end

  def down
    # no-op
  end
end

예시: 지정된 gitlab_schema를 포함하는 데이터베이스에서만 DML gitlab_shared 실행#

db/docs/loose_foreign_keys_deleted_records.yml에서 gitlab_shared로 표시된 loose_foreign_keys_deleted_records 테이블을 업데이트하는 마이그레이션 예시입니다.

이 마이그레이션은 gitlab_ci 제한이 구성되어 있으므로 gitlab_ci 스키마를 포함하는 데이터베이스 컨텍스트에서만 실행됩니다.

class DeleteCiBuildsLooseForeignKeyRecords < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  restrict_gitlab_migration gitlab_schema: :gitlab_ci

  def up
    execute("DELETE FROM loose_foreign_keys_deleted_records WHERE fully_qualified_table_name='ci_builds'")
  end

  def down
    # no-op
  end
end

마이그레이션 건너뛰기 동작#

건너뛰는 마이그레이션은 DML 변경을 수행하는 마이그레이션뿐입니다. DDL 마이그레이션은 항상 조건 없이 실행됩니다.

구현된 솔루션database_tasks:를 사용하여 config/database.yml의 추가 데이터베이스 구성 중 동일한 기본 데이터베이스를 공유하는 항목을 표시합니다. database_tasks: false로 표시된 데이터베이스 구성은 해당 데이터베이스 구성에 대해 db:migrate를 실행하는 것이 면제됩니다.

데이터베이스 구성이 데이터베이스를 공유하지 않는 경우(모두 database_tasks: true인 경우), 각 마이그레이션은 모든 데이터베이스 구성에 대해 실행됩니다:

  • DDL 마이그레이션은 모든 데이터베이스에 모든 구조 변경을 적용합니다.

  • DML 마이그레이션은 지정된 gitlab_schema:를 포함하는 데이터베이스 컨텍스트에서만 실행됩니다.

  • DML 마이그레이션이 실행 자격이 없는 경우 건너뜁니다. 해당 마이그레이션은 여전히 schema_migrations에 실행된 것으로 표시됩니다. db:migrate를 실행하는 동안 건너뛴 마이그레이션은 Current migration is skipped since it modifies 'gitlab_ci' which is outside of 'gitlab_main, gitlab_shared를 출력합니다.

database_tasks: false가 구성된 경우 마이그레이션 손실을 방지하기 위해, 전용 Rake 태스크 gitlab:db:validate_config가 사용됩니다. gitlab:db:validate_config는 각 기본 데이터베이스 구성의 데이터베이스 식별자를 확인하여 database_tasks:의 정확성을 검증합니다. 동일한 데이터베이스를 공유하는 구성은 database_tasks: false가 설정되어야 합니다. gitlab:db:validate_config는 항상 db:migrate 전에 실행됩니다.

유효성 검사#

유효성 검사는 간단히 말해 pg_query를 사용하여 각 쿼리를 분석하고 db/docs/의 정보로 테이블을 분류합니다. 지정된 gitlab_schema가 특정 데이터베이스 연결에서 관리되는 스키마 목록(Gitlab::Database::gitlab_schemas_for_connection) 외부에 있으면 마이그레이션을 건너뜁니다.

Gitlab::Database::Migration[2.0]에는 #migrate 메서드를 확장하는 Gitlab::Database::MigrationHelpers::RestrictGitlabSchema가 포함됩니다. 마이그레이션 실행 중에 restrict_gitlab_migration:으로 정의된 허용된 스키마 목록을 받는 전용 쿼리 분석기 Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas가 설치됩니다. 실행된 쿼리가 허용된 스키마 외부에 있으면 예외가 발생합니다.

예외#

restrict_gitlab_migration의 오용 또는 누락에 따라 마이그레이션 실행 중 다양한 예외가 발생할 수 있으며 마이그레이션 완료를 막을 수 있습니다.

예외 1: DDL 모드에서 실행되는 마이그레이션이 DML select를 수행하는 경우#

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  # Missing:
  # restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  def up
    update_column_in_batches(:projects, :archived, true) do |table, query|
      query.where(table[:archived].eq(false)) # rubocop:disable CodeReuse/ActiveRecord
    end
  end

  def down
    # no-op
  end
end
Select/DML queries (SELECT/UPDATE/DELETE) are disallowed in the DDL (structure) mode
Modifying of 'projects' (gitlab_main) with 'SELECT * FROM projects...

현재 마이그레이션은 restrict_gitlab_migration을 사용하지 않습니다. 이 누락은 DDL 모드로 실행 중인 마이그레이션을 나타내지만, 실행된 페이로드는 projects에서 데이터를 읽으려 합니다.

해결 방법restrict_gitlab_migration gitlab_schema: :gitlab_main_org를 추가하는 것입니다.

예외 2: DML 모드에서 실행되는 마이그레이션이 구조를 변경하는 경우#

class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  # restrict_gitlab_migration if defined indicates DML, it should be removed
  restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  INDEX_NAME = 'index_on_merge_request_reviewers_user_id_and_state'

  def up
    add_concurrent_index :merge_request_reviewers, [:user_id, :state], where: 'state = 2', name: INDEX_NAME
  end

  def down
    remove_concurrent_index_by_name :merge_request_reviewers, INDEX_NAME
  end
end
DDL queries (structure) are disallowed in the Select/DML (SELECT/UPDATE/DELETE) mode.
Modifying of 'merge_request_reviewers' with 'CREATE INDEX...

현재 마이그레이션은 restrict_gitlab_migration을 사용합니다. 이 존재는 DML 모드를 나타내지만, 실행된 페이로드는 구조 변경(DDL)을 수행하려 합니다.

해결 방법restrict_gitlab_migration gitlab_schema: :gitlab_main_org를 제거하는 것입니다.

예외 3: DML 모드에서 실행되는 마이그레이션이 다른 스키마의 테이블 데이터에 접근하는 경우#

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  # Since it modifies `projects` it should use `gitlab_main`
  restrict_gitlab_migration gitlab_schema: :gitlab_ci

  def up
    update_column_in_batches(:projects, :archived, true) do |table, query|
      query.where(table[:archived].eq(false)) # rubocop:disable CodeReuse/ActiveRecord
    end
  end

  def down
    # no-op
  end
end
Select/DML queries (SELECT/UPDATE/DELETE) do access 'projects' (gitlab_main) " \
which is outside of list of allowed schemas: 'gitlab_ci'

현재 마이그레이션은 gitlab_ci로 마이그레이션을 제한하지만, gitlab_main의 데이터를 수정하려 합니다.

해결 방법restrict_gitlab_migration gitlab_schema: :gitlab_ci를 변경하는 것입니다.

예외 4: DDL과 DML 모드 혼용#

class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  # This migration is invalid regardless of specification
  # as it cannot modify structure and data at the same time
  restrict_gitlab_migration gitlab_schema: :gitlab_ci

  def up
    add_concurrent_index :merge_request_reviewers, [:user_id, :state], where: 'state = 2', name: 'index_on_merge_request_reviewers'
    update_column_in_batches(:projects, :archived, true) do |table, query|
      query.where(table[:archived].eq(false)) # rubocop:disable CodeReuse/ActiveRecord
    end
  end

  def down
    # no-op
  end
end

DDLDML을 혼용하는 마이그레이션은 작업 순서에 따라 앞서 설명한 예외 중 하나가 발생합니다.

다중 데이터베이스 마이그레이션의 향후 변경 사항#

gitlab_schema:를 사용하는 restrict_gitlab_migration은 컨텍스트에 따라 선택적으로 마이그레이션을 실행하는 이 기능의 첫 번째 반복으로 간주됩니다. 추후 변경이 없을 때까지 구조 일관성이 유지될 가능성이 높으므로, DML 전용 마이그레이션에 추가 제한을 둬 실행 시기를 제한할 수 있습니다.

잠재적인 확장으로는 DML 마이그레이션 실행을 특정 환경으로만 제한하는 것이 있습니다:

restrict_gitlab_migration gitlab_schema: :gitlab_main_org, gitlab_env: :gitlab_com

백그라운드 마이그레이션#

다음을 사용하는 경우:

  • track_jobstrue로 설정된 백그라운드 마이그레이션, 또는

  • 배치 백그라운드 마이그레이션

마이그레이션은 jobs 테이블에 써야 합니다. 백그라운드 마이그레이션에서 사용하는 모든 jobs 테이블은 gitlab_shared로 표시됩니다. 어떤 데이터베이스의 테이블을 마이그레이션할 때도 이 마이그레이션을 사용할 수 있습니다.

그러나 배치를 큐에 넣을 때는 반복하는 테이블을 기반으로 restrict_gitlab_migration을 설정해야 합니다. 예를 들어, 모든 projects를 업데이트하는 경우 restrict_gitlab_migration gitlab_schema: :gitlab_main_org로 설정합니다. 반면 모든 ci_pipelines를 업데이트하는 경우에는 restrict_gitlab_migration gitlab_schema: :gitlab_ci로 설정합니다.

모든 DML 마이그레이션과 마찬가지로, restrict_gitlab_migration 또는 gitlab_shared 외부의 다른 데이터베이스를 쿼리할 수 없습니다. 다른 데이터베이스를 쿼리해야 하는 경우 마이그레이션을 분리하세요.

백그라운드 마이그레이션의 실제 마이그레이션 로직(큐잉 단계 제외)은 Sidekiq 워커에서 실행되므로, 일반적인 Sidekiq 워커처럼 모든 데이터베이스의 테이블에 대해 DML 쿼리를 수행할 수 있습니다.

주어진 테이블에 대한 gitlab_schema 결정 방법#

데이터베이스 사전을 참조하세요.