고급 검색 마이그레이션 스타일 가이드

새로운 고급 검색 마이그레이션 생성

참고: 이 기능은 GitLab 13.0 및 이후에 생성된 인덱스에서만 지원됩니다.

스크립트를 사용하여

  • 소개: GitLab 16.3에서 소개되었습니다.

scripts/elastic-migration을 실행하고 프롬프트에 따라 다음을 생성하세요:

  • 마이그레이션을 정의하는 마이그레이션 파일: ee/elastic/migrate/YYYYMMDDHHMMSS_migration_name.rb
  • 마이그레이션을 테스트하는 스펙 파일: ee/spec/elastic/migrate/YYYYMMDDHHMMSS_migration_name_spec.rb
  • 마이그레이션을 식별하는 사전 파일: ee/elastic/docs/YYYYMMDDHHMMSS_migration_name.yml

수동으로

  • 소개: GitLab 13.6에서 소개되었습니다.

ee/elastic/migrate/ 폴더에서 YYYYMMDDHHMMSS_migration_name.rb 파일 형식의 새 파일을 만듭니다. 이 형식은 Rails 데이터베이스 마이그레이션과 동일합니다.

# frozen_string_literal: true

class MigrationName < Elastic::Migration
  # 중요: Elastic 인덱스 매핑에 대한 모든 업데이트는 각각의 구성 파일에서 복제되어야 합니다:
  #   - 메인 인덱스를 위한 `Elastic::Latest::Config`
  #   - 독립형 인덱스를 위한 `Elastic::Latest::<Type>Config`

  def migrate
  end

  # 마이그레이션이 완료되었는지 확인합니다
  # 완료되었으면 true를 반환하고, 그렇지 않으면 false를 반환합니다
  def completed?
  end
end

적용된 마이그레이션은 gitlab-#{RAILS_ENV}-migrations 인덱스에 저장됩니다. 실행되지 않은 모든 마이그레이션이 Elastic::MigrationWorker 크론 워커에 의해 순차적으로 적용됩니다.

Elastic 인덱스 매핑을 업데이트하려면 해당 파일에 구성을 적용하세요:

  • 메인 인덱스의 경우: Elastic::Latest::Config.
  • 독립형 인덱스의 경우: Elastic::Latest::<Type>Config.

마이그레이션은 재시도 한도와 실패하여 중단된 것으로 표시될 수 있는 기능을 갖추고 있습니다. 마이그레이션 재시도를 지원하기 위해 필요한 모든 데이터 또는 인덱스 정리는 마이그레이션에서 처리되어야 합니다.

마이그레이션 도우미

다음의 마이그레이션 도우미는 ee/app/workers/concerns/elastic/에 사용 가능합니다:

Elastic::MigrationBackfillHelper

인덱스에서 특정 필드를 백필합니다. 대부분의 경우 필드 매핑은 이미 추가되어야 합니다.

단일 필드를 백필하려면 field_name 메서드와 DOCUMENT_TYPE 상수가 필요합니다.

class MigrationName < Elastic::Migration
  include Elastic::MigrationBackfillHelper

  DOCUMENT_TYPE = Issue

  private

  def field_name
    :schema_version
  end
end

하나 이상의 필드가 널이면 여러 필드를 백필하려면 field_names 메서드와 DOCUMENT_TYPE 상수가 필요합니다.

class MigrationName < Elastic::Migration
  include Elastic::MigrationBackfillHelper

  DOCUMENT_TYPE = Issue

  private

  def field_names
    %w[schema_version visibility_level]
  end
end

Elastic::MigrationUpdateMappingsHelper

매핑을 업데이트하여 지정된 매핑으로 put_mapping을 호출합니다.

new_mappings 메서드와 DOCUMENT_TYPE 상수가 필요합니다.

class MigrationName < Elastic::Migration
  include Elastic::MigrationUpdateMappingsHelper

  DOCUMENT_TYPE = Issue

  private

  def new_mappings
    {
      schema_version: {
        type: 'short'
      }
    }
  end
end

Elastic::MigrationRemoveFieldsHelper

인덱스에서 지정된 필드를 제거합니다.

index_name, document_type 메서드가 필요합니다. 제거할 필드가 하나인 경우 field_to_remove 메서드를 추가하고, 그 외에는 배열 형식의 fields_to_remove를 추가합니다.

document_type와 일치하는 문서가 지정된 필드를 Elasticsearch에서 가지고 있는지 일괄적으로 확인합니다. 문서가 존재하는 경우 Painless 스크립트를 사용하여 update_by_query를 수행합니다.

class MigrationName < Elastic::Migration
  include Elastic::MigrationRemoveFieldsHelper

  batched!
  throttle_delay 1.minute

  private

  def index_name
    User.__elasticsearch__.index_name
  end

  def document_type
    'user'
  end

  def fields_to_remove
    %w[two_factor_enabled has_projects]
  end
