머그레이션에서 다운타임 피하기

데이터베이스 작업 중에는 특정 작업에 다운타임이 필요할 수 있습니다. 마이그레이션에서는 다운타임을 허용할 수 없기 때문에 다운타임 없이 동일한 결과를 얻을 수 있는 일련의 단계를 사용해야 합니다. 이 안내서에서는 다운타임이 필요해 보일 수 있는 여러 작업, 작업의 영향 및 다운타임 없이 수행하는 방법에 대해 설명합니다.

컬럼 삭제

컬럼을 제거하는 것은 까다로운 작업입니다. 왜냐하면 실행 중인 GitLab 프로세스는 이러한 컬럼이 존재할 것으로 예상하며, ActiveRecord는 컬럼 스키마를 캐시하기 때문입니다. 실제로 이러한 컬럼이 참조되지 않더라도 그렇습니다. 이는 컬럼이 명시적으로 ignore로 표시되지 않은 경우에 발생합니다. 이를 안전하게 처리하려면 세 가지 단계를 세 가지 릴리스에 걸쳐 수행해야 합니다:

  1. 컬럼 무시 (릴리스 M)
  2. 컬럼 삭제 (릴리스 M+1)
  3. 무시 규칙 제거 (릴리스 M+2)

컬럼을 세 개의 릴리스에 걸쳐 처리하는 이유는 컬럼을 삭제하는 것이 쉽게 롤백할 수 없는 파괴적인 작업이기 때문입니다.

이 프로시저를 따르면 GitLab.com으로의 배포와 독립형 설치의 업그레이드 프로세스에서 이러한 단계를 함께 묶지 않도록 할 수 있습니다.

컬럼 무시 (릴리스 M)

첫 번째 단계는 애플리케이션 코드에서 컬럼을 무시하고 모델 유효성 검증을 포함하여 해당 컬럼에 대한 모든 코드 참조를 제거하는 것입니다. 이 작업은 필요합니다. 왜냐하면 Rails는 컬럼을 캐시하고 여러 곳에서 이 캐시를 재사용하기 때문입니다. 이는 무시할 컬럼을 정의하여 수행할 수 있습니다. 예를 들어, 릴리스 12.5에서 User 모델에서 updated_at을 무시하려면 다음과 같이 사용할 수 있습니다:

class User < ApplicationRecord
  include IgnorableColumns
  ignore_column :updated_at, remove_with: '12.7', remove_after: '2019-12-22'
end

여러 컬럼을 무시할 수도 있습니다:

ignore_columns %i[updated_at created_at], remove_with: '12.7', remove_after: '2019-12-22'

모델이 CE 및 EE에 모두 존재하는 경우 컬럼은 CE 모델에서 무시해야 합니다. 모델이 EE에만 있는 경우 EE에 추가해야 합니다.

컬럼 무시 규칙을 제거하는 것이 안전한 시점을 나타내기 위해 다음이 필요합니다:

  • remove_with: 일반적으로 컬럼 무시를 추가한 릴리스에서 두 릴리스(M+2) (12.7의 경우) 후에 설정합니다. (예: 12.7이라는 개발 주기가 2019-12-18부터 2020-01-17 사이에 있고, 12.6컬럼을 삭제하는 릴리스이므로, 2019-12-22로 설정하는 것이 안전합니다.)
  • remove_after: 컬럼 무시를 제거할 수 있는 안전한 날짜로, 일반적으로 M+1 릴리스 날짜 이후, M+2 개발 주기 중에 설정합니다. (예: 12.6컬럼을 삭제하는 릴리스이므로, 2019-12-22로 설정하는 것이 안전합니다.)

이 정보를 통해 컬럼 무시에 대해 더 나은 이해를 얻을 수 있고, 일반 릴리스 및 GitLab.com으로의 배포에 대해 컬럼 무시를 너무 일찍 제거하지 않도록 할 수 있습니다. 예를 들어, 이를 통해 컬럼 무시 변경 사항 및 이후 컬럼 무시를 제거하는 사용자 지정 변화를 동시에 배포하여(이로 인해 다운타임이 발생할 수 있음) 상황을 피할 수 있습니다.

이 예에서는 컬럼을 무시하는 변경 사항이 릴리스 12.5에 들어갑니다.

