배치 배경 마이그레이션

배치 배경 마이그레이션은 데이터 마이그레이션을 수행할 때, 마이그레이션이 우리 가이드라인의 시간 제한을 초과할 경우에 사용해야 합니다. 예를 들어, 배치 배경 마이그레이션을 사용하여 단일 JSON 열에 저장된 데이터를 별도의 테이블로 마이그레이션할 수 있습니다.

note
배치 배경 마이그레이션은 레거시 배경 마이그레이션 프레임워크를 대체했습니다. 해당 프레임워크와 관련된 변경 사항은 문서를 확인하세요.
note
배치 배경 마이그레이션 프레임워크는 ChatOps 지원이 가능합니다. ChatOps를 사용하면 GitLab 엔지니어가 시스템 내에 있는 배치 배경 마이그레이션과 상호작용할 수 있습니다.

배치 배경 마이그레이션을 사용할 때

테이블에 많은 행이 포함되어 있어 일반 Rails 마이그레이션을 사용할 경우 우리 가이드라인의 시간 제한을 초과하게 되는 _데이터_를 마이그레이션할 때 배치 배경 마이그레이션을 사용하세요.

  • 배치 배경 마이그레이션은 고트래픽 테이블의 데이터를 마이그레이션할 때 사용해야 합니다.

  • 대규모 데이터 세트의 모든 항목에 대해 수많은 단일 행 쿼리를 실행할 때도 배치 배경 마이그레이션을 사용할 수 있습니다. 일반적으로 단일 레코드 패턴의 경우 런타임은 데이터 세트의 크기에 크게 의존합니다. 데이터 세트를 적절히 분할하여 배경 마이그레이션에 넣으세요.

  • 스키마 마이그레이션을 수행하는 데 배치 배경 마이그레이션을 사용하지 마세요.

배경 마이그레이션은 다음과 같은 경우에 도움이 될 수 있습니다:

  • 한 테이블에서 여러 개의 별도 테이블로 이벤트를 마이그레이션할 때.

  • 다른 열에 저장된 JSON을 기반으로 한 열을 채울 때.

  • 외부 서비스의 출력에 의존하는 데이터를 마이그레이션할 때. (예: API.)

주의사항

  • 배치 배경 마이그레이션이 중요한 업그레이드의 일부분이라면, 그것은 릴리즈 게시물에서 발표되어야 합니다. 이 마이그레이션이 이 범주에 속하는지 확실하지 않다면 프로젝트 관리자와 논의하세요.

  • 배치 배경 마이그레이션을 생성하려면 generator를 사용하여 필수 파일이 기본적으로 생성되도록 하세요.

배치 배경 마이그레이션 작동 방식

배치 배경 마이그레이션(BBM)은 Gitlab::BackgroundMigration::BatchedMigrationJob의 서브클래스로서 perform 메서드를 정의합니다. 첫 번째 단계로, 일반 마이그레이션은 BBM 클래스와 필요한 인수를 사용하여 batched_background_migrations 레코드를 생성합니다. 기본적으로, batched_background_migrations는 활성 상태에 있으며, 이는 Sidekiq 작업자가 실제 배치 마이그레이션을 실행하기 위해 선택합니다.

모든 마이그레이션 클래스는 네임스페이스 Gitlab::BackgroundMigration에 정의되어야 합니다. 파일은 lib/gitlab/background_migration/ 디렉터리에 배치하세요.

실행 메커니즘

배치 배경 마이그레이션은 큐에서 enqueue된 순서대로 선택됩니다. 여러 개의 마이그레이션이 동시에 활성 상태이며 동일한 데이터베이스 테이블을 대상으로 하지 않는 한 병렬로 가져와 실행됩니다. 기본적으로 병렬로 처리되는 마이그레이션 수는 2개이며, GitLab.com의 경우 이 제한은 4로 설정되어 있습니다.

