- 일괄 배치 백그라운드 마이그레이션을 사용하는 시점
- 일괄 배치 백그라운드 마이그레이션 작동 방식
- 방법
- 관리
- EE 전용 기능을 위한 일괄 배경 마이그레이션
- 디버그
- 테스트
- 모범 사례
- 예시
일괄 배치 백그라운드 마이그레이션
일괄 배치 백그라운드 마이그레이션은 우리의 지침에서 migration이 시간 제한을 초과하는 경우 데이터 마이그레이션을 수행하는 데 사용해야 합니다. 예를 들어, 일괄 배치 백그라운드 마이그레이션을 사용하여 JSON 열에 저장된 데이터를 별도의 테이블로 마이그레이션할 수 있습니다.
일괄 배치 백그라운드 마이그레이션을 사용하는 시점
일괄 배치 백그라운드 마이그레이션은 _데이터_를 마이그레이션할 때 사용해야 합니다. 마이그레이션 대상 테이블에 있는 행이 많아서 정규 Rails 마이그레이션을 사용하면 우리의 지침에서 제한 시간을 초과할 경우 사용합니다.
- 일괄 배치 백그라운드 마이그레이션은 고트래픽 테이블에서 데이터를 마이그레이션할 때 사용해야 합니다.
- 대규모 데이터 세트의 각 항목에 대해 수 많은 단일 행 쿼리를 실행할 때에도 일괄 배치 백그라운드 마이그레이션을 사용할 수 있습니다. 일반적으로 단일 레코드 패턴의 경우 런타임은 데이터 집합의 크기에 크게 의존합니다. 따라서 데이터 집합을 적절하게 분할하고 이를 백그라운드 마이그레이션에 넣으십시오.
- 일괄 배치 백그라운드 마이그레이션은 스키마 마이그레이션을 수행하는 데 사용하지 마십시오.
백그라운드 마이그레이션은 다음 경우에 도움이 됩니다:
- 한 테이블에서 여러 개의 별도 테이블로 이벤트를 마이그레이션하는 경우
- 다른 열에 저장된 JSON을 기반으로 한 열을 채우는 경우
- 외부 서비스의 출력에 따라 데이터를 마이그레이션하는 경우. (예: API)
참고사항
- 일괄 배치 백그라운드 마이그레이션이 중요한 업그레이드의 일부인 경우 릴리스 게시물에서 발표해야 합니다. 마이그레이션이 이 범주에 속하는지 확실하지 않은 경우 프로젝트 매니저와 논의하십시오.
- 필요한 파일이 기본적으로 생성되도록 일괄 배치 백그라운드 마이그레이션을 생성하기 위해 generator를 사용해야 합니다.
일괄 배치 백그라운드 마이그레이션 작동 방식
일괄 배치 백그라운드 마이그레이션(BBM)은 Gitlab::BackgroundMigration::BatchedMigrationJob
의 하위 클래스로서 perform
메서드를 정의합니다. 첫 번째 단계로 일반 마이그레이션은 필요한 인수를 가진 batched_background_migrations
레코드를 생성합니다. 기본적으로 batched_background_migrations
는 활성 상태이며, 이를 Sidekiq worker가 실제 일괄 마이그레이션을 실행하도록 진행합니다.
모든 마이그레이션 클래스는 Gitlab::BackgroundMigration
네임스페이스에 정의되어야 합니다. 파일은 lib/gitlab/background_migration/
디렉터리에 위치시켜야 합니다.
실행 메커니즘
일괄 배치 백그라운드 마이그레이션은 대기열에서 enqueued된 순서대로 가져오게 됩니다. 여러 마이그레이션이 병렬로 가져와서 실행되는데, 이 때 활성 상태에 있고 동일한 데이터베이스 테이블을 대상으로하지 않아야 합니다. 기본적으로 병렬로 처리되는 마이그레이션 수는 2개이며, GitLab.com의 경우 이 한도는 4로 구성됩니다. 마이그레이션이 실행되면 작업이 특정 배치를 위해 생성됩니다. 각 작업 실행 후, 마지막 20개의 작업 성능에 따라 마이그레이션의 배치 크기를 증가 또는 감소시킬 수 있습니다.
일괄 배치 백그라운드 마이그레이션이 실행될 때 빠른 시일 안에 실행 워커가 이를 처리하게 됩니다.
동질성
일괄 배치 백그라운드 마이그레이션은 Sidekiq 프로세스의 맥락에서 실행됩니다. 일반적인 Sidekiq 규칙이 적용되며 특히 작업은 작고 동질적이어야 합니다. 마이그레이션 작업이 재시도될 경우 데이터 무결성이 보장되도록 주의하십시오.
자세한 내용은 Sidekiq 최선의 방법 가이드라인을 참조하십시오.
마이그레이션 최적화
각 작업 실행 후, 마이그레이션을 최적화할 수 있는지 확인하는 검증이 수행됩니다. 최적화 기반이 시간 효율성 개념에 기초하며, 마지막 N개의 작업의 시간 효율성의 지수 이동 평균을 계산하고 일괄 배치 백그라운드 마이그레이션의 배치 크기를 최적의 값으로 업데이트합니다.
작업 재시도 메커니즘
일괄 배치 백그라운드 마이그레이션의 재시도 메컨즈은 작업이 실패한 경우 다시 실행되도록 보장합니다. 다음 다이어그램은 재시도 메커니즘의 다양한 단계를 보여줍니다:
-
MAX_ATTEMPTS
는Gitlab::Database::BackgroundMigration
클래스에서 정의됩니다. -
can_split?
은Gitlab::Database::BatchedJob
클래스에서 정의됩니다.
실패한 일괄 배치 백그라운드 마이그레이션
일괄 백그라운드 마이그레이션이 “실패”로 표시됩니다 (/chatops run batched_background_migrations status MIGRATION_ID
를 실행하면 마이그레이션이 “실패”로 표시됨) 다음 중 하나라도 해당되는 경우:
- 더 이상 소비할 작업이 없고 실패한 작업이 있는 경우.
- 백그라운드 마이그레이션이 시작된 이후로 작업의 절반 이상이 실패한 경우.
일괄 배치 마이그레이션 쓰로틀링
배치 마이그레이션이 업데이트가 많이 이루어지고 데이터베이스가 성능 저하 상태일 때 이러한 작업량으로 인해 과부하가 걸린 기간이 있었기 때문에 쓰로틀링 메커니즘이 존재하여 이를 완화합니다.
이러한 데이터베이스 지표가 마이그레이션을 쓰로틀링할 때 확인됩니다. 정지 신호를 받으면 마이그레이션은 설정된 시간 (10분) 동안 일시 중지됩니다:
- 임계값을 초과하는 WAL 큐 대기 아카이브.
- 마이그레이션이 작동 중인 테이블에서 활성화된 autovacuum.
- Patroni apdex SLI가 SLO 아래로 떨어짐.
- 임계값을 초과하는 WAL 속도.
데이터베이스 상태 점검 프레임워크를 더 향상시키기 위해 계속된 노력이 이루어지고 있습니다. 더 많은 세부 정보는 에픽 7594을 참조하세요.
격리
일괄 배치 백그라운드 마이그레이션은 격리되어야 하며 애플리케이션 코드 (예: app/models
에 정의된 모델, ApplicationRecord
클래스 제외)를 사용할 수 없습니다.
이러한 마이그레이션이 실행에 오랜 시간이 걸릴 수 있으므로 새 버전이 배포될 수도 있습니다.
마이그레이트된 데이터에 따라 달라짐
일반적인 또는 마이그레이션 후와는 달리 데이터가 완전히 마이그레이트된 것을 보장하기 위해서는 다음 릴리스를 기다리는 것만으로 충분하지 않습니다.
이것은 즉, BBM이 완료될 때까지 데이터에 의존해서는 안 된다는 것을 의미합니다. 데이터의 100%가 마이그레이트되어야 하는 요구 사항이 있다면, ensure_batched_background_migration_is_finished
도우미를 사용하여 마이그레이션이 완료되고 데이터가 완전히 마이그레이트된 것을 보장할 수 있습니다. (예제로 이동).
방법
일괄 배치 백그라운드 마이그레이션 생성
사용자 지정 생성기 batched_background_migration
은 필요한 파일을 만들고
table_name
, column_name
, feature_category
를 인수로 받습니다. 사용법:
bundle exec rails g batched_background_migration my_batched_migration --table_name=<테이블-이름> --column_name=<컬럼-이름> --feature_category=<기능-카테고리>
이 명령은 다음 파일을 생성합니다:
db/post_migrate/20230214231008_queue_my_batched_migration.rb
spec/migrations/20230214231008_queue_my_batched_migration_spec.rb
lib/gitlab/background_migration/my_batched_migration.rb
spec/lib/gitlab/background_migration/my_batched_migration_spec.rb
일괄 배치 백그라운드 마이그레이션을 대기열에 넣기
일괄 백그라운드 마이그레이션을 대기열에 넣는 것은 배포 후에 수행되어야 합니다.
다음 예시를 사용하여 queue_batched_background_migration
을 사용하여 마이그레이션을 일괄로 실행하도록 대기열에 넣습니다. 클래스 이름과 인수 값을 마이그레이션에 사용된 값으로 바꿉니다:
queue_batched_background_migration(
JOB_CLASS_NAME,
TABLE_NAME,
JOB_ARGUMENTS,
JOB_INTERVAL
)
queue_batched_background_migration
은 제공된 작업 인수의 수가 JOB_CLASS_NAME
에서 정의된 작업 인수의 수와 일치하지 않는 경우 오류를 발생시킵니다.새로 작성된 데이터가 마이그레이트되었거나 생성 시 이전 버전 및 새 버전에 모두 저장되어 있음을 확인하십시오. 교체는 외래 키를 사용하여 캐스케이딩 삭제로 처리할 수 있습니다.
일괄 배치 백그라운드 마이그레이션 완료
일괄 배치 백그라운드 마이그레이션을 완료하려면 ensure_batched_background_migration_is_finished
를 호출합니다.
안전할 때 모든 일괄 배치 백그라운드 마이그레이션을 완료하는 것이 중요합니다. 남아 있는 오래된 일괄 백그라운드 마이그레이션은 기술적 부채로, 테스트와 응용 프로그램 동작에서 유지되어야 합니다. 모든 일괄 배치 백그라운드 마이그레이션이 완료될 때까지는 완료된 것으로 의존해서는 안 된다는 것이 중요합니다.
일괄 배치 백그라운드 마이그레이션을 완료하기 위해 다음 조건이 모두 충족된 후에 권장합니다:
- 사이트인 GitLab.com에서 일괄 배치 백그라운드 마이그레이션이 완료됨
- 일괄 배치 백그라운드 마이그레이션이 마지막 필수 중지 이전에 추가되었거나 이전 버전에서 추가되었어야 합니다.
ensure_batched_background_migration_is_finished
호출은 정확히 해당 마이그레이션과 일치해야 합니다. 다음 사항에 주의하세요:
- 작업 인수: 일치해야 하며 그렇지 않으면 대기열로 이동하지 않습니다.
-
gitlab_schema
: 정확히 일치해야 하며 중간에 테이블의gitlab_schema
가gitlab_main
에서gitlab_main_cell
로 변경되었더라도queue_batched_background_migration
에 사용했다면gitlab_main
으로 완료해야 합니다.
일괄 배치 백그라운드 마이그레이션을 완료할 때는 해당하는 db/docs/batched_background_migrations
파일의 finalized_by
를 업데이트해야 합니다. 값은 완료하기 위해 추가한 마이그레이션의 타임스탬프/버전이어야 합니다.
구체적인 마이그레이션 코드에 대한 실제적인 세부 사항은 아래의 예제에서 확인하십시오.
작업 인수 사용
BatchedMigrationJob
은 작업 인수를 정의하는 작업 클래스가 제공하는 job_arguments
도우미 메서드를 제공합니다.
queue_batched_background_migration
을 사용하여 일정된 일괄 마이그레이션은 반드시 도우미를 사용하여 작업 인수를 정의해야 합니다.
queue_batched_background_migration(
'CopyColumnUsingBackgroundMigrationJob',
TABLE_NAME,
'name', 'name_convert_to_text',
job_interval: DELAY_INTERVAL
)
queue_batched_background_migration
은 오류를 발생시킵니다.이 예시에서 copy_from
은 name
을 반환하고 copy_to
는 name_convert_to_text
를 반환합니다:
class CopyColumnUsingBackgroundMigrationJob < BatchedMigrationJob
job_arguments :copy_from, :copy_to
operation_name :update_all
def perform
from_column = connection.quote_column_name(copy_from)
to_column = connection.quote_column_name(copy_to)
assignment_clause = "#{to_column} = #{from_column}"
each_sub_batch do |relation|
relation.update_all(assignment_clause)
end
end
end
필터 사용
기본적으로, 마이그레이션을 수행하는 백그라운드 작업을 생성할 때, 일괄 백그라운드 마이그레이션은 지정된 테이블 전체를 반복합니다. 이 반복 작업은 PrimaryKeyBatchingStrategy
를 사용하여 수행됩니다. 테이블에 1000개의 레코드가 있고 배치 크기가 100이면, 작업은 10개의 작업으로 나뉩니다. 설명을 위해 EachBatch
은 다음과 같이 사용됩니다:
# PrimaryKeyBatchingStrategy
Namespace.each_batch(of: 100) do |relation|
relation.where(type: nil).update_all(type: 'User') # 이 작업은 각 백그라운드 작업에서 수행됩니다
end
어떤 경우에는 레코드의 일부만 검토해야 하는 경우도 있습니다. 1000개 중 10%의 레코드만 검토해야 하는 경우, 작업을 생성할 때 초기 관련에서 필터를 적용하세요.
Namespace.where(type: nil).each_batch(of: 100) do |relation|
relation.update_all(type: 'User')
end
첫 번째 예시에서는 각 배치에서 몇 개의 레코드가 업데이트될지 알 수 없습니다. 두 번째(필터링된) 예제에서는 각 배치마다 정확히 100개가 업데이트됩니다.
BatchedMigrationJob
은 추가적인 필터를 적용하고 이를 달성하기 위한 scope_to
도우미 메서드를 제공합니다.
-
BatchedMigrationJob
를 상속하고 추가 필터를 정의하는 새로운 마이그레이션 작업 클래스를 만드세요.class BackfillNamespaceType < BatchedMigrationJob scope_to ->(relation) { relation.where(type: nil) } operation_name :update_all feature_category :source_code_management def perform each_sub_batch do |sub_batch| sub_batch.update_all(type: 'User') end end end
scope_to
를 정의하는 EE 마이그레이션의 경우, 모듈이ActiveSupport::Concern
를 확장하는지 확인하세요. 그렇지 않으면, 레코드가 스코프를 고려하지 않고 처리됩니다. -
배포 후 마이그레이션에서 일괄 백그라운드 마이그레이션을 큐잉하세요.
class BackfillNamespaceType < Gitlab::Database::Migration[2.1] MIGRATION = 'BackfillNamespaceType' DELAY_INTERVAL = 2.minutes restrict_gitlab_migration gitlab_schema: :gitlab_main def up queue_batched_background_migration( MIGRATION, :namespaces, :id, job_interval: DELAY_INTERVAL ) end def down delete_batched_background_migration(MIGRATION, :namespaces, :id, []) end end
EachBatch
성능을 최적화하기 위해 해당 필터가 적절히 다루어지는지 확인하는 것이 중요합니다. 위의 예에서는 필터를 지원하기 위해 (type, id)
에 대한 인덱스가 필요합니다. 자세한 내용은 EachBatch 문서를 참조하세요.다중 데이터베이스에 대한 데이터 액세스
일반 마이그레이션과 달리, 백그라운드 마이그레이션에는 여러 데이터베이스에 액세스할 수 있으며, 이를 효율적으로 액세스하고 업데이트하는 데 사용할 수 있습니다. 사용할 데이터베이스를 명시적으로 나타내기 위해 마이그레이션 코드 내에 ActiveRecord 모델을 생성하는 것이 바람직합니다. 해당 모델은 해당 테이블이 위치한 데이터베이스에 따라 올바른 ApplicationRecord
을 사용해야 합니다. 따라서 ActiveRecord::Base
의 사용은 해당 테이블에 대한 액세스에 사용할 데이터베이스를 설명하지 않기 때문에 허용되지 않습니다.
class Gitlab::BackgroundMigration::ExtractIntegrationsUrl
class Project < ::ApplicationRecord
self.table_name = 'projects'
end
class Build < ::Ci::ApplicationRecord
self.table_name = 'ci_builds'
end
end
class Gitlab::BackgroundMigration::ExtractIntegrationsUrl
class Project < ActiveRecord::Base
self.table_name = 'projects'
end
class Build < ActiveRecord::Base
self.table_name = 'ci_builds'
end
end
마찬가지로 ActiveRecord::Base.connection
의 사용도 허용되지 않으며, 이를 대신하여 모델 연결을 사용하세요.
Project.connection.execute("SELECT * FROM projects")
ApplicationRecord.connection.execute("SELECT * FROM projects")
ActiveRecord::Base.connection.execute("SELECT * FROM projects")
일괄 백그라운드 마이그레이션 재큐
일괄 백그라운드 마이그레이션은 여러 이유로 다시 실행될 수 있습니다:
- 마이그레이션에 버그가 있을 때 (예시).
- 마이그레이션이 데이터를 정리했지만, 응용프로그램 논리의 우회로 인해 데이터가 다시 정규화되었을 때 (예시).
- 초기 마이그레이션의 배치 크기가 마이그레이션을 실패시킬 때 (예시).
일괄 백그라운드 마이그레이션을 재큐하기 위해 다음을 수행해야 합니다:
- 기존 마이그레이션 파일의
#up
및#down
메서드 내용은 No-op으로 변경해야 합니다. 그렇지 않으면 다중 패치 릴리스를 업그레이드 중인 시스템에서 기존의 일괄 백그라운드 마이그레이션이 생성되고 삭제된 후 또한 생성됩니다. - 새로운 배포 후 마이그레이션 파일에서 일괄 백그라운드 마이그레이션을 다시 실행하는 새로운 마이그레이션 파일을 추가하세요.
- 새로운 배포 후 마이그레이션 파일에서, 기존의 일괄 백그라운드 마이그레이션 실행이 정리되도록
#up
메서드 시작 부분에delete_batched_background_migration
메서드를 추가하세요. - 기존 마이그레이션에서
db/docs/batched_background_migration/*.yml
파일을 업데이트하여 재큐에 관한 정보를 포함하세요.
중복 열에 대한 일괄 처리
기본 일괄 처리 전략은 기본 키 열을 반복하는 효율적인 방법을 제공합니다. 그러나 값이 고유하지 않은 열을 반복해야 하는 경우 다른 일괄 처리 전략을 사용해야 합니다.
LooseIndexScanBatchingStrategy
일괄 처리 전략은 고유한 컬럼 값을 효율적이고 안정적으로 반복하는 특별한 버전의 EachBatch
를 사용합니다.
다음은 issues.project_id
열을 배치 열로 사용하는 일괄 백그라운드 마이그레이션의 예입니다.
데이터베이스 후 마이그레이션:
class ProjectsWithIssuesMigration < Gitlab::Database::Migration[2.1]
MIGRATION = 'BatchProjectsWithIssues'
INTERVAL = 2.minutes
BATCH_SIZE = 5000
SUB_BATCH_SIZE = 500
restrict_gitlab_migration gitlab_schema: :gitlab_main
disable_ddl_transaction!
def up
queue_batched_background_migration(
MIGRATION,
:issues,
:project_id,
job_interval: INTERVAL,
batch_size: BATCH_SIZE,
batch_class_name: 'LooseIndexScanBatchingStrategy', # 기본 일괄 처리 전략을 재정의
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(MIGRATION, :issues, :project_id, [])
end
end
배경 마이그레이션 클래스 구현:
module Gitlab
module BackgroundMigration
class BatchProjectsWithIssues < Gitlab::BackgroundMigration::BatchedMigrationJob
include Gitlab::Database::DynamicModelHelpers
operation_name :backfill_issues
def perform
distinct_each_batch do |batch|
project_ids = batch.pluck(batch_column)
# 고유한 project_ids로 작업 수행
end
end
end
end
end
배치된 백그라운드 마이그레이션의 전체 시간 추정 계산
BBM이 완료되기까지 얼마나 걸리는지 추정할 수 있습니다. GitLab은 이미 db:gitlabcom-database-testing
파이프라인을 통해 추정치를 제공합니다.
이 추정치는 테스트 환경에서 프로덕션 데이터를 샘플링하여 구축되었으며 마이그레이션이 소요될 수 있는 최대 시간을 나타내는 것입니다. 실제 마이그레이션이 소요되는 실제 시간이 아닐 수 있습니다. 특정 시나리오에서는 db:gitlabcom-database-testing
파이프라인에서 제공하는 추정치만으로는 마이그레이션되는 레코드 주변의 모든 특이 사항을 계산하는 데 충분하지 않을 수 있으며 추가적인 계산이 필요할 수 있습니다. 이를 필요로 하는 경우, interval * number of records / max batch size
공식을 사용하여 마이그레이션이 소요되는 대략적인 추정을 결정하는 데 사용할 수 있습니다.
여기서 interval
및 max batch size
는 작업에 정의된 옵션을 나타내며 total tuple count
는 마이그레이션되어야 하는 레코드 수입니다.
배치된 백그라운드 마이그레이션의 정리
백그라운드 마이그레이션이 오랜 시간이 걸릴 수 있기 때문에, 바로 정리할 수 없습니다. 예를 들어, 마이그레이션 프로세스에서 사용되는 열을 삭제할 수 없으며, 그렇게 하면 작업이 실패합니다. 남은 작업을 정리하기 전에 미래 릴리스에서 별도의 포스트-배포 마이그레이션을 추가하여 남은 모든 작업을 완료해야 합니다. 1. Release A: 1. 주어진 ID를 가진 행에 대한 마이그레이션을 수행하는 마이그레이션 클래스를 생성합니다. 1. 다음 기술 중 하나를 사용하여 새로운 행을 업데이트합니다: - 응용 프로그램 로직이 필요없는 복사 작업을 위한 새로운 트리거를 생성합니다. - 레코드가 생성되거나 업데이트될 때 모델/서비스에서 이 작업을 처리합니다. - 레코드를 업데이트하는 새로운 사용자 정의 백그라운드 작업을 생성합니다. 1. 모든 기존 행에 대한 배치된 백그라운드 마이그레이션을 포스트-배포 마이그레이션으로 대기열에 넣습니다. 1. Release B: 1. 배치된 백그라운드 마이그레이션이 완료되었는지 확인하는 포스트-밸뮈 마이그레이션을 추가합니다. 1. 응용프로그램이 새 열을 사용하도록 코드를 배포하고, 새 레코드를 업데이트하지 않도록합니다. 1. 이전 열을 제거합니다.
import/export version을 업데이트하는 것이 필요할 수 있습니다. 왜냐하면 이전 버전의 GitLab에서 프로젝트를 가져오려면 데이터를 새로운 형식에 맞춰야 할 수 있기 때문입니다.
배치된 백그라운드 마이그레이션을 지원하기 위해 인덱스 추가
때로는 배치된 백그라운드 마이그레이션을 지원하기 위해 새로운 또는 임시 인덱스를 추가해야 할 때가 있습니다. 이를 위해 배포 후 마이그레이션에서 인덱스를 작성하십시오. 이렇게 하면 배포 후 백그라운드 마이그레이션을 대기열에 넣을 수 있습니다.
데이터베이스 인덱스 추가에 대한 문서에서 새 인덱스를 바로 사용할 수 있게 하는 데 특별한 주의가 필요한 경우에 대한 추가 정보를 참조하십시오.
데이터베이스 테스트 파이프라인에서 특정 배치 실행
GitLab.com의 특정 배치에서 배치된 백그라운드 마이그레이션이 실패하고 어떤 쿼리가 실패했는지 이유를 알고 싶은 경우가 있다고 가정해 보겠습니다. 현재 쿼리 정보(특히 쿼리 매개변수)를 검색하는 좋은 방법이 없으며, 더 많은 로깅을 추가하여 전체 마이그레이션을 다시 실행하는 것은 시간이 오래 걸릴 수 있습니다.
다행스럽게도 데이터베이스 마이그레이션 파이프라인을 활용하여 문제를 해결할 수 있습니다.
예를 들어 Draft: Test PG::CardinalityViolation
fix을 참조하되 전체 섹션을 읽는 것이 중요합니다.
이를 위해 다음을 수행해야 합니다:
배치 시작 ID 및 종료 ID 찾기
Kibana에서 해당 ID를 찾을 수 있어야 합니다.
일반 마이그레이션 생성하기
일반 마이그레이션의 up
블록에서 해당 배치를 예약하십시오:
def up
instance = Gitlab::BackgroundMigration::YourBackgroundMigrationClass.new(
start_id: <배치 시작 ID>,
end_id: <배치 종료 ID>,
batch_table: <테이블 이름>,
batch_column: <배치 열>,
sub_batch_size: <서브 배치 크기>,
pause_ms: <배치 간 밀리초>,
job_arguments: <작업 인수(있는 경우)>,
connection: connection
)
instance.perform
end
def down
# no-op
end
마이그레이션 헬퍼에 대한 워크어라운드 적용 (선택 사항)
배치된 백그라운드 마이그레이션이 restrict_gitlab_migration
도우미를 사용하여 지정한 것이 아닌 다른 스키마의 테이블을 사용하는 경우(예: 스케줄링 마이그레이션이 restrict_gitlab_migration gitlab_schema: :gitlab_main
을 가지고 있지만 백그라운드 작업은 :gitlab_ci
스키마의 테이블을 사용하는 경우) 마이그레이션이 실패합니다. 이를 방지하기 위해 데이터베이스 헬퍼를 수정하여 테스팅 파이프라인 작업이 실패하지 않도록 해야 합니다:
-
RestrictGitlabSchema
에 스키마 이름 추가
diff --git a/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb b/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
index b8d1d21a0d2d2a23d9e8c8a0a17db98ed1ed40b7..912e20659a6919f771045178c66828563cb5a4a1 100644
--- a/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
+++ b/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
@@ -55,7 +55,7 @@ def unmatched_schemas
end
def allowed_schemas_for_connection
- Gitlab::Database.gitlab_schemas_for_connection(connection)
+ Gitlab::Database.gitlab_schemas_for_connection(connection) << :gitlab_ci
end
end
end
-
RestrictAllowedSchemas
에 스키마 이름 추가
diff --git a/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb b/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
index 4ae3622479f0800c0553959e132143ec9051898e..d556ec7f55adae9d46a56665ce02de782cb09f2d 100644
--- a/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
+++ b/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
@@ -79,7 +79,7 @@ def restrict_to_dml_only(parsed)
tables = self.dml_tables(parsed)
schemas = self.dml_schemas(tables)
- if (schemas - self.allowed_gitlab_schemas).any?
+ if (schemas - (self.allowed_gitlab_schemas << :gitlab_ci)).any?
raise DMLAccessDeniedError, \
"Select/DML queries (SELECT/UPDATE/DELETE) do access '#{tables}' (#{schemas.to_a}) " \
"which is outside of list of allowed schemas: '#{self.allowed_gitlab_schemas}'. " \
데이터베이스 마이그레이션 파이프라인 시작하기
변경 사항으로 Draft Merge Request을 만들고 매뉴얼으로 db:gitlabcom-database-testing
작업을 트리거합니다.
의존성 설정
일부 경우에는 마이그레이션은 이전에 대기 중인 BBM의 완료를 의존합니다. BBM이 계속 실행 중인 경우, 종속 마이그레이션이 실패합니다. 예: 큰 테이블에 고유한 인덱스를 도입하는 것은 이전에 대기 중인 BBM에 의해 중복 레코드가 처리되기를 의존할 수 있습니다.
다음 프로세스는 의존성을 더 명확하게 만들기 위해 마이그레이션을 작성하는 동안 구성되었습니다.
- BBM을 대기열에 넣은 마이그레이션의 버전은 batched_background_migrations 테이블과 BBM 사전 파일에 저장됩니다.
-
DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS
상수는 각 마이그레이션 파일에 추가됩니다(기본적으로 주석 처리됨). 의존성을 설정하려면 종속 BBM의queued_migration_version
을 추가하세요. 그렇지 않으면 주석 처리된 행을 제거하세요. -
Migration::UnfinishedDependencies
cop은 종속 BBM이 아직 완료되지 않았는지 확인합니다. 이는 BBM 사전에서finalized_by
키를 찾아서 결정합니다.
예시:
# db/post_migrate/20231113120650_queue_backfill_routes_namespace_id.rb
class QueueBackfillRoutesNamespaceId < Gitlab::Database::Migration[2.1]
MIGRATION = 'BackfillRouteNamespaceId'
restrict_gitlab_migration gitlab_schema: :gitlab_main
...
...
def up
queue_batched_background_migration(
MIGRATION,
...
)
end
end
# 이 것은 QueueBackfillRoutesNamespaceId BBM의 완료에 의존합니다
class AddNotNullToRoutesNamespaceId < Gitlab::Database::Migration[2.1]
DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = ["20231113120650"]
def up
add_not_null_constraint :routes, :namespace_id
end
def down
remove_not_null_constraint :routes, :namespace_id
end
end
주의사항
-
BackgroundMigration::DictionaryFile
cop은 BBM 사전에finalize_after
와introduced_by_url
키의 존재 여부를 보장합니다.-
finalize_after
: BBM이 완료될 것으로 예상되는 (대략적인) 날짜를 기록합니다. -
introduced_by_url
:finalize_after
날짜 이후에introduced_by_url
의 라벨과 작성자를 사용하여 이슈가 생성됩니다.- 작성 시점(2023-08-11), 이슈 #424886가 아직 열려 있습니다.
-
관리
chatops
통합을 통해 진행됩니다.대기 중인 배치 배경 마이그레이션 디렉터리
시스템의 대기 중인 배치 배경 마이그레이션을 나열하려면 다음 명령을 실행하세요:
/chatops run batched_background_migrations list
이 명령은 다음 옵션을 지원합니다:
- 데이터베이스 선택:
-
--database DATABASE_NAME
: 주어진 데이터베이스에 연결합니다:-
main
: 기본적으로 main 데이터베이스를 사용합니다. -
ci
: CI 데이터베이스를 사용합니다.
-
-
- 환경 선택:
-
--dev
:dev
환경을 사용합니다. -
--staging
:staging
환경을 사용합니다. -
--staging_ref
:staging_ref
환경을 사용합니다. -
--production
:production
환경을 사용합니다 (기본값).
-
출력 예시:
created_at
에 따라 20개의 배치 배경 마이그레이션을 반환합니다.배치 배경 마이그레이션의 진행과 상태 모니터링
특정 배치 배경 마이그레이션의 상태와 진행 상황을 확인하려면 다음 명령을 실행하세요:
/chatops run batched_background_migrations status MIGRATION_ID
이 명령은 다음 옵션을 지원합니다:
- 데이터베이스 선택:
-
--database DATABASE_NAME
: 주어진 데이터베이스에 연결합니다:-
main
: 기본적으로 main 데이터베이스를 사용합니다. -
ci
: CI 데이터베이스를 사용합니다.
-
-
- 환경 선택:
-
--dev
:dev
환경을 사용합니다. -
--staging
:staging
환경을 사용합니다. -
--staging_ref
:staging_ref
환경을 사용합니다. -
--production
:production
환경을 사용합니다 (기본값).
-
출력 예시:
진행
은 완료된 배경 마이그레이션의 백분율을 나타냅니다.
배치 배경 마이그레이션 상태의 정의:
-
활성: 다음과 같은 상태:
- 러너가 가져가기 준비됨.
- 배치 작업을 실행 중.
- 완료 중: 배치 작업을 실행 중.
- 실패: 배치 배경 마이그레이션 실패.
- 완료: 배치 배경 마이그레이션이 완료됨.
- 정지됨: 러너에서 보이지 않음.
배치 배경 마이그레이션 일시 정지
배치 배경 마이그레이션을 일시 정지하려면 다음 명령을 실행해야 합니다:
/chatops run batched_background_migrations pause MIGRATION_ID
이 명령은 다음 옵션을 지원합니다:
- 데이터베이스 선택:
-
--database DATABASE_NAME
: 주어진 데이터베이스에 연결합니다:-
main
: 기본적으로 main 데이터베이스를 사용합니다. -
ci
: CI 데이터베이스를 사용합니다.
-
-
- 환경 선택:
-
--dev
:dev
환경을 사용합니다. -
--staging
:staging
환경을 사용합니다. -
--staging_ref
:staging_ref
환경을 사용합니다. -
--production
:production
환경을 사용합니다 (기본값).
-
출력 예시:
배치 배경 마이그레이션 다시 시작
배치 배경 마이그레이션을 다시 시작하려면 다음 명령을 실행해야 합니다:
/chatops run batched_background_migrations resume MIGRATION_ID
이 명령은 다음 옵션을 지원합니다:
- 데이터베이스 선택:
-
--database DATABASE_NAME
: 주어진 데이터베이스에 연결합니다:-
main
: 기본적으로 main 데이터베이스를 사용합니다. -
ci
: CI 데이터베이스를 사용합니다.
-
-
- 환경 선택:
-
--dev
:dev
환경을 사용합니다. -
--staging
:staging
환경을 사용합니다. -
--staging_ref
:staging_ref
환경을 사용합니다. -
--production
:production
환경을 사용합니다 (기본값).
-
출력 예시:
배경 마이그레이션 활성화 또는 비활성화
극히 제한된 상황에서 GitLab 관리자는 다음 중 하나 또는 모두의 피처 플래그를 비활성화할 수 있습니다.:
execute_background_migrations
execute_batched_migrations_on_schedule
이러한 플래그는 기본적으로 활성화되어 있습니다. 데이터베이스 호스트 유지 관리와 같은 특별한 상황에서만 데이터베이스 작업을 제한하기 위해 마지막 수단으로만 비활성화하세요.
execute_background_migrations
또는 execute_batched_migrations_on_schedule
피처 플래그 중 하나라도 비활성화하지 마십시오. 무엇을 제대로 이해하지 않은 채로 이러한 플래그를 비활성화하면 GitLab 업그레이드가 실패할 수 있으며 데이터 손실이 발생할 수 있습니다.EE 전용 기능을 위한 일괄 배경 마이그레이션
EE 전용 기능을 위한 모든 백그라운드 마이그레이션 클래스는 GitLab FOSS에 있어야 합니다. 이를 위해 GitLab FOSS용 빈 클래스를 만들고, Enterprise Edition 기능 구현 가이드에 설명된 대로 GitLab EE를 위해 확장하세요.
일괄 백그라운드 마이그레이션 생성하기를 이용하여 새로운 일괄 배경 마이그레이션 생성 시 --ee-only
플래그를 전달하여 EE 전용 마이그레이션 스캐폴드를 생성할 수 있습니다.
디버그
실패 오류 로그 보기
두 가지 방법으로 실패를 확인할 수 있습니다:
- GitLab 로그를 통해:
- 일괄 배경 마이그레이션을 실행한 후 작업에 실패한 경우, Kibana에서 로그를 확인하세요.
json.new_state: failed
json.job_class_name: <Batched Background Migration job class name>
-
json.job_arguments: <Batched Background Migration job class arguments>
를 필터링하세요.
-
json.exception_class
및json.exception_message
값을 검토하여 작업 실패 이유를 이해하는 데 도움을 받으세요. - 재시도 메커니즘을 기억하세요. 실패가 있더라도 작업이 실패했음을 의미하지는 않습니다. 항상 작업의 마지막 상태를 확인하세요.
- 일괄 배경 마이그레이션을 실행한 후 작업에 실패한 경우, Kibana에서 로그를 확인하세요.
- 데이터베이스에서:
- 일괄 배경 마이그레이션
CLASS_NAME
을 가져옵니다. -
PostgreSQL 콘솔에서 다음 쿼리를 실행하세요:
SELECT migration.id, migration.job_class_name, transition_logs.exception_class, transition_logs.exception_message FROM batched_background_migrations as migration INNER JOIN batched_background_migration_jobs as jobs ON jobs.batched_background_migration_id = migration.id INNER JOIN batched_background_migration_job_transition_logs as transition_logs ON transition_logs.batched_background_migration_job_id = jobs.id WHERE transition_logs.next_status = '2' AND migration.job_class_name = "CLASS_NAME";
- 일괄 배경 마이그레이션
테스트
다음에 대해 테스트를 작성해야 합니다:
- 일괄 백그라운드 마이그레이션 대기열 마이그레이션.
- 일괄 백그라운드 마이그레이션 자체.
- 정리 마이그레이션.
배경 마이그레이션 스펙에 대해 :migration
및 schema: :latest
RSpec 태그가 자동으로 설정됩니다. Rails 마이그레이션 테스트하기 스타일 가이드를 참조하세요.
before
및 after
RSpec 훅이 데이터베이스를 다운 및 업 마이그레이션하므로 주의하세요. 이러한 훅은 다른 일괄 백그라운드 마이그레이션이 호출될 수 있습니다. 귀하가 it
블록에서 정의한 예상과 충돌할 수 있으므로 spy
테스트 더블 및 have_received
를 사용하는 것이 권장됩니다. 이에 대해서는 issue #35351를 참조하세요.
모범 사례
- 다루고 있는 데이터 양을 파악하세요.
- 일괄 백그라운드 마이그레이션 작업이 멱등해야 합니다.
- 작성한 테스트가 거짓 긍정이 아닌지 확인하세요.
- 마이그레이션 중요하고 손실이 발생해서는 안 되는 데이터라면, 정리 마이그레이션이 완료되기 전에 반드시 데이터의 최종 상태를 확인해야 합니다.
- 데이터베이스 전문가와 숫자를 상의하세요. 마이그레이션이 예상보다 데이터베이스에 더 큰 압력을 가할 수 있습니다. 스테이징에서 메트릭하거나, 누군가에게는 프로덕션에서 메트릭할 것을 요청하세요.
- 일괄 백그라운드 마이그레이션을 실행하는 데 필요한 시간을 알아둘 것을 권장합니다.
-
작업 클래스 내에서 무시하여 발생한 예외에 대해 조심하세요. 실패 시에도 작업이 성공으로 표시될 수 있습니다.
# good def perform each_sub_batch do |sub_batch| sub_batch.update_all(name: 'My Name') end
예시
라우트 사용 사례
routes
테이블에는 다형적 관계에 사용되는 source_type
필드가 있습니다.
데이터베이스 재설계의 일환으로 다형적 관계를 제거하고 source_id
열의 데이터를 새로운 단수 외래 키로 마이그레이션하는 작업 일부입니다. 이전의 행을 나중에 삭제할 예정이므로 배경 마이그레이션의 일부로 업데이트할 필요가 없습니다.
-
생성기를 사용하여 다음과 같이 일괄 백그라운드 마이그레이션 파일을 생성하세요:
bundle exec rails g batched_background_migration BackfillRouteNamespaceId --table_name=routes --column_name=id --feature_category=source_code_management
-
마이그레이션 작업(
BatchedMigrationJob
의 하위 클래스)을 업데이트하여source_id
값을namespace_id
로 복사하세요:class Gitlab::BackgroundMigration::BackfillRouteNamespaceId < BatchedMigrationJob # 설명을 위해 로컬 모델을 사용할 경우, 아래와 같이 `ApplicationRecord`를 기본 클래스로 사용하여 정의할 수 있습니다. # class Route < ::ApplicationRecord # self.table_name = 'routes' # end operation_name :update_all feature_category :source_code_management def perform each_sub_batch( batching_scope: -> (relation) { relation.where("source_type <> 'UnusedType'") } ) do |sub_batch| sub_batch.update_all('namespace_id = source_id') end end end
작업 클래스는 일괄 마이그레이션 프레임워크에 의해 올바르게 처리되도록BatchedMigrationJob
를 상속받습니다.BatchedMigrationJob
의 어떤 하위 클래스든지 일괄 배치를 수행하고 추적 데이터베이스에 대한 연결이 실행될 수 있도록 필요한 인수로 초기화됩니다. -
데이터베이스에 새로운 트리거를 추가하는 데이터베이스 마이그레이션을 생성하세요.
-
작성된 배포 후 마이그레이션을 다음과 같이 지연 시간 및 배치 크기로 업데이트하세요:
class QueueBackfillRoutesNamespaceId < Gitlab::Database::Migration[2.1] MIGRATION = 'BackfillRouteNamespaceId' DELAY_INTERVAL = 2.minutes BATCH_SIZE = 1000 SUB_BATCH_SIZE = 100 restrict_gitlab_migration gitlab_schema: :gitlab_main def up queue_batched_background_migration( MIGRATION, :routes, :id, job_interval: DELAY_INTERVAL, batch_size: BATCH_SIZE, sub_batch_size: SUB_BATCH_SIZE ) end def down delete_batched_background_migration(MIGRATION, :routes, :id, []) end
# db/docs/batched_background_migrations/backfill_route_namespace_id.yml --- migration_job_name: BackfillRouteNamespaceId description: routes에서 source_id 값을 namespace_id로 복사합니다. feature_category: source_code_management introduced_by_url: "https://mr_url" milestone: 16.6 queued_migration_version: 20231113120650 finalize_after: "2023-11-15" finalized_by: # 이 마이그레이션의 버전
일괄 백그라운드 마이그레이션을 대기열에 넣을 때 실제 변경 사항을 수행하는 데이터베이스 스키마를 제한해야 합니다. 우리는 레코드를 업데이트하기 때문에routes
레코드를 업데이트합니다, 그래서restrict_gitlab_migration gitlab_schema: :gitlab_main
으로 설정합니다. 그러나 CI 데이터 마이그레이션을 수행해야 하는 경우에는restrict_gitlab_migration gitlab_schema: :gitlab_ci
를 설정해야 합니다.배포 후, 우리의 애플리케이션: - 계속해서 데이터를 이전처럼 사용합니다. - 기존 및 새 데이터가 모두 마이그레이션된 것을 보장합니다.
-
일괄 배경 마이그레이션이 완료될 때까지 일괄 배경 마이그레이션이 완료되었는지 확인하는 새로운 배포 후 마이그레이션을 추가합니다. 또한 BBM 사전에서
finalized_by
속성을 이 마이그레이션 버전으로 업데이트하세요.class FinalizeBackfillRouteNamespaceId < Gitlab::Database::Migration[2.1] MIGRATION = 'BackfillRouteNamespaceId' disable_ddl_transaction! restrict_gitlab_migration gitlab_schema: :gitlab_main def up ensure_batched_background_migration_is_finished( job_class_name: MIGRATION, table_name: :routes, column_name: :id, job_arguments: [], finalize: true ) end def down # no-op end
# db/docs/batched_background_migrations/backfill_route_namespace_id.yml --- migration_job_name: BackfillRouteNamespaceId description: routes에서 source_id 값을 namespace_id로 복사합니다. feature_category: source_code_management introduced_by_url: "https://mr_url" milestone: 16.6 queued_migration_version: 20231113120650 finalize_after: "2023-11-15" finalized_by: 20231115120912
배권 백그라운드 마이그레이션이 완료되지 않은 경우 시스템은 일괄 백그라운드 마이그레이션을 인라인으로 실행합니다. 이 동작을 보고 싶지 않은 경우,finalize: false
를 전달해야 합니다.응용 프로그램이 데이터가 100% 마이그레이션이 된 것에 의존하지 않는 경우(예를 들어, 데이터가 자문적이고 중요하지 않은 경우), 이 마지막 단계를 건너뛸 수 있습니다. 이 단계는 마이그레이션이 완료되었는지 확인하고 모든 행이 마이그레이션된 것을 확인합니다.
-
트리거를 제거하기 위해 데이터베이스 마이그레이션을 추가합니다.
class RemoveNamepaceIdTriggerFromRoutes < Gitlab::Database::Migration[2.1] FUNCTION_NAME = 'example_function' TRIGGER_NAME = 'example_trigger' def up drop_trigger(TRIGGER_NAME, :routes) drop_function(FUNCTION_NAME) end def down # 트리거 및 함수를 추가한 마이그레이션의 up 메서드에서 역으로 해야 합니다 end
일괄 마이그레이션이 완료되면, 안전하게 routes.namespace_id
의 데이터에 의존할 수 있습니다.