- 일괄 배경 이동 사용 시기
- 일괄 배경 이동 작동 방식
- 방법
- 관리
- EE 전용 기능용 일괄 배경 마이그레이션
- 디버그
- 테스팅
- Best practices
- 예시
일괄 배경 이동
일괄 배경 이동은 다음과 같은 경우에 데이터 마이그레이션을 수행하는 데 사용해야 합니다. 가이드라인의 시간 제한을 초과하는 마이그레이션이 발생할 때 사용해야 합니다. 예를 들어, 단일 JSON 열에 저장된 데이터를 별도의 테이블로 마이그레이션하는 경우 일괄 배경 이동을 사용할 수 있습니다.
참고: 일괄 배경 이동은 레거시 백그라운드 이동 프레임워크를 대체했습니다. 이 프레임워크와 관련된 변경 사항은 해당 문서를 확인하세요.
참고: 일괄 배경 이동 프레임워크에는 ChatOps 지원이 있습니다. ChatOps를 사용하면 GitLab 엔지니어는 시스템에 있는 일괄 배경 이동과 상호 작용할 수 있습니다.
일괄 배경 이동 사용 시기
일괄 배경 이동을 사용하여 테이블 내에 있는 데이터를 마이그레이션할 때, 일반적인 Rails 마이그레이션을 사용하면 가이드라인의 시간 제한을 초과하는 경우에 사용합니다.
- 고트래픽 테이블에서 데이터를 마이그레이션해야 할 때에는 일괄 배경 이동을 사용해야 합니다.
- 대규모 데이터세트에서 모든 항목에 대해 많은 단일 행 쿼리를 실행해야 할 때에도 일괄 배경 이동을 사용할 수 있습니다. 일반적으로 단일 레코드 패턴에 대해 런타임은 데이터세트의 크기에 크게 의존합니다. 데이터세트를 분할하고 배경 이동에 넣습니다.
- 일괄 배경 이동은 스키마 마이그레이션을 수행하는 데 사용하지 마세요.
백그라운드 마이그레이션은 다음 경우에 도움이 됩니다.
- 이벤트를 한 테이블에서 여러 개의 별도 테이블로 마이그레이션하는 경우.
- 다른 열을 JSON로 저장한 내용을 기반으로 열을 채우는 경우.
- 외부 서비스의 출력에 따라 데이터를 마이그레이션하는 경우. (예: API)
참고
- 일괄 배경 이동이 중요한 업그레이드의 일부인 경우 릴리스 게시물에 발표되어야 합니다. 마이그레이션이 해당 범주에 속하는지 확실하지 않은 경우 프로젝트 매니저와 상의하세요.
- 필요한 파일이 기본적으로 생성되도록 일괄 배경 이동을 만들려면 생성기를 사용해야 합니다.
일괄 배경 이동 작동 방식
일괄 배경 이동(BBM)은 Gitlab::BackgroundMigration::BatchedMigrationJob
의 하위 클래스로, perform
메서드를 정의하는 것으로 정의됩니다. 일반 마이그레이션이 먼저 batched_background_migrations
레코드를 BBM 클래스와 필요한 매개변수로 생성합니다. 기본적으로 batched_background_migrations
는 활성 상태이며, Sidekiq 워커에 의해 실제 일괄 마이그레이션이 실행됩니다.
모든 마이그레이션 클래스는 Gitlab::BackgroundMigration
네임스페이스에 정의되어야 합니다. 파일은 lib/gitlab/background_migration/
디렉터리에 위치해야 합니다.
실행 메커니즘
일괄 배경 이동은 일괄로 대기열에서 가져오고 실행됩니다. 여러 마이그레이션이 동시에 가져와 병렬로 실행되며, 활성 상태이며 동일한 데이터베이스 테이블을 대상으로하지 않는 한이 실행 한도는 기본적으로 2이며, GitLab.com의 경우 이 한도는 4로 구성됩니다. 마이그레이션이 실행되면 특정 배치를 위해 작업이 생성됩니다. 각 작업 실행 후, 마지막 20개 작업의 성능에 따라 마이그레이션의 배치 크기를 증가 또는 감소시킬 수 있습니다.
작업자가 사용 가능해지자마자 BBM이 해당 작업자에 의해 처리됩니다.
동일성
일괄 배경 이동은 Sidekiq 프로세스의 맥락에서 실행됩니다. 일반적으로 Sidekiq 규칙이 적용되며 특히 작업은 작고 동일성을 보장해야 한다는 규칙이 적용됩니다. 마이그레이션 작업이 재시도되는 경우 데이터 무결성이 보장되도록하십시오.
자세한 내용은 Sidekiq best practices guidelines를 참조하세요.
마이그레이션 최적화
각 작업 실행 후, 마이그레이션이 최적화될 수 있는지를 확인하는 검증이 진행됩니다. 최적화의 기본적인 메커니즘은 시간 효율성 개념에 기반합니다. 마이그레이션의 마지막 N개 작업의 시간 효율성의 지수 이동 평균을 계산하고 일괄 배경 마이그레이션의 배치 크기를 최적 값으로 업데이트합니다.
GitLab SAAS의 경우
대규모 데이터세트를 업데이트할 때 GitLab SAAS에 대해 서로 다른 일괄 크기를 지정합니다.
# frozen_string_literal: true
class BatchedMigration < Gitlab::Database::Migration[2.2]
BATCH_SIZE = 1000
SUB_BATCH_SIZE = 100
GITLAB_OPTIMIZED_BATCH_SIZE = 75_000
GITLAB_OPTIMIZED_SUB_BATCH_SIZE = 250
def up
queue_batched_background_migration(
MIGRATION,
TABLE_NAME,
COLUMN_NAME,
job_interval: DELAY_INTERVAL,
**batch_sizes
)
end
private
def batch_sizes
if Gitlab.com_except_jh?
{
batch_size: GITLAB_OPTIMIZED_BATCH_SIZE,
sub_batch_size: GITLAB_OPTIMIZED_SUB_BATCH_SIZE
}
else
{
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
}
end
end
end
잡 재시도 메커니즘
일괄 배경 마이그레이션 재시도 메커니즘은 작업이 실패한 경우 작업이 다시 실행되도록 보장합니다. 다음 다이어그램은 재시도 메커니즘의 다양한 단계를 보여줍니다.
-
MAX_ATTEMPTS
는Gitlab::Database::BackgroundMigration
클래스에서 정의됩니다. -
can_split?
은Gitlab::Database::BatchedJob
클래스에서 정의됩니다.
실패한 일괄 배경 마이그레이션
일괄 배경 마이그레이션의 전체가 failed
로 표시됩니다
(/chatops run batched_background_migrations status MIGRATION_ID
를 통해
마이그레이션이 failed
로 나타납니다) 다음 중 하나라도 해당하는 경우:
- 소비할 작업이 더 이상 없고, 실패한 작업이 있는 경우.
- 배경 마이그레이션이 시작된 이후 작업의 절반 이상이 실패한 경우 (링크).
일괄 마이그레이션 쓰로틀링
일괄 마이그레이션은 업데이트가 많으며 데이터베이스 성능이 떨어지는 동안 중복 쓰로틀링으로 인한 사건이 있었기 때문에 이슈를 완화하기 위해 쓰로틀링 메커니즘이 존재합니다.
이 데이터베이스 지표는 마이그레이션을 조절하는 데 사용됩니다. 중단 신호를 받으면 일정 시간(10분) 동안 마이그레이션을 일시 중지합니다.
- 임계값을 넘는 WAL 큐 대기 아카이브
- 마이그레이션이 작동하는 테이블에서 활성 자동 진공
- Patroni apdex SLI가 SLO를 넘지 않도록
- 임계값을 넘는 WAL 속도
더 많은 지표를 추가하여 데이터베이스 건강 검사 프레임워크를 더욱 향상시키는 작업이 진행 중입니다. 자세한 내용은 epic 7594을 참조하십시오.
격리
일괄 배경 마이그레이션은 격리되어야 하며 응용 프로그램 코드를 사용해서는 안 됩니다(예: app/models
에 정의된 모델을 제외한 ApplicationRecord
클래스).
이러한 마이그레이션은 실행에 오랜 시간이 걸릴 수 있기 때문에 새 버전이 배포될 수도 있습니다.
마이그레이션된 데이터에 따름
일반적인 마이그레이션이나 포스트 마이그레이션과 달리 데이터가 완전히 마이그레이션되었음을 보장하기 위해 다음 릴리스를 기다리는 것만으로 충분하지 않습니다.
즉, BBM이 완료될 때까지 데이터에 의존해서는 안 됩니다. 데이터의 100%가 마이그레이션되는 것이 요구 사항이라면, ensure_batched_background_migration_is_finished
도우미를 사용하여 마이그레이션이 완료되고
데이터가 완전히 마이그레이션된 것을 보장할 수 있습니다. (예시 확인).
방법
일괄 배경 마이그레이션 생성
사용자 정의 생성자 batched_background_migration
은 필요한 파일을 뼈대로 만들고
table_name
, column_name
, feature_category
를 인수로 받습니다. column_name
을 선택할 때는 구별할 수 있는 열 유형을 사용하는 것이 좋으며, 이상적으로는 테이블의 주 키를 사용합니다. 여기에서 정의된 열을 기반으로 테이블이 반복됩니다.
자세한 정보는 Batch over non-distinct columns을 참조하십시오.
사용법:
bundle exec rails g batched_background_migration my_batched_migration --table_name=<table-name> --column_name=<column-name> --feature_category=<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
예제를 사용하여 마이그레이션을 일괄적으로 실행하도록 대기열에 넣습니다. 클래스 이름과 인수를 마이그레이션에서 사용하는 값으로 바꾸십시오.
ruby
queue_batched_background_migration(
JOB_CLASS_NAME,
TABLE_NAME,
JOB_ARGUMENTS,
JOB_INTERVAL
)
참고:
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
로 변경되었더라도 gitlab_main
으로 완료해야 합니다.
일괄 배경 마이그레이션을 완료하면 해당하는 db/docs/batched_background_migrations
파일의 finalized_by
를 업데이트해야 합니다. 해당 값은 마이그레이션의 타임스탬프/버전이어야 합니다.
구체적인 마이그레이션 코드에 대한 자세한 내용은 아래의 Examples를 참조하십시오.
작업 인수 사용
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
참고:
추가적인 필터를 적용하는 경우, 적절한 인덱스로 옵티마이즈 작업이 되도록 확인하는 것이 중요합니다. 위 예제에서는 (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
메서드의 내용을 무시하세요. 그렇지 않으면 배포 중인 시스템에서 여러 패치 릴리스를 업그레이드하는 경우 배치된 백그라운드 마이그레이션이 만들어지고 삭제된 후에 다시 만들어집니다. - 배치된 백그라운드 마이그레이션을 다시 실행하는 새로운 배포 후 마이그레이션을 추가하세요.
- 새로운 배포 후 마이그레이션에서는 기존의 배치된 백그라운드 마이그레이션을 삭제하기 위해
delete_batched_background_migration
메서드를#up
메서드의 시작 부분에서 사용하여 기존 실행을 정리할 수 있도록 합니다. - 원래 마이그레이션의
db/docs/batched_background_migration/*.yml
파일을 업데이트하여 재대기에 대한 정보를 포함하도록 합니다.
그 이외에 더 지원이 필요한 내용이 있는 경우에는 the EachBatch
documentation를 참조하세요.
# frozen_string_literal: true
class QueueResolveVulnerabilitiesForRemovedAnalyzers < Gitlab::Database::Migration[2.2]
milestone '17.3'
MIGRATION = "ResolveVulnerabilitiesForRemovedAnalyzers"
def up
# no-op because there was a bug in the original migration, which has been
# fixed by
end
def down
# no-op because there was a bug in the original migration, which has been
# fixed in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162527
end
end
배치된 마이그레이션 색인:
milestone
및 queued_migration_version
은 재큐된 마이그레이션(예: RequeueResolveVulnerabilitiesForRemovedAnalyzers)의 것이어야 합니다.
---
migration_job_name: ResolveVulnerabilitiesForRemovedAnalyzers
description: 제거된 분석기에 대한 모든 감청된 취약점을 해결합니다.
feature_category: static_application_security_testing
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162691
milestone: '17.4'
queued_migration_version: 20240814085540
finalized_by: #이 배치된 백그라운드 마이그레이션을 완료한 마이그레이션 버전
비대칭 열에 대한 배치 처리
기본 배치 전략은 기본 키 열을 효율적으로 반복하는 방법을 제공합니다. 그러나 고유하지 않은 값을 가진 열을 반복해야 하는 경우 다른 배치 전략을 사용해야 합니다.
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)
# 고유 프로젝트 ID로 작업 수행
end
end
end
end
end
참고:
[사용 필터](#use-filters)를 `LooseIndexScanBatchingStrategy` 및 `distinct_each_batch`에서 무시하고 있고 있습니다.
배치된 백그라운드 마이그레이션의 전체 시간 추정 계산
배치된 백그라운드 마이그레이션을 완료하는 데 얼마나 걸리는지 추정할 수 있습니다. GitLab은 이미 db:gitlabcom-database-testing
파이프라인을 통해 추정치를 제공합니다.
이 추정치는 테스트 환경에서 프로덕션 데이터를 샘플링하여 구축되며 마이그레이션이 소요될 수 있는 최대 시간을 나타내며 반드시 실제 마이그레이션이 소요되는 시간을 의미하는 것은 아닙니다. 특정 시나리오에서 db:gitlabcom-database-testing
파이프라인에서 제공하는 추정치만으로 이주르 계산하는 데 충분하지 않다면, 다음과 같이 사용할 수 있는 공식 interval * number of records / max batch size
에 따라 마이그레션을 완료하는 데 약간의 추정치를 결정할 수 있습니다. 여기서 interval
과 최대 배치 크기
는 작업을 정의하는 옵션을 가리키며 total tuple count
은 마이그레이션된 레코드 수를 나타냅니다.
참고: 마이그레이션 최적화 메커니즘에 의해 추정치가 영향을 받을 수 있습니다.
배치된 백그라운드 마이그레이션 정리
참고: 남아 있는 백그라운드 마이그레이션을 정리해야 하는 경우, 주요 또는 부수 릴리스를 통해 수행해야 합니다. 패치 릴리스에서 이 작업을 수행해서는 안됩니다.
백그라운드 마이그레이션이 오랜 시간이 소요되므로 즉시 정리할 수는 없습니다. 예를 들어, 마이그레이션 프로세스에서 사용되는 열을 삭제할 수 없다는 점 등이 해당됩니다. 남아 있는 모든 작업을 완료한 후 정리할 수 있도록 별도의 배포 후 마이그레이션을 추가해야 합니다. (예: 열을 제거).
열 foo
(큰 JSON 블롭을 포함)에서 열 bar
(문자열을 포함)로 데이터를 마이그레이션하는 경우 다음과 같이 수행합니다:
- A 릴리스:
- 주어진 ID를 가진 행에 대해 마이그레이션을 수행하는 마이그레이션 클래스를 생성합니다.
- 새 행을 업데이트하는 방법 중 하나를 사용하여 신규 행을 업데이트합니다:
- 응용 프로그램 로직이 필요하지 않은 복사 작업에 대한 새 트리거 생성
- 레코드가 생성 또는 업데이트될 때 모델/서비스에서 이 작업 처리
- 레코드를 업데이트하는 새로운 사용자 정의 백그라운드 작업 생성
- 모든 기존 행에 대해 배치된 백그라운드 마이그레이션을 대기열에 넣습니다.
- B 릴리스:
- 배치된 백그라운드 마이그레이션이 완료되었는지 확인하는 후배포 마이그레이션을 추가합니다.
- 응용프로그램이 새 열을 사용하기 시작하고 새 레코드를 업데이트하는 것을 중지시키기 위한 코드 배포를 수행합니다.
- 이전 열을 제거합니다.
가져오기/내보내기 버전을 올리는 것이 필요할 수 있으며, 이는 이전 버전의 GitLab에서 프로젝트 가져오기가 새 형식의 데이터를 요구하는 경우 해당됩니다. ```
배치된 백그라운드 마이그레이션을 지원하기 위한 인덱스 추가
가끔은 배치된 백그라운드 마이그레이션을 지원하기 위해 새로운 또는 임시 인덱스를 추가해야 할 때가 있습니다. 이를 위해 배경 마이그레이션을 큐에 넣는 사후 배포 마이그레이션의 전에 인덱스를 생성합니다.
인덱스가 생성된 직후에 사용할 수 있도록 몇 가지 특별한 주의가 필요한 경우에 대한 자세한 내용은 데이터베이스 인덱스 추가 문서를 참조하십시오.
데이터베이스 테스트 파이프라인에서 특정 배치 실행
참고: 데이터베이스 유지 관리자만 데이터베이스 테스트 파이프라인 아티팩트를 볼 수 있습니다. 이 방법을 사용해야 하는 경우 도움을 받기 위해 문의하십시오.
GitLab.com의 특정 배치에서 배치된 백그라운드 마이그레이션이 실패한 경우, 어떤 쿼리가 실패했는지와 그 이유를 확인하려고 합니다. 현재, 쿼리 정보(특히 쿼리 매개변수)를 검색하는 좋은 방법이 없으며 로깅을 더 많이 사용하여 전체 마이그레이션을 다시 실행하는 것은 시간이 오래 걸릴 수 있습니다.
다행히도 데이터베이스 마이그레이션 파이프라인을 활용하여 추가 로깅 및/또는 수정으로 특정 배치를 다시 실행하여 문제가 해결되는지 확인할 수 있습니다.
예를 들어 Draft: Test PG::CardinalityViolation
fix 를 참조하되 전체 섹션을 읽도록 하십시오.
이를 위해 다음을 수행해야 합니다.
배치 start_id
와 end_id
찾기
이를 Kibana에서 찾을 수 있어야 합니다.
일반 마이그레이션 생성
일반 마이그레이션의 up
블록에서 배치를 예약하십시오.
def up
instance = Gitlab::BackgroundMigration::YourBackgroundMigrationClass.new(
start_id: <batch start_id>,
end_id: <batch end_id>,
batch_table: <table name>,
batch_column: <batching column>,
sub_batch_size: <sub batch size>,
pause_ms: <miliseconds between batches>,
job_arguments: <job arguments if any>,
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}'. " \
데이터베이스 마이그레이션 파이프라인 시작
변경 사항이 있는 드래프트 합병 요청을 생성하고 수동으로 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
관리
참고:
BBM(배치된 백그라운드 마이그레이션) 관리는 GitLab 팀 멤버에게만 허용되는 chatops
통합을 통해 이루어집니다.
일괄 배경 마이그레이션 목록
시스템의 일괄 배경 마이그레이션 목록을 보려면 다음 명령을 실행하십시오:
/chatops run batched_background_migrations list
이 명령은 다음 옵션을 지원합니다:
- 데이터베이스 선택:
-
--database DATABASE_NAME
: 주어진 데이터베이스에 연결합니다:-
main
: 기본값인 main 데이터베이스를 사용합니다. -
ci
: CI 데이터베이스를 사용합니다.
-
-
- 환경 선택:
-
--dev
:dev
환경을 사용합니다. -
--staging
:staging
환경을 사용합니다. -
--staging_ref
:staging_ref
환경을 사용합니다. -
--production
: 기본값인production
환경을 사용합니다.
-
출력 예시:
참고:
ChatOps는 created_at
(DESC)에 따라 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
환경을 사용합니다.
-
출력 예시:
진행
은 완료된 배경 마이그레이션의 백분율을 나타냅니다.
일괄 배경 마이그레이션 상태의 정의:
-
Active(활성): 다음 중 하나:
- 실행을 위해 실행 대기 중인 상태.
- 일괄 작업 실행 중.
- Finalizing(종료 중): 일괄 작업 실행 중.
- Failed(실패): 일괄 배경 마이그레이션 실패.
- Finished(완료): 일괄 배경 마이그레이션이 완료됨.
- Paused(일시 정지): runner에 표시되지 않음.
일괄 배경 마이그레이션 일시 정지
일괄 배경 마이그레이션을 일시 정지하려면 다음 명령을 실행해야 합니다:
/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를 위해 빈 클래스를 만들고 GitLab EE를 위해 확장해야 합니다. 엔터프라이즈 에디션 기능 구현 가이드라인에 설명된 대로 이 작업을 수행하십시오.
참고: 작업 인수를 사용하는 EE 전용 기능용 배경 마이그레이션 클래스는 GitLab FOSS 클래스에 정의되어야 합니다. 정의는 GitLab FOSS 컨텍스트에서 마이그레이션이 예약되었을 때 작업 인수 유효성 검사에 실패하지 않도록 요구됩니다.
새로운 배경 마이그레이션 생성 시 --ee-only
플래그를 전달하여 EE 전용 마이그레이션 뼈대를 생성하려면 생성기를 사용할 수 있습니다.
디버그
실패 오류 로그 보기
두 가지 방법으로 실패를 볼 수 있습니다:
- GitLab 로그를 통해:
- 일괄 배경 마이그레이션을 실행한 후 작업이 실패하는 경우 Kibana에서 로그를 확인하십시오.
Production Sidekiq 로그를 확인하고 다음을 필터링하십시오:
json.new_state: failed
json.job_class_name: <일괄 배경 마이그레이션 작업 클래스 이름>
json.job_arguments: <일괄 배경 마이그레이션 작업 클래스 인수>
-
작업이 실패한 이유를 이해하는 데 도움이 되는
json.exception_class
및json.exception_message
값을 검토합니다. - 재시도 메커니즘을 기억하십시오. 실패가 발생했다고 해서 작업이 실패했다는 것은 아닙니다. 항상 작업의 마지막 상태를 확인하십시오.
- 일괄 배경 마이그레이션을 실행한 후 작업이 실패하는 경우 Kibana에서 로그를 확인하십시오.
Production Sidekiq 로그를 확인하고 다음을 필터링하십시오:
-
데이터베이스를 통해:
- 일괄 배경 마이그레이션
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 태그가 자동으로 설정됩니다. Testing Rails migrations 스타일 가이드를 참조하세요.
before
및 after
RSpec 훅은 데이터베이스를 다운 및 업 마이그레이션합니다. 이러한 훅은 다른 배치된 백그라운드 마이그레이션이 호출될 수 있습니다. RSpec 훅에서 정의된 기대치가 있으면 spy
테스트 더블을 사용하여 기대치를 정의해야 합니다. 이를 통해 it
블록에서 정의한 기대치가 RSpec 훅에서 호출되는 것과 충돌하지 않습니다. 자세한 내용은 issue #35351을 참조하세요.
Best practices
- 다루고 있는 데이터의 양을 파악하세요.
- 배치된 백그라운드 마이그레이션 작업이 멱등성을 가지도록 합니다.
- 작성한 테스트가 거짓 긍정이 되지 않도록 확인하세요.
- 마이그레이션되는 데이터가 중요하고 손실되어서는 안 되는 경우, 정리 마이그레이션은 완료하기 전에 데이터의 최종 상태를 확인해야 합니다.
- 숫자를 데이터베이스 전문가와 함께 검토하세요. 마이그레이션은 예상보다 데이터베이스에 더 많은 압력을 가할 수 있습니다. 스테이징에서 측정하거나, 프로덕션에서 측정할 수 있도록 누군가에게 요청하세요.
- 배치된 백그라운드 마이그레이션을 실행하는 데 필요한 시간을 파악하세요.
-
작업 클래스 내에서 조용히 예외를 대신 반환하는 경우 주의하세요. 이로 인해 작업이 실패하는 경우에도 작업이 성공으로 표시될 수 있습니다.
# 좋음 def perform each_sub_batch do |sub_batch| sub_batch.update_all(name: 'My Name') end end # 허용됨 def perform each_sub_batch do |sub_batch| sub_batch.update_all(name: 'My Name') rescue Exception => error logger.error(message: error.message, class: error.class) raise end end # 나쁨 def perform each_sub_batch do |sub_batch| sub_batch.update_all(name: 'My Name') rescue Exception => error logger.error(message: error.message, class: self.class.name) end 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 AddTriggerToRoutesToCopySourceIdToNamespaceId < Gitlab::Database::Migration[2.1] FUNCTION_NAME = 'example_function' TRIGGER_NAME = 'example_trigger' def up execute(<<~SQL) CREATE OR REPLACE FUNCTION #{FUNCTION_NAME}() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW."namespace_id" = NEW."source_id" RETURN NEW; END; $$; CREATE TRIGGER #{TRIGGER_NAME}() AFTER INSERT OR UPDATE ON routes FOR EACH ROW EXECUTE FUNCTION #{FUNCTION_NAME}(); SQL end def down drop_trigger(TRIGGER_NAME, :routes) drop_function(FUNCTION_NAME) end end
-
생성된 배포 후 마이그레이션을 지연 및 배치 크기로 업데이트하세요.
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 end
# db/docs/batched_background_migrations/backfill_route_namespace_id.yml --- migration_job_name: BackfillRouteNamespaceId description: Copies source_id values from routes to namespace_id feature_category: source_code_management introduced_by_url: "https://mr_url" milestone: 16.6 queued_migration_version: 20231113120650 finalized_by: # version of the migration that ensured this bbm
배치된 백그라운드 마이그레이션을 대기열에 넣을 때 실제 변경을 수행하는 데이터베이스의 스키마를 제한해야 합니다. 여기서는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 end
# db/docs/batched_background_migrations/backfill_route_namespace_id.yml --- migration_job_name: BackfillRouteNamespaceId description: Copies source_id values from routes to namespace_id feature_category: source_code_management introduced_by_url: "https://mr_url" milestone: 16.6 queued_migration_version: 20231113120650 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
의 데이터에 안전하게 의존할 수 있습니다.