마이그레이션이 실행을 위해 선택되면, 특정 배치에 대한 작업이 생성됩니다. 각 작업 실행 후, 마지막 20개의 작업의 성과에 따라 마이그레이션의 배치 크기가 증가하거나 감소할 수 있습니다.

작업자가 사용 가능해지면 BBM이 실행됩니다.

Idempotence

일괄 백그라운드 마이그레이션은 Sidekiq 프로세스의 맥락에서 실행됩니다.

일반적인 Sidekiq 규칙이 적용되며, 특히 작업은 작고 멱등성 있어야 한다는 규칙이 그러합니다. 마이그레이션 작업이 재시도되는 경우 데이터 무결성이 보장되도록 하십시오.

자세한 내용은 Sidekiq 최선의 관행 가이드라인을 참조하세요.

Migration optimization

각 작업 실행 후, 마이그레이션이 최적화될 수 있는지 확인하는 검증이 발생합니다.

최적화의 기본 메커니즘은 시간 효율성 개념을 기반으로 합니다. 마지막 N개의 작업에 대한 시간 효율성의 지수 이동 평균을 계산하고 일괄 백그라운드 마이그레이션의 배치 크기를 최적 값으로 업데이트합니다.

For 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

Job retry mechanism

일괄 백그라운드 마이그레이션의 재시도 메커니즘은 작업 실패 시 작업을 다시 실행하도록 보장합니다.

다음 다이어그램은 우리의 재시도 메커니즘의 다양한 단계를 보여줍니다:

Failed batched background migrations

아래 조건 중 하나라도 참인 경우 전체 일괄 백그라운드 마이그레이션이 failed로 표시됩니다
(/chatops run batched_background_migrations status MIGRATION_ID
마이그레이션을 failed로 표시합니다):

Throttling batched migrations

일괄 마이그레이션은 업데이트가 중점이고 이러한 마이그레이션으로 인한 사고가 발생한 적이 있기 때문에, 부하가 적은 미래의 사고를 완화하기 위해 스로틀링 메커니즘이 존재합니다.

다음 데이터베이스 지표가 마이그레이션을 스로틀링하기 위해 확인됩니다.
정지 신호를 수신하면 마이그레이션이 설정된 시간(10분) 동안 일시 중지됩니다:

  • 임계값을 넘어서는 WAL 큐 보류 아카이브.
  • 마이그레이션이 작동하는 테이블에 대한 활성 자동 진공.
  • 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를 인수로 받습니다.

column_name을 선택할 때는, 고유하게 반복할 수 있는 열 유형을 사용하고, 바람직하게는 테이블의 기본 키를 사용하는지 확인하세요.

테이블은 여기에서 정의된 열을 기반으로 반복됩니다. 자세한 내용은 비 고유 열에 대한 배치를 참조하세요.

사용법:

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 예제를 사용하여 마이그레이션을 배치로 실행되도록 큐에 추가하세요. 클래스 이름과 인수를 마이그레이션의 값으로 교체하세요:

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_schemagitlab_main에서 gitlab_main_cell로 변경되었더라도, 배치된 백그라운드 마이그레이션을 큐에 추가할 때 사용된 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_fromname을 반환하고, copy_toname_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 도우미 메서드를 제공합니다:

  1. 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을 확장하도록 해야 합니다. 그렇지 않으면 레코드가 범위를 고려하지 않고 처리됩니다.

  2. 배포 후 마이그레이션에서 배치된 백그라운드 마이그레이션을 큐에 추가합니다:

    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)에 대한 인덱스가 필요합니다. 각 배치 문서 더 자세한 정보를 참조하세요.

여러 데이터베이스에 대한 데이터 액세스

백그라운드 마이그레이션은 일반 마이그레이션과 달리 여러 데이터베이스에 액세스할 수 있으며, 이를 통해 효율적으로 데이터에 액세스하고 업데이트할 수 있습니다. 사용될 데이터베이스를 올바르게 지정하기 위해 마이그레이션 코드 내에 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 파일을 업데이트할 것.