컬럼 삭제 (릴리스 M+1)

이 예에서는 컬럼을 삭제하는 것이 12.6 릴리스의 포스트-배포 마이그레이션에 들어갑니다:

우선 포스트-배포 마이그레이션을 작성하여 시작합니다:

bundle exec rails g post_deployment_migration remove_users_updated_at_column

컬럼을 제거하는 마이그레이션을 작성할 때 고려해야 할 시나리오는 다음과 같습니다:

삭제된 컬럼에 인덱스나 제약이 없음

이 경우 트랜잭션 마이그레이션을 사용할 수 있습니다:

class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.1]
  def up
    remove_column :users, :updated_at
  end
  
  def down
    add_column :users, :updated_at, :datetime
  end
end

삭제된 컬럼에 해당하는 인덱스나 제약이 있음

down 메서드에서 삭제한 인덱스나 제약을 다시 추가해야 하는 경우 트랜잭션 마이그레이션을 사용할 수 없습니다. 이러한 마이그레이션은 다음과 같이 작성해야 합니다:

class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!
  
  def up
    remove_column :users, :updated_at
  end
  
  def down
    add_column(:users, :updated_at, :datetime, if_not_exists: true)
    
    # up 메서드에서 삭제한 인덱스나 제약을 다시 추가해야 합니다. 예:
    add_concurrent_index(:users, :updated_at)
  end
end

down 메서드에서 컬럼이 이미 존재하는지 확인한 후 다시 추가합니다. 이는 마이그레이션이 트랜잭션이 아니므로 실행 중에 실패할 수 있기 때문에 수행합니다.

전체 마이그레이션을 감싸는 트랜잭션을 비활성화하기 위해 disable_ddl_transaction!를 사용합니다.

데이터베이스 마이그레이션에 대한 자세한 정보는 마이그레이션 스타일 가이드를 참조하세요.

무시 규칙 제거 (릴리스 M+2)

다음 릴리스인 이 예에서 12.7에 무시 규칙을 제거하는 Merge Request을 설정합니다. 이를 통해 ignore_column 줄을 제거하고 (필요하지 않다면) IgnoreableColumns를 사용하지 않도록 설정합니다.

이 Merge Request은 remove_with로 표시된 릴리스가 지정된 후와 remove_after 날짜가 지난 후에만 Merge되어야 합니다.

컬럼 이름 변경

기본적인 방식으로 컬럼 이름을 변경하면 다운타임이 필요할 수 있습니다. 왜냐하면 애플리케이션이 데이터베이스 마이그레이션 중이나 후에도 이전 컬럼 이름을 계속 사용할 수 있기 때문입니다. 다운타임 없이 컬럼의 이름을 변경하려면 일반 마이그레이션과 포스트-배포 마이그레이션 두 가지 마이그레이션이 필요합니다. 이 두 가지 마이그레이션은 동일한 릴리스에 들어갈 수 있습니다. 다음과 같은 단계가 필요합니다:

  1. 일반 마이그레이션 추가 (릴리스 M)
  2. 컬럼 무시 (릴리스 M)
  3. 포스트-배포 마이그레이션 추가 (릴리스 M)
  4. 무시 규칙 제거 (릴리스 M+1)
note
기본 값을 사용하는 컬럼은 이름을 변경할 수 없습니다. 자세한 내용은 이 merge request를 참조하세요.

일반 마이그레이션 추가 (릴리스 M)

먼저 일반 마이그레이션을 작성해야 합니다. 이 마이그레이션은 Gitlab::Database::MigrationHelpers#rename_column_concurrently를 사용하여 이름을 변경해야 합니다. 예를 들어,

# db/migrate의 일반 마이그레이션
class RenameUsersUpdatedAtToUpdatedAtTimestamp < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!
  
  def up
    rename_column_concurrently :users, :updated_at, :updated_at_timestamp
  end
  
  def down
    undo_rename_column_concurrently :users, :updated_at, :updated_at_timestamp
  end
end

이 마이그레이션은 이름을 변경하여 데이터를 동기화시키고 인덱스 및 외래 키를 복사하는 작업을 처리합니다.

컬럼에 원래 컬럼 이름을 포함하지 않는 하나 이상의 인덱스가 있는 경우, 앞에서 설명한 절차는 실패합니다. 이 경우 이러한 인덱스의 이름을 변경해야 합니다.