end

기본 일괄 크기는 10,000입니다. 이 값을 BATCH_SIZE로 지정하여 재정의할 수 있습니다.

class MigrationName < Elastic::Migration
  include Elastic::MigrationRemoveFieldsHelper

  batched!
  BATCH_SIZE = 100

  ...
end

Elastic::MigrationObsolete

필요하지 않을 때 마이그레이션을 사용하지 않게 표시합니다.

class MigrationName < Elastic::Migration
  include Elastic::MigrationObsolete
end

Elastic::MigrationCreateIndex

새로운 색인을 생성합니다.

필요 사항:

  • target_classdocument_type 메서드
  • ee/lib/elastic/latest/ee/lib/elastic/v12p1/ 내의 클래스에 대한 매핑 및 색인 설정

경고: 동일한 마일스톤에서 색인을 채우기 위한 후속 마이그레이션을 수행해야 합니다.

class MigrationName < Elastic::Migration
  include Elastic::MigrationCreateIndex

  retry_on_failure

  def document_type
    :epic
  end

  def target_class
    Epic
  end
end

Search::Elastic::MigrationReindexBasedOnSchemaVersion

지정된 문서 유형을 저장하는 색인에 있는 모든 문서를 재색인하고 schema_version을 업데이트합니다.

DOCUMENT_TYPENEW_SCHEMA_VERSION 상수가 필요합니다. 색인 매핑에는 YYMM 형식의 schema_version 정수 필드가 있어야 합니다.

class MigrationName < Elastic::Migration
  include Search::Elastic::MigrationReindexBasedOnSchemaVersion

  batched!
  batch_size 9,000
  throttle_delay 1.minute

  DOCUMENT_TYPE = WorkItem
  NEW_SCHEMA_VERSION = 23_08
  UPDATE_BATCH_SIZE = 100
end

Search::Elastic::MigrationDeleteBasedOnSchemaVersion

지정된 문서 유형을 저장하는 색인에서 schema_version이 주어진 값보다 작은 모든 문서를 삭제합니다.

DOCUMENT_TYPE 상수와 schema_version 메서드가 필요합니다. 색인 매핑에는 YYMM 형식의 schema_version 정수 필드가 있어야 합니다.

class MigrationName < Elastic::Migration
  include ::Search::Elastic::MigrationDeleteBasedOnSchemaVersion

  DOCUMENT_TYPE = Issue

  batch_size 10,000
  batched!
  throttle_delay 1.minute
  retry_on_failure

  def schema_version
    23_12
  end
end

Search::Elastic::MigrationDatabaseBackfillHelper

데이터베이스의 모든 문서를 limited_indexing 설정을 준수하여 Elastic Search 색인에 다시 색인합니다.

DOCUMENT_TYPE 상수와 respect_limited_indexing? 메서드가 필요합니다.

class MigrationName < Elastic::Migration
  include ::Search::Elastic::MigrationDatabaseBackfillHelper

  batch_size 10,000
  batched!
  throttle_delay 1.minute
  retry_on_failure

  DOCUMENT_TYPE = Issue

  def respect_limited_indexing?
    true
  end
end

Elastic::MigrationHelper

이전 예제에 맞지 않는 마이그레이션을 처리할 때 사용할 수 있는 메서드를 포함합니다.

class MigrationName < Elastic::Migration
  include Elastic::MigrationHelper

  def migrate
  ...
  end

  def completed?
  ...
  end
end

Elastic::MigrationWorker가 지원하는 마이그레이션 옵션

Elastic::MigrationWorker는 다음 마이그레이션 옵션을 지원합니다:

  • batched! - 마이그레이션을 일괄 처리로 실행할 수 있도록 허용합니다. 설정된 경우, Elastic::MigrationWorker 자체를 throttle_delay 옵션으로 설정된 지연 시간으로 다시 대기열에 넣습니다. 일괄처리는 migrate 메서드에서 처리해야 합니다. 이 설정은 다시 대기열에 대해서만 제어합니다.

  • batch_size - batched! 마이그레이션 실행 중 수정된 문서 수를 설정합니다. 이 크기는 업데이트에 충분한 시간을 제공하는 값이어야 합니다. 이는 아래 설명된 throttle_delay 옵션과 조합하여 튜닝할 수 있습니다. 일괄 처리는 사용자 정의 migrate 메서드에서 처리하거나 Elastic::MigrationBackfillHelper 이 설정을 사용하는 migrate 메서드를 통해 조정될 수 있습니다. 기본값은 1000입니다.

  • throttle_delay - 일괄 처리 간의 대기 시간을 설정합니다. 각 마이그레이션 일괄 처리가 충분한 시간을 제공할 수 있도록 설정해야 합니다. 또한 이 시간은 5분보다 짧아야 합니다. 왜냐하면 Elastic::MigrationWorker cron 작업이 동작하는 주기이기 때문입니다. 기본값은 3분입니다.

  • pause_indexing! - 마이그레이션이 실행될 때 색인을 일시 중지합니다. 이 설정은 마이그레이션이 완료될 때 원래의 색인 설정을 기록하고 다시 설정합니다.

  • space_requirements! - 마이그레이션이 실행될 때 클러스터에 충분한 여유 공간이 있는지 확인합니다. 이 설정은 마이그레이션이 실행될 때 필요한 저장 공간이 없는 경우 마이그레이션을 중지시킵니다. 마이그레이션은 space_required_bytes 메서드를 통해 필요한 공간을 제공해야 합니다.

  • retry_on_failure - 실패 시 재시도 기능을 활성화합니다. 기본적으로 30번의 재시도를 수행한 후 마이그레이션이 중단됩니다. 재시도 횟수를 사용자 정의하려면 max_attempts 매개변수를 전달하세요: retry_on_failure max_attempts: 10