예시

원래 마이그레이션:

# frozen_string_literal: true

class QueueResolveVulnerabilitiesForRemovedAnalyzers < Gitlab::Database::Migration[2.2]
  milestone '17.3'

  MIGRATION = "ResolveVulnerabilitiesForRemovedAnalyzers"

  def up
    # 노-옵, 원래 마이그레이션에 버그가 있었으며, 이는 수정되었습니다
  end

  def down
    # 노-옵, 원래 마이그레이션에 버그가 있었으며, 이는 https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162527에서 수정되었습니다
  end
end

재큐된 마이그레이션:

# frozen_string_literal: true

class RequeueResolveVulnerabilitiesForRemovedAnalyzers < Gitlab::Database::Migration[2.2]
  milestone '17.4'

  restrict_gitlab_migration gitlab_schema: :gitlab_main

  MIGRATION = "ResolveVulnerabilitiesForRemovedAnalyzers"
  DELAY_INTERVAL = 2.minutes
  BATCH_SIZE = 10_000
  SUB_BATCH_SIZE = 100

  def up
    # QueueResolveVulnerabilitiesForRemovedAnalyzers에서 이전 백그라운드 마이그레이션 실행을 지웁니다
    delete_batched_background_migration(MIGRATION, :vulnerability_reads, :id, [])

    queue_batched_background_migration(
      MIGRATION,
      :vulnerability_reads,
      :id,
      job_interval: DELAY_INTERVAL,
      batch_size: BATCH_SIZE,
      sub_batch_size: SUB_BATCH_SIZE
    )
  end

  def down
    delete_batched_background_migration(MIGRATION, :vulnerability_reads, :id, [])
  end
end

배치 마이그레이션 사전:

milestonequeued_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: # 이 BBM을 완료한 마이그레이션 버전

비식별 열에 대한 배치 처리

기본 배치 처리 전략은 기본 키 열을 효과적으로 반복하는 방법을 제공합니다.

하지만 값이 고유하지 않은 열에서 반복해야 하는 경우, 다른 배치 처리 전략을 사용해야 합니다.

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

참고: scope_to로 정의된 추가 필터LooseIndexScanBatchingStrategydistinct_each_batch에 의해 무시됩니다.

배치 백그라운드 마이그레이션의 전체 시간 추정 계산

BBM이 완료되는 데 얼마나 걸리는지 추정하는 것이 가능합니다. GitLab은 이미 db:gitlabcom-database-testing 파이프라인을 통해 추정을 제공합니다.

이 추정치는 테스트 환경에서 생산 데이터를 샘플링하여 작성된 것으로, 마이그레이션이 걸릴 수 있는 최대 시간을 나타내며, 반드시 마이그레이션이 실제로 걸리는 시간은 아닙니다.

특정 시나리오에서는 db:gitlabcom-database-testing 파이프라인에서 제공된 추정치로는 마이그레이션되는 기록의 모든 특성을 계산하기에 충분하지 않을 수 있으며, 추가적인 계산이 필요할 수 있습니다.

그렇게 필요할 경우, interval * number of records / max batch size 공식을 사용하여 마이그레이션에 걸리는 시간을 대략적으로 추정할 수 있습니다.

여기서 intervalmax batch size는 작업에 대해 정의된 옵션을 나타내며, total tuple count는 마이그레이션할 기록의 수입니다.

참고: 추정치는 마이그레이션 최적화 메커니즘의 영향을 받을 수 있습니다.

배치 백그라운드 마이그레이션 정리하기

참고: 남아 있는 백그라운드 마이그레이션을 정리하는 것은 주요 또는 부차적 릴리스에서 수행해야 합니다. 패치 릴리스에서는 이를 수행할 수 없습니다.