열(M 릴리스) 무시

애플리케이션 코드에서 해당 열을 무시하고 사용하지 않도록 하는 것이 다음 단계입니다. 이 단계는 Rails에서 열을 캐시하고 다양한 위치에서 이 캐시를 재사용하기 때문에 필요합니다. 해당 단계는 열을 삭제했을 때의 첫 번째 단계와 유사하며 동일한 요구 사항이 적용됩니다.

class User < ApplicationRecord
  include IgnorableColumns
  ignore_column :updated_at, remove_with: '12.7', remove_after: '2019-12-22'
end

배포 후 마이그레이션 추가 (릴리스 M)

이름을 변경하는 과정에서는 배포 후 마이그레이션으로 일정 정리가 필요합니다. 이 정리는 Gitlab::Database::MigrationHelpers#cleanup_concurrent_column_rename를 사용하여 수행할 수 있습니다.

# db/post_migrate에 있는 배포 후 마이그레이션
class CleanupUsersUpdatedAtRename < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!
  
  def up
    cleanup_concurrent_column_rename :users, :updated_at, :updated_at_timestamp
  end
  
  def down
    undo_cleanup_concurrent_column_rename :users, :updated_at, :updated_at_timestamp
  end
end

큰 테이블을 리네이밍하는 경우, 첫 번째 마이그레이션이 실행되었지만 두 번째 정리 마이그레이션이 아직 실행되지 않은 상태를 주의하여 고려하세요. Canary로 인해 시스템이 상당한 시간 동안 이 상태에서 실행될 수 있습니다.

무시 규칙 제거 (릴리스 M+1)

열을 삭제했을 때와 마찬가지로 이름이 변경된 후에는, 다음 릴리스에서 무시 규칙을 제거해야 합니다.

열 제약 조건 변경

NOT NULL 절(또는 다른 제약 조건)을 추가하거나 제거하는 것은 일반적으로 다운타임 없이 수행할 수 있습니다. 그러나 이는 어떤 애플리케이션 변경이 먼저 배포되어야 한다는 것을 의미합니다. 따라서 열의 제약 조건을 변경하는 작업은 배포 후 마이그레이션에서 수행해야 합니다.

change_column을 사용하지 않는 것이 좋습니다. 왜냐하면 이는 전체 열 유형을 재정의하기 때문에 효율적이지 않은 쿼리를 생성하기 때문입니다.

각 특정 사용 사례에 대한 다음 가이드를 확인할 수 있습니다:

열 유형 변경

열의 유형을 변경하는 것은 Gitlab::Database::MigrationHelpers#change_column_type_concurrently를 사용하여 수행할 수 있습니다. 이 메서드는 rename_column_concurrently와 유사하게 작동합니다. 예를 들어, users.username의 유형을 string에서 text로 변경하려면:

  1. 일반 마이그레이션 생성
  2. 배포 후 마이그레이션 생성
  3. 새 유형으로 데이터 캐스팅

일반 마이그레이션 생성

일반 마이그레이션은 일시적인 이름으로 새 열을 생성하고 데이터를 동기화하기 위해 일부 트리거를 설정하는 데 사용됩니다. 이러한 마이그레이션은 다음과 같이 보일 것입니다:

# db/migrate에 있는 일반 마이그레이션
class ChangeUsersUsernameStringToText < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!
  
  def up
    change_column_type_concurrently :users, :username, :text
  end
  
  def down
    undo_change_column_type_concurrently :users, :username
  end
end

배포 후 마이그레이션 생성

다음으로는 배포 후 마이그레이션을 사용하여 변경 사항을 정리해야 합니다.

# db/post_migrate에 있는 배포 후 마이그레이션
class ChangeUsersUsernameStringToTextCleanup < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!
  
  def up
    cleanup_concurrent_column_type_change :users, :username
  end
  
  def down
    undo_cleanup_concurrent_column_type_change :users, :username, :string
  end
end

이것으로 끝났습니다!

새 유형으로 데이터 캐스팅

일부 유형 변경은 새로운 유형으로 데이터를 캐스팅할 필요가 있습니다. 예를 들어 text에서 jsonb로 변경하는 경우 type_cast_function 옵션을 사용합니다. 잘못된 데이터가 없으며 캐스트가 항상 성공하는지 확인하세요. 또한 캐스트 오류를 처리하는 사용자 지정 함수를 제공할 수도 있습니다.