# frozen_string_literal: true

class BatchedMigrationName < Elastic::Migration
  # 마이그레이션을 일괄 처리로 실행할 것임을 선언합니다.
  batched!
  throttle_delay 10.minutes
  pause_indexing!
  space_requirements!
  retry_on_failure

  # ...
end

다운 타임 방지

마이그레이션 되돌리기

만약 마이그레이션이 실패하거나 GitLab.com에서 중단된 경우, 마이그레이션을 도입한 변경 사항을 되돌리는 것을 선호합니다. 이렇게 함으로써 Self-Managed 고객이 손상된 마이그레이션을 받지 않게 되고 백포트(backport)가 필요한 경우가 줄어듭니다.

병합 시기

릴리즈 후 1주일 이내에 마이그레이션을 병합하는 것을 선호하지 않습니다. 이렇게 함으로써 마이그레이션이 실패하거나 예상대로 작동하지 않는 경우를 대비하는 시간을 확보할 수 있습니다. 릴리즈의 최종 주에 개발 중이거나 검토 중인 마이그레이션은 다음 마일스톤으로 미루어져야 합니다.

다중 버전 호환성

다른 GitLab 변경 사항처럼 고급 검색 마이그레이션도 동시에 여러 버전의 애플리케이션이 실행 중인 경우를 지원해야 합니다. (다중 버전 호환성 참조)

배포 순서에 따라, 마이그레이션이 시작되었거나 완료되었고 마이그레이션 이전 애플리케이션 코드를 실행 중인 서버가 있는 경우가 가능합니다. 이러한 점을 고려하여 모든 고급 검색 마이그레이션이 배포가 완료된 후에 시작될 수 있도록 보장해야 합니다.

고위험 마이그레이션

Elasticsearch가 트랜잭션을 지원하지 않기 때문에, 마이그레이션이 시작되거나 완료된 후에 애플리케이션 코드가 되돌아가는 상황을 고려해야 합니다.

이러한 이유로 파괴적인 작업(예: 데이터 이동 후 삭제)은 일반적으로 마이그레이션이 성공적으로 완료된 후 다음 병합 요청으로 연기해야 합니다. Self-Managed 고객의 경우 중요한 데이터 손실이 발생할 수 있는 경우 릴리즈를 미루어 다음 릴리즈로 연기해야 합니다.

마이그레이션 실행 시간 계산

GitLab.com에서 마이그레이션이 실행되는 데 소요되는 시간을 이해하는 것은 중요합니다. 마이그레이션에 의해 처리될 문서 수를 파생할 수 있습니다. 이 숫자는 데이터베이스 또는 기존 Elasticsearch 인덱스를 쿼리하여 얻을 수 있습니다. 다음 공식을 사용하여 실행 시간을 계산하십시오:

> batch_size = 9_000
=> 9000
> throttle_delay = 1.minute
=> 1 minute
> number_of_documents = 15_536_906
=> 15536906
> (number_of_documents / batch_size) * throttle_delay
=> 1726 minutes
> (number_of_documents / batch_size) * throttle_delay / 1.hour
=> 28

고급 검색 마이그레이션을 위한 모범 사례