백그라운드 마이그레이션이 오랜 시간이 걸릴 수 있으므로, 큐에 추가한 후 즉시 정리할 수 없습니다. 예를 들어, 마이그레이션 프로세스에서 사용되는 열을 삭제할 수 없으며, 작업이 실패할 것입니다.

따라서 남아 있는 작업을 완료한 후 정리할 수 있도록 향후 릴리스에서 별도의 배포 후 마이그레이션을 추가해야 합니다. (예: 열 삭제)

foo(큰 JSON 블롭 포함)에서 열 bar(문자열 포함)로 데이터를 마이그레이션하려면 다음과 같이 수행합니다:

  1. 릴리스 A:
    1. 주어진 ID를 가진 행에 대한 마이그레이션을 수행하는 마이그레이션 클래스를 생성합니다.
    2. 다음 기술 중 하나를 사용하여 새 행을 업데이트합니다:
      • 응용 프로그램 로직이 필요 없는 복사 작업을 위한 새로운 트리거 생성.
      • 레코드가 생성되거나 업데이트될 때 모델/서비스에서 이 작업을 처리합니다.
      • 레코드를 업데이트하는 새로운 사용자 정의 백그라운드 작업을 생성합니다.
    3. 배포 후 마이그레이션에서 모든 기존 행에 대해 배치 백그라운드 마이그레이션을 큐에 추가합니다.
  2. 릴리스 B:
    1. 배치 백그라운드 마이그레이션이 완료되었는지 확인하는 배포 후 마이그레이션을 추가합니다.
    2. 애플리케이션이 새 열을 사용하고 새 레코드를 업데이트하지 않도록 코드를 배포합니다.
    3. 이전 열을 제거합니다.

이전에 GitLab의 이전 버전에서 프로젝트를 가져오는 경우, 임포트/익스포트 버전을 증가시켜야 할 수 있으며, 새 형식으로 데이터를 유지해야 합니다.

배치 백그라운드 마이그레이션을 지원하는 인덱스 추가

때로는 배치 백그라운드 마이그레이션을 지원하기 위해 새로운 인덱스나 임시 인덱스를 추가해야 합니다.

이를 위해, 배경 마이그레이션을 큐에 추가하는 배포 후 마이그레이션보다 앞서서 게시된 마이그레이션에서 인덱스를 생성합니다.

인덱스 생성 후 직접 사용할 수 있도록 하기 위해 특별한 주의가 필요한 몇 가지 경우에 대한 추가 정보는 데이터베이스 인덱스 추가하기 문서를 참조하세요.

데이터베이스 테스트 파이프라인에서 특정 배치 실행

참고: 오직 데이터베이스 유지관리자들만이 데이터베이스 테스트 파이프라인 아티팩트를 볼 수 있습니다. 이 방법을 사용해야 한다면, 누군가의 도움을 요청하세요.

우리는 GitLab.com에서 특정 배치에서 배치 백그라운드 마이그레이션이 실패했다고 가정합니다. 어떤 쿼리가 실패했는지 및 그 이유를 파악하고 싶습니다. 현재로서는 쿼리 정보(특히 쿼리 매개변수)를 검색하는 좋은 방법이 없으며, 더 많은 로깅을 통해 전체 마이그레이션을 다시 실행하는 것은 긴 과정이 될 것입니다.

다행히도, 데이터베이스 마이그레이션 파이프라인을 활용하여 추가 로깅 및/또는 문제 해결을 위해 특정 배치를 다시 실행할 수 있습니다.

예시는 Draft: Test PG::CardinalityViolation fix를 참조하되, 전체 섹션을 읽는 것을 잊지 마세요.

이를 위해서는 다음을 수행해야 합니다:

  1. 배치 start_idend_id 찾기
  2. 정기 마이그레이션 생성하기
  3. 마이그레이션 헬퍼에 대한 우회 처리 적용하기 (선택사항)
  4. 데이터베이스 마이그레이션 파이프라인 시작하기