예시 마이그레이션:

  def up
    change_column_type_concurrently :users, :settings, :jsonb, type_cast_function: 'jsonb'
  end

열 기본값 변경

열 기본값을 변경하는 것은 Rails가 기본값과 동일한 값을 기록할 때의 동작 방식 때문에 어려울 수 있습니다.

note
데이터베이스는 레코드를 작성할 때 기본값을 PostgreSQL에 전송하는 것을 무시합니다. 이 작업은 데이터베이스에 맡기게 됩니다. 마이그레이션이 열 기본값을 변경하면 실행 중인 애플리케이션은 스키마 캐시 덕분에 이 변경 사실을 인지하지 못합니다. 따라서 데이터베이스 마이그레이션 후 코드의 새 버전을 배포하는 경우에 특히 문제가 발생합니다.

실수로 코드가 기존 기본값을 명시적으로 기록하도록 하는 INSERT 쿼리에서 Rails가 이전 기본값을 새 기본값으로 교체하지 않도록 하려면 다단계 프로세스를 따라야 합니다:

  1. 모델에 SafelyChangeColumnDefault concern를 추가하고 포스트 마이그레이션에서 기본값 변경을 수행합니다.
  2. 다음 소수릴리스에서 SafelyChangeColumnDefault concern을 정리합니다(#clean-up-the-safelychangecolumndefault-concern-in-the-next-minor-release).

Self-Managed형 버전은 단일 제로-다운타임 배포에 전체 미러 릴리스를 번들합니다.

모델에 SafelyChangeColumnDefault concern 추가 및 포스트 마이그레이션에서 기본값 변경

첫 번째 단계는 애플리케이션 코드에서 열을 안전하게 변경할 수 있도록 표시하는 것입니다.

class Ci::Build < ApplicationRecord
  include SafelyChangeColumnDefault
  
  columns_changing_default :partition_id
end

그런 다음 기본값을 변경하기 위해 포스트 마이그레이션을 만듭니다:

bundle exec rails g post_deployment_migration change_ci_builds_default
class ChangeCiBuildsDefault < Gitlab::Database::Migration[2.1]
  def change
    change_column_default('ci_builds', 'partition_id', from: 100, to: 101)
  end
end

다음 소수릴리스에서 SafelyChangeColumnDefault concern 정리

다음 소수릴리스에서 새로운 Merge Request를 만들어 columns_changing_default 호출을 제거하세요. 또한 다른 열에 필요하지 않은 경우 SafelyChangeColumnDefault를 제거하세요.

대규모 테이블의 스키마 변경

change_column_type_concurrentlyrename_column_concurrently은 다운타임 없이 테이블의 스키마를 변경하는 데 사용될 수 있지만 대규모 테이블에는 잘 작동하지 않을 수 있습니다. 모든 작업이 순차적으로 수행되기 때문에 마이그레이션이 완료되기까지 매우 오랜 시간이 걸릴 수 있어 배포를 지연시킬 수 있습니다. 또한 데이터베이스에 많은 압력을 주어 순차적으로 많은 행을 빠르게 업데이트하기 때문에 데이터베이스에 많은 압력을 주게 됩니다.

데이터베이스 압력을 줄이려면 대규모 테이블의 열을 마이그레이션할 때 배경 마이그레이션을 대신 사용해야 합니다(예: issues). 배경 마이그레이션은 작업/하부로드를 배포할 때 보다 긴 시간 동안 분산하여 작업하므로 배포를 늦추지 않고도 작업/로드를 분산합니다.

자세한 내용은 일괄 배경 마이그레이션 정리 문서를 참조하십시오.

인덱스 추가

add_concurrent_index를 사용하면 인덱스를 추가할 때 다운타임이 필요하지 않습니다.

자세한 내용은 마이그레이션 스타일 가이드를 참조하세요.

인덱스 삭제

인덱스를 삭제하는 경우 다운타임이 필요하지 않습니다.

테이블 추가

이 작업은 아직 테이블을 사용하는 코드가 없기 때문에 안전합니다.

테이블 삭제

포스트-디플로이먼트 마이그레이션을 사용하여 테이블을 안전하게 삭제할 수 있지만, 응용 프로그램에서 더 이상 해당 테이블을 사용하지 않는 경우에만 해당합니다.

데이터베이스 사전에 설명된 프로세스를 사용하여 db/docs/deleted_tables에 테이블을 추가하십시오. 테이블이 삭제되었더라도 데이터베이스 마이그레이션에서 아직 참조됩니다.

테이블 이름 변경

테이블 이름을 변경하는 경우 데이터베이스 마이그레이션 중 또는 후에 애플리케이션이 이전 테이블 이름을 계속 사용할 수 있기 때문에 다운타임이 필요합니다.

테이블과 ActiveRecord 모델이 아직 사용되지 않는 경우, 기존 테이블을 제거하고 새로운 테이블을 만드는 것이 테이블을 “이름 바꾸는” 우선적인 방법입니다.

멀티 릴리스 테이블 이름 변경 프로세스를 따르면 다운타임 없이 테이블 이름을 변경할 수 있습니다.

외래 키 추가

외래 키를 추가하는 경우 일반적으로 3단계로 진행됩니다:

  1. 트랜잭션 시작
  2. ALTER TABLE을 사용하여 제약 조건 추가
  3. 모든 기존 데이터 확인

일반적으로 ALTER TABLE은 트랜잭션 종료까지 배타적 잠금을 확보하므로 이 접근 방식은 다운타임이 필요합니다.

GitLab에서는 Gitlab::Database::MigrationHelpers#add_concurrent_foreign_key를 사용하여 이를 우회할 수 있습니다. 이 방법을 사용하면 다운타임이 필요하지 않습니다.

외래 키 제거

이 작업은 다운타임이 필요하지 않습니다.

integer 기본 키를 bigint로 마이그레이션

integer 기본 키(PK)를 가진 일부 테이블에 대한 오버플로우 리스크를 방지하기 위해 해당 PK를 bigint로 마이그레이션해야 합니다. 이를 위한 다운타임 없는 마이그레이션 및 데이터베이스에 과도한 부하를 유발하지 않는 과정은 아래에 설명되어 있습니다.

컨버전 초기화 및 기존 데이터 마이그레이션 시작 (릴리스 N)

프로세스를 시작하려면, 새로운 bigint 열을 만들기 위한 일반적인 마이그레이션을 추가하십시오. 제공된 initialize_conversion_of_integer_to_bigint 헬퍼를 사용하십시오. 이 헬퍼는 또한 새 레코드의 경우 양쪽 열을 동기화하기 위한 데이터베이스 트리거를 생성합니다 (코드):

# frozen_string_literal: true

class InitializeConversionOfMergeRequestMetricsToBigint < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!
  
  TABLE = :merge_request_metrics
  COLUMNS = %i[id]
  
  def up
    initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS)
  end
  
  def down
    revert_initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS)
  end