최상의 결과를 얻기 위해 다음의 모범 사례를 따르십시오:

  • 각 문서 유형별로 모든 마이그레이션을 순서화하여, Elastic::MigrationUpdateMappingsHelper를 사용하는 마이그레이션을 사용하는 마이그레이션들이 Elastic::MigrationBackfillHelper를 사용하는 마이그레이션보다 먼저 실행되도록 합니다. 이렇게 함으로써 모든 마이그레이션이 적용되지 않은 상태에서 동일한 문서를 다수 번 재색인하는 것을 피하고 백필(backfill) 시간을 줄일 수 있습니다.
  • 배치 작업 시, 배치 크기를 9,000개 미만으로 유지하십시오. 대량 색인기는 1분마다 실행되며 여기서 10,000개의 문서를 처리합니다. 따라서 대량 색인기가 다음 마이그레이션 배치를 시도하기 전에 레코드를 처리할 시간을 확보할 수 있습니다.
  • 마이그레이션이 완료되었는지 확인하기 전에 인덱스를 새로고침해야 합니다.
  • 각 마이그레이션 시작 시, 완료 확인이 발생할 때, 마이그레이션이 완료될 때마다 로깅 문을 추가하십시오. 이러한 로그는 마이그레이션 문제를 디버깅할 때 유용합니다.
  • Elasticsearch Reindex API 작업을 사용하는 경우 색인 작업을 일시 중지하십시오.
  • 마이그레이션 실패 가능성이 있는 경우 재시도 제한을 설정하는 것을 검토하십시오. 이렇게 하면 문제가 발생할 경우 마이그레이션을 중지할 수 있습니다.

주요 버전 업그레이드 시 고급 검색 마이그레이션 삭제

일반적으로 고급 검색 마이그레이션은 장기간에 걸쳐 여러 코드 경로를 지원해야 하므로, 안전하게 삭제할 수 있는 시기에 제거하는 것이 중요합니다.

GitLab 주요 버전 업그레이드를 안전한 시기로 선택하여 완전히 마이그레이션되지 않은 인덱스의 역호환성을 제거하는 것을 선택합니다. 이에 관한 내용은 업그레이드 문서에서 문서화되어 있습니다. 또한, 멈춰진 마이그레이션 코드를 제거하고 테스트를 제거하여:

  • 고급 검색 마이그레이션에서 호출되는 코드를 유지할 필요가 없습니다.
  • 더 이상 지원하지 않는 마이그레이션에 대한 테스트를 실행하는 데 시간을 낭비할 필요가 없습니다.
  • 이러한 마이그레이션이 실행되지 않은 연산자가 대상 버전으로 직접 업그레이드하는 경우 다시 처음부터 색인 작업을 진행할 것을 요구하는 메시지가 표시됩니다.

더해, 매우 안전하게 제거하려면 주요 업그레이드 직전의 마이너 버전에서만 추가된 마이그레이션을 제거하지 않습니다. 예를 들어, %14.0으로 업그레이드하는 경우 %13.12에서만 추가된 마이그레이션을 제거하면 안됩니다. 이러한 추가적인 안전장치를 통해 GitLab.com에서 여러 주간 걸릴 수 있는 마이그레이션에 대비할 수 있습니다. GitLab.com 배포는 자동화되어 있으며 이를 방지하는 자동화된 검사가 없으므로 추가적인 주의가 필요합니다. 게다가, 만일 해당 자동화된 검사가 있더라도 고급 검색 마이그레이션을 GitLab.com 배포를 지연시키고 싶지 않을 것이므로, 이러한 마이그레이션은 종종 1주일의 추가 기간이 필요하기 때문에 실제로는 GitLab.com 배포를 차단할 필요가 없습니다.

마이그레이션을 오래 되었다고 표시하는 프로세스

주요 버전을 업그레이드하기 2개의 마이너 버전 전에 생성된 모든 마이그레이션에 대해 우리는 다음을 합니다:

  1. 마이그레이션이 GitLab.com에서 실제로 성공적으로 완료되었는지 확인합니다.
  2. 마이그레이션 내용을 다음으로 대체합니다:

    include Elastic::MigrationObsolete
    
  3. 이 마이그레이션을 지원하는 모든 spec 파일을 삭제합니다.
  4. .rubocop_todo/ 디렉토리에서 이 마이그레이션에 대한 참조가 없는지 확인합니다.
  5. 이 마이그레이션에 대한 역 호환성 처리를 제거합니다. 이를 찾으려면 Elastic::DataMigrationService.migration_has_finished?(:migration_name_in_lowercase)를 찾으면 됩니다.
  6. 이러한 변경으로 병합 요청을 작성합니다. 우리가 주요 릴리스가 시작되기 전에 이를 실수로 병합하지 않도록 주의하세요.

마이그레이션 제거 프로세스

  1. 현재 주요 릴리스 이전에 폐기로 표시된 마이그레이션을 선택합니다.
  2. 위 단계에 모든 폐기 마이그레이션이 포함되면, 적용되지 않은 마이그레이션을 가진 고객들을 위한 안전장치로 마지막 마이그레이션을 유지합니다.
  3. 해당 마이그레이션 파일 및 spec 파일을 삭제합니다.
  4. .rubocop_todo/ 디렉토리에서 해당 마이그레이션에 대한 참조가 없는지 확인합니다.
  5. 병합 요청을 생성하고 그것을 글로벌 검색 팀의 팀원에게 할당합니다.