배치 start_idend_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 스키마의 테이블을 사용함) 마이그레이션이 실패하게 됩니다. 이를 방지하기 위해, 데이터베이스 헬퍼를 몽키 패치하여 테스트 파이프라인 작업이 실패하지 않도록 해야 합니다:

  1. 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
  1. 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 경고가 표시되면 종속 BBM이 아직 완료되지 않은 것입니다. 이는 finalized_by 키를 참조하여 완료 여부를 결정합니다. BBM 사전.

예제:

# 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

관리

note
BBM 관리는 GitLab 팀원만 사용할 수 있는 chatops 통합을 통해 이루어집니다.

배치 백그라운드 마이그레이션 목록

시스템의 배치 백그라운드 마이그레이션을 나열하려면 다음 명령을 실행합니다:

/chatops run batched_background_migrations list

이 명령은 다음과 같은 옵션을 지원합니다:

  • 데이터베이스 선택:
    • --database DATABASE_NAME: 주어진 데이터베이스에 연결합니다:
      • main: 메인 데이터베이스 사용(기본).
      • ci: CI 데이터베이스 사용.
  • 환경 선택:
    • --dev: dev 환경 사용.
    • --staging: staging 환경 사용.
    • --staging_ref: staging_ref 환경 사용.
    • --production : production 환경 사용(기본).

출력 예시:

활성 배치 백그라운드 마이그레이션 목록을 나열하는 ChatOps 명령의 출력.

note
ChatOps는 created_at(내림차순)에 따라 정렬된 20개의 배치 백그라운드 마이그레이션을 반환합니다.

배치 백그라운드 마이그레이션의 진행 및 상태 모니터링

특정 배치 백그라운드 마이그레이션의 상태 및 진행 상황을 보려면 이 명령을 실행합니다:

/chatops run batched_background_migrations status MIGRATION_ID

이 명령은 다음과 같은 옵션을 지원합니다:

  • 데이터베이스 선택:
    • --database DATABASE_NAME: 주어진 데이터베이스에 연결합니다:
      • main: 메인 데이터베이스 사용(기본)
      • ci: CI 데이터베이스 사용
  • 환경 선택:
    • --dev: dev 환경 사용.
    • --staging: staging 환경 사용.
    • --staging_ref: staging_ref 환경 사용.
    • --production : production 환경 사용(기본).

출력 예시:

MIGRATION_ID를 사용하여 특정 배치 백그라운드 마이그레이션의 진행 상황과 상태를 확인하는 ChatOps 명령의 출력.

Progress는 완료된 배경 마이그레이션의 비율을 나타냅니다.

배치 백그라운드 마이그레이션 상태 정의:

  • Active: 다음 중 하나:
    • 러너가 픽업할 준비가 되어 있습니다.
    • 배치 작업을 실행 중입니다.
  • Finalizing: 배치 작업을 실행 중입니다.

  • Failed: 실패한 배치 백그라운드 마이그레이션입니다.

  • Finished: 완료된 배치 백그라운드 마이그레이션입니다.

  • Paused: 러너에게 보이지 않습니다.

배치 백그라운드 마이그레이션 중지

배치 백그라운드 마이그레이션을 중지하려면 다음 명령어를 실행해야 합니다:

/chatops run batched_background_migrations pause MIGRATION_ID

이 명령은 다음 옵션을 지원합니다:

  • 데이터베이스 선택:
    • --database DATABASE_NAME: 지정된 데이터베이스에 연결:
      • main: 기본 데이터베이스를 사용합니다.
      • ci: CI 데이터베이스를 사용합니다.
  • 환경 선택:
    • --dev: dev 환경을 사용합니다.
    • --staging: staging 환경을 사용합니다.
    • --staging_ref: staging_ref 환경을 사용합니다.
    • --production : production 환경을 사용합니다.

출력 예제:

MIGRATION_ID를 사용하여 특정 배치 백그라운드 마이그레이션을 중지하기 위한 ChatOps 명령의 출력입니다.

