고급 검색 이관 스타일 가이드

새로운 고급 검색 이관 만들기

note
이 기능은 GitLab 13.0 및 이후에 생성된 색인에 대해서만 지원됩니다.

스크립트를 사용하여

  • [GitLab 16.3]에서 소개됨(https://gitlab.com/gitlab-org/gitlab/-/issues/414674).

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]에서 소개됨(https://gitlab.com/gitlab-org/gitlab/-/issues/234046).

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

하나 이상의 필드가 null이면 여러 필드를 다시 채우려면 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

지정된 매핑을 호출하여 인덱스의 매핑을 업데이트합니다.

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에서 지정된 필드를 가지고 있는지 배치 단위로 확인합니다. 문서가 있는 경우 update_by_query를 수행하기 위해 Painless 스크립트를 사용합니다.

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/의 클래스에 대한 매핑 및 인덱스 설정
caution
같은 마일스톤에서 인덱스를 채우는 후속 이관을 수행해야 합니다.
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 설정을 존준하는 엘라스틱 서치 색인에서 데이터베이스에 있는 모든 문서를 재색인합니다.

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::MigrationWorkerthrottle_delay 옵션으로 설정된 지연 시간까지 자체를 재기록합니다. 일괄 처리는 migrate 메서드에서 처리해야 합니다. 이 설정은 재기록만 제어합니다.

  • batch_size - batched! 마이그레이션 실행 중 수정된 문서 수를 설정합니다. 이 크기는 업데이트에 충분한 시간을 확보할 수 있는 값으로 설정해야 합니다. 이는 아래 설명된 throttle_delay 옵션과 조합하여 튜닝할 수 있습니다. 기본값은 1000개 문서입니다.

  • throttle_delay - 일괄 실행 간의 대기 시간을 설정합니다. 각 마이그레이션 일괄 처리에 충분한 시간을 확보할 수 있도록 이 시간은 충분히 길게 설정해야 합니다. 또한, 이 시간은 Elastic::MigrationWorker cron 작업이 실행되는 시간 간격인 5분보다 작아야 합니다. 기본값은 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
  pause_indexing!
  space_requirements!
  retry_on_failure
  
  # ...
end

마이그레이션 중단 최소화

마이그레이션 되돌리기

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

Merge 시기

릴리스 후 1주일 이내에 마이그레이션을 Merge하지 않는 것을 선호합니다. 이렇게 하면 마이그레이션이 작동하지 않거나 예상대로 작동하지 않는 경우 되돌리는 시간을 확보할 수 있습니다. 릴리스 최종 주에 개발이나 검토 중인 마이그레이션은 다음 마일스톤으로 밀도록 해야 합니다.

다중 버전 호환성

기타 GitLab 변경 사항과 마찬가지로 고급 검색 마이그레이션은 응용 프로그램의 여러 버전이 동시에 실행 중인 경우를 지원해야 합니다.

배포 순서에 따라 마이그레이션이 시작되거나 완료되었는데도 마이그레이션 이전 애플리케이션 코드를 실행 중인 서버가 있을 수 있습니다. 이를 고려해야 하며, 모든 고급 검색 마이그레이션이 배포가 완료된 후에 시작함을 보장할 수 있을 때까지 이에 대한 고려가 필요합니다.

고위험 마이그레이션

Elasticsearch는 트랜잭션을 지원하지 않기 때문에, 마이그레이션이 실행된 후에 애플리케이션이 되돌아가는 상황을 수용할 수 있도록 마이그레이션을 설계해야 합니다. 이러한 이유로 보통 사용자 정의해서 중요 데이터 손실 위험이 있는 경우, Self-managed 고객에게 중요 데이터 손실 위험이 있는 경우 다른 릴리스로 연기해야 합니다.

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

GitLab.com에서 마이그레이션이 실행될 수 있는 시간을 이해하는 것이 중요합니다. 마이그레이션이 처리할 문서 수를 유도해야 합니다. 이 숫자는 데이터베이스 또는 기존 Elasticsearch 색인에서 나올 수 있습니다. 다음 공식을 사용하여 실행 시간을 계산하세요:

> batch_size = 9_000
=> 9000
> throttle_delay = 1
=> 1
> number_of_documents = 15_536_906
=> 15536906
> (number_of_documents / batch_size) * throttle_delay
=> 1726
> (number_of_documents / batch_size) * throttle_delay / 1시간
=> 28

고급 검색 마이그레이션의 모범 관행

최상의 결과를 얻으려면 다음과 같은 최고의 실천 방법을 따르세요:

  • 각 문서 유형에 대한 모든 마이그레이션을 순서대로 정렬하고, Elastic::MigrationUpdateMappingsHelper을 사용하는 마이그레이션이 Elastic::MigrationBackfillHelper을 사용하는 마이그레이션보다 먼저 실행되도록 합니다. 이렇게 하면 모든 마이그레이션이 적용되지 않은 상태라면 동일한 문서를 여러 번 다시 색인하지 않아도 되며, 백필 시간을 줄일 수 있습니다.
  • 일괄 처리 시 일괄 크기를 9,000건 이하로 유지하세요. 대량 색인기는 매분 실행되고 일괄 처리될 문서 수가 10,000건입니다. 이렇게 함으로써 대량 색인기는 다른 마이그레이션 일괄 처리가 시도되기 전에 레코드를 처리할 시간이 생깁니다.
  • 문서 개수가 최신 상태인지 확인하려면 마이그레이션이 완료되기 전에 색인을 새로 고칩니다.
  • 각 마이그레이션에 로깅 문을 추가하여, 마이그레이션이 시작될 때, 완료 확인이 수행될 때, 그리고 마이그레이션이 완료될 때마다 해당 로그를 기록합니다. 이러한 로그는 마이그레이션 문제를 디버깅할 때 유용합니다.
  • Elasticsearch Reindex API 작업을 사용하는 경우 색인을 일시 중지하세요.
  • 마이그레이션에 실패할 가능성이 있는 경우 재시도 한도를 추가하세요. 이렇게 하면 문제가 발생할 경우 마이그레이션이 중단될 수 있습니다.

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

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

당사는 GitLab 주요 버전 업그레이드를 사용하여 완전히 마이그레이트되지 않은 색인에 대한 후방 호환성을 제거하는 것으로 안전한 시점으로 선택합니다. 이를 업그레이드 설명서에서 문서화합니다. 또한 마이그레이션 코드를 중단된 마이그레이션으로 대체하고 해당 마이그레이션에 대한 테스트를 제거하여 다음과 같은 이유로 선택합니다.

  • 고급 검색 마이그레이션에서 호출되는 코드를 유지할 필요가 없음.
  • 더 이상 지원하지 않는 마이그레이션에 대한 테스트를 실행하는 CI 시간을 낭비하지 않음.
  • 이 마이그레이션을 실행하지 않은 경우 및 대상 버전으로 직접 업그레이드한 운영자는 새로 고침을 요청하는 메시지를 볼 수 있습니다.

추가적인 안전을 위해, 주요 업그레이드 전의 마이너 버전에서 만든 마이그레이션은 삭제하지 않습니다. 따라서 만약 %14.0로 업그레이드한다면 %13.12에서만 추가된 마이그레이션은 삭제하지 않아야 합니다. 이 추가적인 안전장치를 사용하면 GitLab.com에서 여러 주 동안 걸리는 마이그레이션을 고려할 수 있습니다. 따라서 %13.12에서 마이그레이션을 마칠 때까지 GitLab.com을 %14.0으로 업그레이드하는 것은 바람직하지 않습니다. GitLab.com 배포는 자동화되어 있으므로 이를 방지하는 자동화된 점검이 없으며, 추가적으로 있더라도 고급 검색 마이그레이션으로 인해 GitLab.com 배포를 지연시키고 싶지 않습니다. 그 이유는 아직 다른 남은 주가 있을 수 있으며, 그 기간은 너무 깁니다.

마이그레이션을 오래된 것으로 표시하는 프로세스

주요 버전 업그레이드 2개 전에 만든 각 마이그레이션에 대해 다음을 수행합니다:

  1. 해당 마이그레이션이 GitLab.com에서 실제로 완료되었는지 확인합니다.
  2. 마이그레이션의 내용을 다음으로 변경합니다:

    include Elastic::MigrationObsolete
    
  3. 이 마이그레이션을 지원하는 모든 특수 파일을 삭제합니다.
  4. .rubocop_todo/ 디렉터리에서 마이그레이션에 대한 어떠한 참조도 없는지 확인합니다.
  5. 이 마이그레이션을 위한 후방 호환성을 처리하는 모든 로직을 제거합니다. 이를 찾으려면 Elastic::DataMigrationService.migration_has_finished?(:migration_name_in_lowercase) 을 찾아야 합니다.
  6. 이러한 변경 사항으로 Merge 리퀘스트를 만듭니다. 주의: 주요 릴리스가 시작되기 전에 실수로 Merge하지 않도록 합니다.

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

  1. 현재 주요 릴리스 이전에 폐기된 마이그레이션을 선택합니다.
  2. 위 단계가 모든 폐기된 마이그레이션을 포함하는 경우, 적용하지 않은 마이그레이션이 있는 고객을 위한 막걸리로서 마지막 하나의 마이그레이션을 보존합니다.
  3. 해당 마이그레이션 파일 및 특수 파일을 삭제합니다.
  4. .rubocop_todo/ 디렉터리에서 해당 마이그레이션에 대한 어떠한 참조도 없는지 확인합니다.
  5. 이를 범세계 검색팀의 팀원에게 할당하여 Merge 리퀘스트를 만듭니다.