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

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

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

스크립트 사용

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

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

매뉴얼으로

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.

마이그레이션은 재시도 제한과 실패 및 중단 표시(한국어 웹사이트에만 적용) 기능을 가질 수 있습니다. 마이그레이션 재시도를 지원하기 위해 필요한 모든 데이터 또는 인덱스 정리는 마이그레이션에서 처리해야 합니다.

건너뛴 마이그레이션

skip_if 프록 추가를 통해 마이그레이션을 건너뛸 수 있습니다. 이 프록는 true 또는 false로 평가됩니다:

class MigrationName < Elastic::Migration
  skip_if ->() { true|false }

조건이 false인 경우에만 마이그레이션이 실행됩니다. 건너뛴 마이그레이션은 대기 중인 마이그레이션의 일부로 표시되지 않습니다.

건너뛴 마이그레이션을 더 이상 사용하지 않도록 표시할 수 있지만, 이러한 마이그레이션은 항상 건너뛰어야 하므로 skip_if 조건을 유지해야 합니다. 한 번 건너뛴 마이그레이션이 폐기되면 처음부터 인덱스를 다시 만드는 방법 이외에 변경을 적용할 방법이 없습니다.

건너뛴 마이그레이션의 문서 파일을 다음 속성과 함께 업데이트하세요:

skippable: true
skip_condition: '<description>'

마이그레이션 도우미

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

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

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를 추가해야 합니다.

batched!를 사용하여 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

건너뛴 마이그레이션을 폐기 처리할 때, skip_if 조건을 유지해야 합니다.

Elastic::MigrationCreateIndex

새로운 인덱스를 생성합니다.

다음이 필요합니다:

  • target_classdocument_type 메서드
  • 해당 클래스에 대한 매핑 및 인덱스 설정은 ee/lib/elastic/latest/ee/lib/elastic/v12p1/에서 수행해야 합니다.
caution
동일한 단계에서 인덱스를 채우기 위해 follow-up 마이그레이션을 수행해야 합니다.
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

데이터베이스의 모든 문서를 elastic search 색인에 다시 색인하며 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::MigrationWorker 해당 옵션에 의해 설정된 지연 시간으로 자체를 다시 대기열에 넣습니다. 일괄 처리는 migrate 메서드에서 처리해야 합니다. 이 설정은 다시 대기열에 넣기 만을 제어합니다.

  • batch_size - batched! 마이그레이션 실행 중 수정된 문서 수를 설정합니다. 이 크기는 업데이트를 충분히 완료할 수있는 값으로 설정해야 합니다. 이것은 아래서 설명하는 throttle_delay 옵션과 함께 조정될 수 있습니다. 이 일괄 처리는 해당 설정을 사용하는 사용자 정의migrate 메서드에서 또는 Elastic::MigrationBackfillHelper 이 설정을 사용하는 migrate 메서드에서 처리합니다. 기본 값은 1000문서입니다.

  • throttle_delay - 일괄 실행 간의 대기 시간을 설정합니다. 각 마이그레이션 일괄 처리에 충분한 시간을 제공하기 위해 이 시간은 충분히 길어야합니다. 또한, 5분 미만이어야합니다. Elastic::MigrationWorker cron worker가 실행되는 주기입니다. 기본 값은 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형 고객이 손상된 마이그레이션을 받지 않게하고 되감기가 필요한 경우를 줄입니다.

Merge 시기

릴리스 후 1주일 내에 마이그레이션을 Merge하지 않는 것이 좋습니다. 이렇게 함으로써 마이그레이션이 실패하거나 예상대로 작동하지 않는 경우 되감기 시간을 확보할 수 있습니다. 최종 주에 아직 개발 중이거나 검토중인 마이그레이션은 다음 마일스톤으로 이동해야합니다.

다중 버전 호환성

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

배포 순서에 따라 마이그레이션이 시작되거나 마쳤는데 그 때까지 응용 프로그램 코드를 실행중인 서버가 여전히 있을 수 있습니다. 이를 고려해야하며 모든 고급 검색 마이그레이션이 배포가 완료된 후에 시작함을 확실히가능할 때까지 이것을 고려해야합니다.

고위험 마이그레이션