주의: 활성 배치 백그라운드 마이그레이션만 중지할 수 있습니다.

배치 백그라운드 마이그레이션 재개

배치 백그라운드 마이그레이션을 재개하려면 다음 명령어를 실행해야 합니다:

/chatops run batched_background_migrations resume MIGRATION_ID

이 명령은 다음 옵션을 지원합니다:

  • 데이터베이스 선택:
    • --database DATABASE_NAME: 지정된 데이터베이스에 연결:
      • main: 기본 데이터베이스를 사용합니다.
      • ci: CI 데이터베이스를 사용합니다.
  • 환경 선택:
    • --dev: dev 환경을 사용합니다.
    • --staging: staging 환경을 사용합니다.
    • --staging_ref: staging_ref 환경을 사용합니다.
    • --production : production 환경을 사용합니다.

출력 예제:

MIGRATION_ID를 사용하여 특정 배치 백그라운드 마이그레이션을 재개하기 위한 ChatOps 명령의 출력입니다.

주의: 활성 배치 백그라운드 마이그레이션만 재개할 수 있습니다.

배경 마이그레이션 활성화 또는 비활성화

매우 제한된 상황에서, 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 로그를 통해:
    1. 배치 백그라운드 마이그레이션을 실행한 후, 작업이 실패하면, Kibana에서 로그를 확인하세요. 프로덕션 Sidekiq 로그를 보고 다음으로 필터링합니다:

      • json.new_state: failed
      • json.job_class_name: <배치 백그라운드 마이그레이션 작업 클래스 이름>
      • json.job_arguments: <배치 백그라운드 마이그레이션 작업 클래스 인수>
    2. json.exception_classjson.exception_message 값을 검토하여 작업이 실패한 이유를 이해하는 데 도움을 받으세요.

    3. 재시도 메커니즘을 기억하세요. 실패가 있다고 해서 작업이 실패한 것은 아닙니다. 항상 작업의 마지막 상태를 확인하세요.

  • 데이터베이스를 통해:

    1. 배치 백그라운드 마이그레이션 CLASS_NAME을 가져옵니다.
    2. 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";
      

테스트

테스트 작성을 요구하는 항목:

  • 배치된 백그라운드 마이그레이션의 큐잉 마이그레이션.
  • 배치된 백그라운드 마이그레이션 자체.
  • 정리 마이그레이션.

:migrationschema: :latest RSpec 태그는 백그라운드 마이그레이션 사양에 대해 자동으로 설정됩니다.
Rails 마이그레이션 테스트 스타일 가이드를 참조하세요.

beforeafter RSpec 훅이 데이터베이스를 아래로 및 위로 마이그레이션합니다. 이 훅들은 다른 배치된 백그라운드 마이그레이션이 호출되는 결과를 초래할 수 있습니다.
spy 테스트 더블을 have_received와 함께 사용하는 것이 권장되며, 일반 테스트 더블을 사용하는 것보다 it 블록에서 정의한 기대 사항이 RSpec 훅에서 호출된 것과 충돌할 수 있기 때문입니다.
자세한 내용은 문제 #35351을 참조하세요.

모범 사례

  1. 처리 중인 데이터 양을 파악하세요.

  2. 배치된 백그라운드 마이그레이션 작업이 idempotent 하도록 하세요.

  3. 작성한 테스트가 잘못된 긍정이 아님을 확인하세요.

  4. 마이그레이션되는 데이터가 중요하고 손실될 수 없는 경우,
    정리 마이그레이션은 완료 전에 데이터의 최종 상태를 확인해야 합니다.

  5. 숫자에 대해 데이터베이스 전문가와 논의하세요.
    마이그레이션은 예상보다 데이터베이스에 더 많은 압력을 추가할 수 있습니다. 스테이징에서 측정하거나 누군가에게 프로덕션에서 측정해 달라고 요청하세요.

  6. 배치된 백그라운드 마이그레이션을 실행하는 데 필요한 시간 양을 파악하세요.

  7. 작업 클래스 내에서 예외를 조용히 처리할 때 주의하세요.
    이로 인해 실패 시나리오에서도 작업이 성공으로 표시될 수 있습니다.

    # 좋음
    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 열에서 데이터를 새 단일 외래 키로 마이그레이션하는 것입니다.
