다중 데이터베이스 이관
- 이관 목적을 설명하는 지원은 GitLab 14.8에서 도입되었습니다.
이 문서는 여러 데이터베이스를 사용하는 분해된 GitLab 애플리케이션을 위해 데이터베이스 이관을 올바르게 작성하는 방법을 설명합니다. 자세한 내용은 다중 데이터베이스(multiple databases)를 참조하세요.
다중 데이터베이스의 디자인(Geo 데이터베이스 제외)은 모든 분해된 데이터베이스가 동일한 구조(예: 스키마)를 가지지만 각 데이터베이스의 데이터가 다르다고 가정합니다. 이는 일부 테이블이 각 데이터베이스에 데이터를 포함하지 않음을 의미합니다.
작업
사용된 구조에 따라, 이관을 다음과 같이 분류할 수 있습니다:
- 구조 수정 (DDL - 데이터 정의 언어)(예:
ALTER TABLE
). - 데이터 수정 (DML - 데이터 조작 언어)(예:
UPDATE
). -
다른 쿼리를 수행함(예:
SELECT
)하여 우리의 이관 목적을 위해 DML로 처리됨.
Gitlab::Database::Migration[2.0]
의 사용은 항상 하나의 목적의 이관이 되어야 함을 의미합니다.
이관은 응용프로그램이 모든 분해된 데이터베이스에서 동일한 구조(db/structure.sql
에 기술된대로)를 요구하기 때문에 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
).
이러한 DDL 이관은 다음과 같이 할 수 없습니다:
- SQL 문이나 ActiveRecord 모델을 통한 데이터 읽기 또는 수정.
- 열 값 업데이트(예:
update_column_in_batches
). - 백그라운드 이관 예약(예:
queue_background_migration_jobs_by_range_at_intervals
). -
main:
에 저장된 피처 플래그 상태 읽기(기능 및feature_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
예제: 단일 데이터베이스에 저장을 위한 새로운 테이블 추가
-
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
-
스키마 이관에서 테이블 생성:
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
). - 응용 프로그램 설정 액세스(예:
main:
데이터베이스용ApplicationSetting.last
). -
main:
데이터베이스를 위해 피처 플래그 읽기 및 수정.
DML 이관은 다음과 같이 할 수 없습니다:
- DDL에 대한 어떠한 변경도 수행할 수 없으므로 모든 분해된 데이터베이스에서
structure.sql
를 일치시키는 규칙을 위반함. - 다른 데이터베이스에서 데이터 읽기.
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:
를 사용할 필요가 없으며, 제한 없이 실행되는 마이그레이션이 모든 데이터베이스에 걸쳐 데이터를 수정할 수 있습니다. gitlab_schema:
가 지정된 경우 restrict_gitlab_migration gitlab_schema:
이 예시된다면 DML
마이그레이션은 주어진 gitlab_schema
를 포함하는 데이터베이스 컨텍스트에서만 실행됩니다.
예시: 모든 데이터베이스에서 gitlab_shared
DML 마이그레이션 실행
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
를 포함하는 데이터베이스에서만 gitlab_shared
DML 실행
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
를 실행하는 동안 생략된 마이그레이션은 “현재 마이그레이션이 ‘gitlab_main, gitlab_shared’ 바깥의 ‘gitlab_ci’을 수정하기 때문에 생략됩니다”를 출력합니다.
database_tasks: false
이 구성되면 마이그레이션이 손실되는 것을 방지하기 위해 전용 Rake 작업이 사용됩니다 gitlab:db:validate_config
. gitlab:db:validate_config
는 각 기본 데이터베이스 구성의 데이터베이스 식별자를 확인하여 database_tasks:
의 정확성을 검증합니다. 데이터베이스 구성은 동일한 주 데이터베이스를 공유해야 하는 데이터베이스의 경우 database_tasks: false
로 설정되어야 합니다. gitlab:db:validate_config
는 항상 db:migrate
의 전에 실행됩니다.
유효성 검사
유효성 검사는 각 쿼리를 분석하고 db/docs/
의 정보를 사용하여 테이블을 분류하기 위해 pg_query
를 활용합니다. 특정 gitlab_schema
가 주어진 데이터베이스 연결(Gitlab::Database::gitlab_schemas_for_connection
)에 의해 관리되는 스키마 디렉터리을 벗어나는 경우, 마이그레이션이 생략됩니다.
Gitlab::Database::Migration[2.0]
는 Gitlab::Database::MigrationHelpers::RestrictGitlabSchema
를 포함하며 #migrate
메서드를 확장합니다. 마이그레이션이 실행되는 동안 전용 쿼리 분석기 Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas
가 설치되며 restrict_gitlab_migration:
에 의해 정의된 허용된 스키마 디렉터리을 수락합니다. 실행된 쿼리가 허용된 스키마 디렉터리을 벗어난 경우, 예외를 발생시킵니다.
예외
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(구조) 모드에서 선택/DML 쿼리(SELECT/UPDATE/DELETE)가 허용되지 않습니다
'projects'를 수정 중 '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
DDL(구조) 쿼리는 Select/DML(SELECT/UPDATE/DELETE) 모드에서 허용되지 않습니다.
'merge_request_reviewers'의 수정은 'INDEX 생성...'과 같은 구조 변경을 시도합니다.
현재 마이그레이션은 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
# 작업 없음
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!
# 이 마이그레이션은 구조와 데이터를 동시에 수정할 수 없기 때문에 specification과 무관하게 잘못되었습니다.
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
# 작업 없음
end
end
작업 순서에 따라 DDL 및 DML을 혼합하는 마이그레이션은 이전에 언급된 예외의 하나를 발생시킵니다.
다중 데이터베이스 마이그레이션에 대한 예정된 변경 사항
gitlab_schema:
를 사용한 restrict_gitlab_migration
은 맥락에 따라 실행되는 마이그레이션의 첫 번째 반복이고 이 기능은 가능성이 있습니다.
추가 제약을 추가하여 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 작업자에서 실행되므로 마이그레이션 논리는 일반적인 Sidekiq 작업자처럼 어느 데이터베이스의 테이블에서도 DML 쿼리를 수행할 수 있습니다.
주어진 테이블에 대한 gitlab_schema
결정 방법
데이터베이스 사전을 참조하세요.