end

새로운 bigint 열을 무시하십시오:

# frozen_string_literal: true

class MergeRequest::Metrics < ApplicationRecord
  include IgnorableColumns
  ignore_column :id_convert_to_bigint, remove_with: '16.0', remove_after: '2023-05-22'
end

배치된 백그라운드 마이그레이션을 큐에 넣으십시오 (코드) 기존 데이터를 마이그레이션하기 위해:

# frozen_string_literal: true

class BackfillMergeRequestMetricsForBigintConversion < Gitlab::Database::Migration[2.1]
  restrict_gitlab_migration gitlab_schema: :gitlab_main
  
  TABLE = :merge_request_metrics
  COLUMNS = %i[id]
  
  def up
    backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS, sub_batch_size: 200)
  end
  
  def down
    revert_backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS)
  end
end

백그라운드 마이그레이션 모니터링

마이그레이션이 실행 중인지를 확인하십시오. 실행 중인 상태를 확인하는 다양한 방법은 아래에 설명되어 있습니다.

배치된 백그라운드 마이그레이션의 상위 수준 상태

배치된 백그라운드 마이그레이션 상태를 확인하는 방법은 배치된 백그라운드 마이그레이션 상태를 확인을 참조하세요.

데이터베이스 쿼리

관련 데이터베이스 테이블을 직접 쿼리할 수 있습니다. 읽기 전용 복제에 대한 액세스가 필요합니다. 예시 쿼리:

-- 주어진 테이블에 대한 배치된 백그라운드 마이그레이션의 세부정보 가져오기
SELECT * FROM batched_background_migrations WHERE table_name = 'namespaces'\gx

-- 주어진 테이블에 대한 배치된 백그라운드 마이그레이션 작업의 상태별 작업 수
SELECT
  batched_background_migrations.id, batched_background_migration_jobs.status, COUNT(*)
FROM
  batched_background_migrations
  JOIN batched_background_migration_jobs ON batched_background_migrations.id = batched_background_migration_jobs.batched_background_migration_id
WHERE
  table_name = 'namespaces'
GROUP BY
  batched_background_migrations.id, batched_background_migration_jobs.status;

-- 주어진 테이블에 대한 배치된 백그라운드 마이그레이션 진행율 (추정 총 튜플 수 기준)
SELECT
  m.table_name,
  LEAST(100 * sum(j.batch_size) / pg_class.reltuples, 100) AS percentage_complete
FROM
  batched_background_migrations m
  JOIN batched_background_migration_jobs j ON j.batched_background_migration_id = m.id
  JOIN pg_class ON pg_class.relname = m.table_name
WHERE
  j.status = 3 AND m.table_name = 'namespaces'
GROUP BY m.id, pg_class.reltuples;

Sidekiq 로그

배치된 백그라운드 마이그레이션을 실행하는 워커를 모니터링하는 데 Sidekiq 로그를 사용할 수 있습니다:

  1. @gitlab.com 이메일 주소로 Kibana에 로그인합니다.
  2. 인덱스 패턴을 pubsub-sidekiq-inf-gprd*로 변경합니다.
  3. json.queue: cronjob:database_batched_background_migration 필터를 추가합니다.

PostgreSQL 느린 쿼리 로그

느린 쿼리 로그는 1초 이상 소요된 낮은 쿼리를 추적합니다. 배치된 백그라운드 마이그레이션에 대한 로그를 보려면:

  1. @gitlab.com 이메일 주소로 Kibana에 로그인합니다.
  2. 인덱스 패턴을 pubsub-postgres-inf-gprd*로 변경합니다.
  3. json.endpoint_id.keyword: Database::BatchedBackgroundMigrationWorker 필터를 추가합니다.
  4. 원하는 경우, 최신 업데이트만 보려면 json.command_tag.keyword: UPDATE 필터를 추가합니다.
  5. 원하는 경우, 실패한 명령만 보려면 json.error_severity.keyword: ERROR 필터를 추가합니다.
  6. 테이블 이름으로 필터를 추가합니다.

Grafana 대시보드

데이터베이스의 상태를 모니터링하기 위해 다음 추가 메트릭을 사용하세요:

  • PostgreSQL Tuple Statistics: 활발하게 변환 중인 테이블에 대한 업데이트 속도가 높거나 해당 테이블의 데드 튜플 백분율이 증가하는 경우, autovacuum이 따라가지 못하는 것일 수 있습니다.
  • PostgreSQL Overview: 주 데이터베이스 서버의 시스템 사용률 또는 초당 트랜잭션이 높은 경우, 마이그레이션이 문제를 일으키고 있는 것일 수 있습니다.

프로메테우스 메트릭스

각 배치된 백그라운드 마이그레이션마다 발행된 메트릭스 가 프로메테우스에 공개되었습니다. 이러한 메트릭스는 Thanos에서 검색하고 시각화할 수 있습니다 (예시 확인).

열 교환 (릴리스 N + 1)