후에 오래된 행을 삭제할 계획이므로 백그라운드 마이그레이션의 일환으로 이들을 업데이트할 필요는 없습니다.

  1. 생성기를 사용하여 배치된 백그라운드 마이그레이션 파일을 생성합니다:

    bundle exec rails g batched_background_migration BackfillRouteNamespaceId --table_name=routes --column_name=id --feature_category=source_code_management
    
  2. source_id 값을 namespace_id로 복사하도록 마이그레이션 작업( BatchedMigrationJob의 하위 클래스)을 업데이트합니다:

    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
    
    note
    작업 클래스는 배치 마이그레이션 프레임워크에 의해 올바르게 처리되도록 BatchedMigrationJob에서 상속됩니다.
    BatchedMigrationJob의 모든 하위 클래스는 배치를 실행하는 데 필요한 인수와
    추적 데이터베이스에 대한 연결로 초기화됩니다.
  3. 데이터베이스에 새 트리거를 추가하는 데이터베이스 마이그레이션을 작성합니다. 예시:

    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
    
  4. 필요한 지연 및 배치 크기로 배포 후 마이그레이션을 업데이트합니다:

    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: source_id 값을 routes에서 namespace_id로 복사함
     feature_category: source_code_management
     introduced_by_url: "https://mr_url"
     milestone: 16.6
     queued_migration_version: 20231113120650
     finalized_by: # 이 bbm을 보장한 마이그레이션 버전
    
    note
    배치된 백그라운드 마이그레이션을 큐에 추가할 때,
    실제 변경을 수행하는 데이터베이스로 스키마를 제한해야 합니다.
    이 경우 routes 레코드를 업데이트하고 있기 때문에
    restrict_gitlab_migration gitlab_schema: :gitlab_main을 설정합니다.
    그러나 CI 데이터 마이그레이션을 수행해야 하는 경우,
    restrict_gitlab_migration gitlab_schema: :gitlab_ci를 설정해야 합니다.

    배포 후, 우리의 애플리케이션은: - 이전과 같이 데이터를 계속 사용합니다. - 기존 데이터와 새로운 데이터 모두가 마이그레이션되도록 보장합니다.

  5. 배치된 백그라운드 마이그레이션이 완료되었는지 확인하는 새 배포 후 마이그레이션을 추가합니다.
    또한 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: source_id 값을 routes에서 namespace_id로 복사함
     feature_category: source_code_management
     introduced_by_url: "https://mr_url"
     milestone: 16.6
     queued_migration_version: 20231113120650
     finalized_by: 20231115120912
    
    note
    만약 배치된 백그라운드 마이그레이션이 완료되지 않으면, 시스템은
    배치된 백그라운드 마이그레이션을 인라인으로 실행합니다.
    이 동작을 원치 않으면 finalize: false를 전달해야 합니다.

    만약 애플리케이션이 100% 마이그레이션된 데이터에 의존하지 않는다면
    (예를 들어, 데이터가 권고적이고 미션 크리티컬이 아니므로),
    이 마지막 단계를 건너뛸 수 있습니다.
    이 단계는 마이그레이션이 완료되었고 모든 행이 마이그레이션되었음을 확인합니다.

  6. 트리거를 제거하는 데이터베이스 마이그레이션을 추가합니다.

    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
    end
    

배치 마이그레이션이 완료된 후,
routes.namespace_id에 데이터가 채워지는 것을 안전하게 의존할 수 있습니다.