- 작업
- 유효성 검사
- 예외
- 다중 데이터베이스 마이그레이션에 대한 예정 변경 사항
- 백그라운드 마이그레이션
- 주어진 테이블의
gitlab_schema결정 방법
다중 데이터베이스 마이그레이션
- 마이그레이션 목적을 설명하는 지원이 GitLab 14.8에서 도입되었습니다.
이 문서는 다중 데이터베이스를 사용하여 GitLab 애플리케이션을 분해하는 방법에 대한 데이터베이스 마이그레이션을 올바르게 작성하는 방법을 설명합니다. 자세한 정보는 다중 데이터베이스를 참조하십시오.
다중 데이터베이스의 설계(지오 데이터베이스 제외)는 모든 분해된 데이터베이스가 동일한 구조(예: 스키마)를 가지지만 각 데이터베이스에서는 데이터가 다르다는 것을 전제로 합니다. 즉, 일부 테이블은 각 데이터베이스에 데이터를 포함하지 않습니다.
작업
사용된 구성에 따라 마이그레이션을 다음과 같이 분류할 수 있습니다.
- 구조 수정 (DDL - 데이터 정의 언어) (예:
ALTER TABLE). - 데이터 수정(DML - 데이터 조작 언어) (예:
UPDATE). - 다른 쿼리 수행(기타 쿼리) (예:
SELECT)는 마이그레이션의 목적에 따라 DML로 처리됩니다.
Gitlab::Database::Migration[2.0] 사용은 마이그레이션이 항상 하나의 목적을 가져야한다.
마이그레이션은 DDL 및 DML 변경을 혼합할 수 없으며 애플리케이션은 모든 분해된 데이터베이스에서 구조를 정확히 동일하게 유지해야합니다(db/structure.sql로 설명됨).
데이터 정의 언어 (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:에 저장되어 있기 때문에 기능 플래그의 상태를 읽지 못합니다(features및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
예시: 단일 데이터베이스에 저장하기 위한 새 테이블 추가
-
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). - 애플리케이션 설정에 액세스함(예:
main:데이터베이스에 대해 실행된 경우ApplicationSetting.last). -
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의 활성 연결이 변경됨. 따라서 MigrationRecord를 사용하여 ActiveRecord::Base를 사용하는 것을 피해야 함.
이는 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 테이블에는 모든 데이터베이스를 통틀어 포함되는 데이터가 허용됨. 이는 이러한 마이그레이션이 모든 데이터베이스에서 실행되어 각각 수정을 수행할 수 있음을 나타냄.
따라서 gitlab_shared에 액세스하는 이러한 마이그레이션은 restrict_gitlab_migration 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(데이터 정의 언어) 마이그레이션은 항상 무조건적으로 실행됩니다.
구현된 해결책은 추가 데이터베이스 구성(config/database.yml 내)이 주 데이터베이스를 공유하는지를 나타내기 위해 database_tasks:를 사용합니다. 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: 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
# 무작위 작업
end
end
Select/DML 쿼리 (SELECT/UPDATE/DELETE)는 DDL(구조) 모드에서 허용되지 않습니다
'projects'를 수정하는 중에 'projects에서 SELECT *...'의 데이터를 읽는 중
현재 마이그레이션은 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'를 수정하는 중에 'CREATE 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 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!
# 이 마이그레이션은 명세에 관계없이 올바르지 않습니다
# 구조 및 데이터를 동시에 수정할 수 없기 때문입니다
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을 혼합하는 마이그레이션이 연산 순서에 따라 이전 예외 중 하나를 발생시킵니다.
다중 데이터베이스 마이그레이션에 대한 예정 변경 사항
restrict_gitlab_migration은 gitlab_schema:를 사용하여 컨텍스트에 따라 마이그레이션을 선택적으로 실행하는 기능의 첫 번째 반복으로 간주됩니다. structure의 일관성이 추가로 고지될 때까지 DML 전용 마이그레이션에 추가 제한을 추가하는 것이 가능합니다.
잠재적으로 확장되는 방법은 DML 마이그레이션을 특정 환경에서만 실행하도록 제한하는 것입니다:
restrict_gitlab_migration gitlab_schema: :gitlab_main, gitlab_env: :gitlab_com
백그라운드 마이그레이션
다음을 사용할 때:
-
track_jobs가true로 설정된 백그라운드 마이그레이션 또는 - 배치형 백그라운드 마이그레이션
마이그레이션은 jobs 테이블에 쓰여야 합니다. 백그라운드 마이그레이션에 의해 사용된 모든 jobs 테이블은 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 결정 방법
데이터베이스 사전을 참조하세요.
도움말