백그라운드 마이그레이션이 완료되고 모든 레코드에 대해 새 bigint 열이 채워지면 열을 교환할 수 있습니다. 교환은 포스트-배포 마이그레이션으로 수행됩니다. 정확한 프로세스는 변환되는 테이블에 따라 다르지만, 일반적으로 다음 단계에서 수행됩니다:

  1. 제공된 ensure_batched_background_migration_is_finished 헬퍼를 사용하여 배치 마이그레이션이 완료되었는지 확인하세요 (예시 확인). 마이그레이션이 완료되지 않았다면, 이후 단계는 어차피 실패합니다. 사전 확인을 통해 더 유익한 오류 메시지를 보여주기를 바랍니다.
  2. Gitlab::Database::MigrationHelpers::ConvertToBigint 모듈의 add_bigint_column_indexes 헬퍼 메서드를 사용하여 기존 인덱스와 일치하는 bigint 열에 대한 인덱스를 생성합니다.
    • 헬퍼 메서드는 필요한 모든 bigint 인덱스를 생성하는 것으로 예상되지만, 기존 인덱스 중 누락된 것이 없는지 확인하는 것이 좋습니다. 헬퍼에 대한 자세한 내용은 합병 요청에서 찾을 수 있습니다.
  3. 기존 integer 열을 사용하는 기존 FK에 맞는 bigint 열을 사용하여 FK(Foreign Key)를 생성합니다. 테이블을 참조하는 FK 및 마이그레이션 대상이 되는 테이블을 참조하는 FK 둘 다 이 작업을 수행합니다 (예시 확인).
  4. 트랜잭션 내에서 열을 교환합니다:
    1. 관련된 테이블을 잠급니다. 교착 상태의 가능성을 줄이기 위해 부모에서 자식 순서로 수행하는 것이 권장됩니다 (예시 확인).
    2. 이름을 변경하여 열을 교환합니다 (예시 확인).
    3. 트리거 함수를 재설정합니다 (예시 확인).
    4. 기본값을 교환합니다 (예시 확인).
    5. PK(Parent Key) 제약 조건을 교환합니다 (해당하는 경우) (예시 확인).
    6. 기존 인덱스를 제거하고 새로운 인덱스의 이름을 변경합니다 (예시 확인).
      • add_bigint_column_indexes 헬퍼를 사용하여 생성된 bigint 인덱스의 이름은 Gitlab::Database::MigrationHelpers::ConvertToBigint 모듈에서 bigint_index_name을 호출하여 검색할 수 있습니다.
    7. 기존 외래 키(FK)를 제거하고 새로운 외래 키의 이름을 변경합니다 (예시 확인).

예시 합병 요청마이그레이션 참조.

트리거 및 이전 integer 열 제거 (릴리스 N + 2)

포스트-배포 마이그레이션 및 제공된 cleanup_conversion_of_integer_to_bigint 헬퍼를 사용하여 데이터베이스 트리거와 이전 integer 열을 제거합니다 (예시 확인).

무시 규칙 제거 (릴리스 N + 3)

열이 삭제된 후 다음 릴리스에서 더 이상 필요하지 않으므로 무시 규칙을 제거합니다 (예시 확인).

데이터 마이그레이션

데이터 마이그레이션은 까다로울 수 있습니다. 데이터를 마이그레이션하는 보통적인 접근 방식은 다음과 같이 3단계로 이뤄집니다:

  1. 초기 데이터 배치를 마이그레이션합니다.
  2. 애플리케이션 코드를 배포합니다.
  3. 나머지 데이터를 마이그레이션합니다.

보통은 이렇게 수행되지만 항상 그렇지는 않습니다. 예를 들어, 필드 형식을 JSON에서 다른 형식으로 변경해야 하는 경우에는 약간의 문제가 발생할 수 있습니다. 애플리케이션 코드를 배포하기 전에 기존 데이터를 변경하려고 한다면 대부분의 경우 에러가 발생할 가능성이 높습니다. 반면, 애플리케이션 코드를 배포한 후에 마이그레이션을 수행한다면 같은 문제에 부딪힐 수 있습니다.

잘못된 데이터를 단순히 수정해야 한다면 보통 포스트-배포 마이그레이션이 충분합니다. 데이터 형식을 변경해야 하는 경우(예: JSON에서 다른 형식으로)에는 일반적으로 새로운 열을 추가하고 애플리케이션이 해당 열을 사용하도록 하는 것이 가장 좋습니다. 이러한 경우에는 다음 절차를 따릅니다:

  1. 새 형식의 새로운 열을 추가합니다.
  2. 기존 데이터를 이 새로운 열로 복사합니다.
  3. 애플리케이션 코드를 배포합니다.
  4. 포스트-배포 마이그레이션에서 나머지 데이터를 복사합니다.

일반적으로 일반적인 해결책은 없으므로 가능한 최선의 방법으로 구현되도록 합병 요청에서 이러한 유형의 마이그레이션을 논의하는 것이 가장 좋습니다.