Elasticsearch는 트랜잭션을 지원하지 않으므로 항상 응용 프로그램이되있는 상황을 수용할 수있게 마이그레이션을 설계해야합니다. 코드가 마이그레이션이 시작된 후 또는 완료된 후에 되감기된 경우에 이러한 이유 때문에 일반적으로 파괴적인 작업(예 : 데이터 이동 후 삭제)은 마이그레이션이 성공적으로 완료된 후에 다른 Merge Request으로 연기해야합니다. 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를 사용하는 마이그레이션을 실행합니다. 응용 프로그램별 마이그레이션이 미적용 상태인 경우 동일한 문서를 여러 번 재색인하는 것을 피하고 백필 시간을 줄일 수 있습니다.
  • 일괄 처리 작업할 때 일괄 처리 크기를 9,000 문서 미만으로 유지하십시오. 일괄 처리 프로그램은 1분마다 실행되며 10,000 개의 문서를 처리합니다. 따라서 일괄 처리 프로그램은 다른 마이그레이션 일괄 처리가 시도되기 전에 기록을 처리할 시간이 있습니다.
  • 마이그레이션이 완료되는지 확인하기 전에 색인을 새로 고칠때문에 문서 수가 최신 상태인지 확인해야합니다.
  • 각 마이그레이션에 마이그레이션이 시작될 때, 완료 확인이 발생할 때, 마이그레이션이 완료되었을 때의 로그를 추가하십시오. 마이그레이션 문제를 디버깅할 때 이러한 로그가 유용합니다.
  • Elasticsearch Reindex API 작업을 사용하는 경우 모든 색인을 일시 중지하십시오.
  • 마이그레이션이 실패할 수있는 경우 재시도 제한을 추가하십시오. 문제가 발생하는 경우 마이그레이션을 중단시키도록하여 마이그레이션이 잘못되지 않도록하십시오.

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

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

GitLab 주요 버전 업그레이드를 안전한 시기로 선택하여 완전히 마이그레이션되지 않은 색인에 대한 역방향 호환성을 제거합니다. 이러한 작업을 시기함은 업그레이드 문서에 문서화되어 있습니다. 우리는 또한 마이그레이션 코드를 중단된 마이그레이션으로 교체하고 이러한 코드를 지원하지 않는 마이그레이션에 대한 테스트를 제거하여 다음과 같이 선택하는 것이 좋습니다. - 고급 검색 마이그레이션을 유지 관리 할 필요가 없습니다. - 더 이상 지원하지 않는 마이그레이션을 위한 테스트를 실행하지 않아도됩니다. - 해당 마이그레이션이 마저된 migration이 없는 경우 직접 버전에 업그레이드하는 사용자는 처음부터 재색인을 해야한다는 메시지가 표시됩니다.

더 안전하게 하기 위해, 주요 업그레이드 이전 마지막 부속 버전을 추가합니다. 따라서 %13.12에서만 추가된 마이그레이션이 아닌 마이그레이션은 삭제하지 않습니다. 이렇게 하면 GitLab.com에 추가된 마이그레이션이 여러 주가 걸릴 수 있기 때문에 %14.0으로 업그레이드하기 전에 %13.12에서 마이그레이션이 완료되지 않습니다. 이러한 추가적인 안전망은 GitLab.com 배포가 자동화되어 있으며이를 방지하기 위한 자동화 된 확인이없기 때문에 정말 안전합니다. 추가로, 자동화 된 확인이 있더라도 고급 검색 마이그레이션을 위해 GitLab.com 배포를 차단하는 것이 합리적이지 않으며, 또 다른 주가 걸릴 것이기 때문에 유지하고 싶지 않습니다.

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

대상 버전을 업그레이드하기 2개의 이전 마이너 버전에서 생성된 각 마이그레이션에 대해, 우리는 다음을 수행합니다:

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

    include Elastic::MigrationObsolete
    
  3. 사용 가능한 마이그레이션을 오래된 것으로 표시할 때, skip_if 조건을 유지합니다.
  4. 이 마이그레이션을 지원하는 모든 특정 파일을 삭제합니다.
  5. .rubocop_todo/ 디렉터리에서 해당 마이그레이션에 대한 참조가 없는지 확인합니다.
  6. 이 마이그레이션의 역 호환성을 처리하는 모든 로직을 제거합니다. 이를 찾으려면 Elastic::DataMigrationService.migration_has_finished?(:migration_name_in_lowercase)를 찾으면 됩니다.
  7. 이러한 변경 사항이 있는 Merge Request을 만듭니다. 주의해야 할 점은 주요 릴리스가 시작되기 전에 실수로 이를 Merge하지 않아야 합니다.

마이그레이션을 제거하는 프로세스

  1. 현재 주요 릴리스 이전에 오래된 것으로 표시된 마이그레이션을 선택합니다.
  2. 위 단계가 모든 오래된 마이그레이션을 포함하는 경우, 적용되지 않은 마이그레이션이 있는 고객을 위해 마지막으로 하나의 마이그레이션을 보호장치로 유지합니다.
  3. 해당 마이그레이션 파일 및 특정 파일을 삭제합니다.
  4. .rubocop_todo/ 디렉터리에서 해당 마이그레이션에 대한 참조가 없는지 확인합니다.
  5. Merge Request을 만들고 해당 요청을 글로벌 검색팀의 팀원에게 할당합니다.