- 작업
- 검증
- 예외
- 다중 데이터베이스 마이그레이션의 향후 변경 사항
- 백그라운드 마이그레이션
- 주어진 테이블에 대한
gitlab_schema
결정 방법
여러 데이터베이스를 위한 마이그레이션
이 문서는 여러 데이터베이스를 사용하는 분해된 GitLab 애플리케이션을 위한 데이터베이스 마이그레이션을 올바르게 작성하는 방법에 대해 설명합니다.
자세한 정보는 여러 데이터베이스를 참조하세요.
여러 데이터베이스에 대한 설계(Geo 데이터베이스를 제외하고)는 모든 분해된 데이터베이스가 동일한 구조(예: 스키마)를 가지고 있지만 각 데이터베이스의 데이터는 다르다는 것을 전제로 합니다. 이는 일부 테이블이 각 데이터베이스에 데이터가 포함되어 있지 않다는 것을 의미합니다.
작업
사용되는 구성에 따라 마이그레이션을 다음과 같이 분류할 수 있습니다:
- 구조 수정하기 (DDL - 데이터 정의 언어) (예:
ALTER TABLE
). - 데이터 수정하기 (DML - 데이터 조작 언어) (예:
UPDATE
). -
DML로 처리되는 기타 쿼리 수행하기 (예:
SELECT
).
Gitlab::Database::Migration[2.0]
의 사용은 마이그레이션이 항상 단일 용도로 해야 함을 요구합니다.
마이그레이션은 애플리케이션이 모든 분해된 데이터베이스에서 구조가 정확히 동일해야 하므로 DDL과 DML 변경을 혼합할 수 없습니다.
데이터 정의 언어 (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 마이그레이션은 CANNOT:
- SQL 문 또는 ActiveRecord 모델을 통해 데이터를 읽거나 수정할 수 없습니다.
- 열 값을 업데이트할 수 없습니다 (예:
update_column_in_batches
). - 백그라운드 마이그레이션을 예약할 수 없습니다 (예:
queue_background_migration_jobs_by_range_at_intervals
). -
main:
에 저장된 기능 플래그의 상태를 읽을 수 없습니다 (기능과 기능 게이트). - 애플리케이션 설정을 읽을 수 없습니다 (설정이
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: 설명 예시 introduced_by_url: 병합 요청 링크 milestone: 마일스톤 예시 feature_categories: - 기능 카테고리 예시 classes: - 클래스 예시 gitlab_schema: gitlab_main
-
스키마 마이그레이션에서 테이블을 만듭니다:
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
데이터 조작 언어 (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_background_migration_jobs_by_range_at_intervals
). - 애플리케이션 설정 접근 (예:
ApplicationSetting.last
if run formain:
database). -
main:
데이터베이스에서 실행될 경우 기능 플래그를 읽고 수정합니다.
DML 마이그레이션은 할 수 없습니다:
-
structure.sql
을 모든 분해된 데이터베이스에서 일관되게 유지하는 규칙을 깨기 때문에 DDL에 대한 변경을 할 수 없습니다. - 다른 데이터베이스에서 데이터 읽기.
DML
마이그레이션 유형을 나타내려면 마이그레이션은 마이그레이션 클래스에서 restrict_gitlab_migration gitlab_schema:
구문을 사용해야 합니다. 이것은 주어진 마이그레이션을 DML로 표시하고 해당 마이그레이션에 대한 접근을 제한합니다.
예시: 주어진 gitlab_schema
를 포함하는 데이터베이스의 컨텍스트에서 DML만 수행하기
gitlab_main
스키마를 포함하는 데이터베이스에 대해만 실행되는 projects
의 archived
열을 업데이트하는 예제 마이그레이션입니다.
class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
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
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:
를 사용하는 방법을 제공합니다(기본 데이터베이스와 같은 공유).
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
가 포함되어 있습니다.
마이그레이션 기간 동안 허용된 스키마 목록을 수락하는 전용 쿼리 분석기 Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas
가 설치됩니다.
허용된 스키마 외부에서 실행된 쿼리는 예외를 발생시킵니다.
예외
잘못 사용되거나 restrict_gitlab_migration
가 없을 경우
마이그레이션 실행의 일환으로 여러 가지 예외가 발생할 수 있으며
마이그레이션이 완료되는 것을 방지할 수 있습니다.
예외 1: DDL 모드에서 실행되는 마이그레이션이 DML 선택을 수행함
class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
# 누락:
# restrict_gitlab_migration gitlab_schema: :gitlab_main
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
DDL(구조) 모드에서는 Select/DML 쿼리(SELECT/UPDATE/DELETE)가 허용되지 않습니다.
'projects' (gitlab_main) 수정 중 'SELECT * FROM projects...
현재 마이그레이션은 restrict_gitlab_migration
를 사용하지 않으며
이로 인해 DDL 모드에서 실행되고 있지만 실행된 페이로드가 projects
에서 데이터를 읽고 있습니다.
해결책은 restrict_gitlab_migration gitlab_schema: :gitlab_main
을 추가하는 것입니다.
예외 2: DML 모드에서 실행되는 마이그레이션이 구조를 변경함
class AddUserIdAndStateIndexToMergeRequestReviewers < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
# 정의된 경우 restrict_gitlab_migration은 DML을 나타내며 제거되어야 합니다.
restrict_gitlab_migration gitlab_schema: :gitlab_main
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
Select/DML (SELECT/UPDATE/DELETE) 모드에서는 DDL 쿼리(구조)가 허용되지 않습니다.
'CREATE INDEX...'로 'merge_request_reviewers'를 수정 중입니다.
현재 마이그레이션은 restrict_gitlab_migration
를 사용하고 있습니다.
존재 때문에 DML 모드로 표시되지만 실행된 페이로드가 구조 변경(DDL)을 하고 있습니다.
해결책은 restrict_gitlab_migration gitlab_schema: :gitlab_main
을 제거하는 것입니다.
예외 3: DML 모드에서 실행되는 마이그레이션이 다른 스키마의 테이블에서 데이터에 접근함
class UpdateProjectsArchivedState < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
# 'projects'를 수정하므로 '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 쿼리(SELECT/UPDATE/DELETE)는 'projects' (gitlab_main)에 접근하며,
허용된 스키마 목록: '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!
# 이 마이그레이션은 사양에 관계없이 유효하지 않습니다.
# 구조와 데이터를 동시에 수정할 수 없기 때문입니다.
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
DDL 및 DML을 혼합한 마이그레이션은 작업 순서에 따라 이전 예외 중 하나를 발생시킵니다.
다중 데이터베이스 마이그레이션의 향후 변경 사항
restrict_gitlab_migration
을 gitlab_schema:
와 함께 사용하는 것은
상황에 따라 선택적으로 마이그레이션을 실행하기 위한
이 기능의 첫 번째 반복으로 간주됩니다. DML 전용 마이그레이션에
추가 제한을 추가하는 것이 가능하며(구조 일관성은
추가 통지가 있을 때까지 동일하게 유지될 가능성이 높음)
언제 실행될지를 제한할 수 있습니다.
잠재적인 확장으로 특정 환경에만 DML 마이그레이션을 실행하도록 제한할 수 있습니다:
restrict_gitlab_migration gitlab_schema: :gitlab_main, gitlab_env: :gitlab_com
백그라운드 마이그레이션
다음과 같이 사용할 때:
-
track_jobs
가true
로 설정된 백그라운드 마이그레이션 또는 - 배치 처리된 백그라운드 마이그레이션
마이그레이션은 작업 테이블에 써야 합니다. 백그라운드 마이그레이션에서 사용되는 모든
작업 테이블은 gitlab_shared
로 표시됩니다. 이러한 마이그레이션은
모든 데이터베이스의 테이블을 마이그레이션할 때 사용할 수 있습니다.
그러나 배치를 큐에 넣을 때는 반복하고 있는 테이블을 기준으로
restrict_gitlab_migration
을 설정해야 합니다. 예를 들어, 모든 projects
를
업데이트하고 있는 경우, restrict_gitlab_migration gitlab_schema: :gitlab_main
으로 설정합니다.
하지만 모든 ci_pipelines
를 업데이트하는 경우,
restrict_gitlab_migration gitlab_schema: :gitlab_ci
로 설정해야 합니다.
모든 DML 마이그레이션과 마찬가지로,
restrict_gitlab_migration
또는 gitlab_shared
외부의 다른 데이터베이스를
쿼리할 수 없습니다. 다른 데이터베이스를 쿼리해야 하는 경우,
마이그레이션을 분리하십시오.
백그라운드 마이그레이션에 대한 실제 마이그레이션 로직(큐잉 단계가 아님)은 사이키크(Sidekiq) 작업자에서 실행되므로, 로직은 어떤 데이터베이스의 테이블에 대해서도 DML 쿼리를 수행할 수 있습니다. 일반적인 사이키크 작업자가 할 수 있는 것처럼 말입니다.
주어진 테이블에 대한 gitlab_schema
결정 방법
데이터베이스 사전을 참조하세요.