직접 전송 가입자에 새로운 관계 추가

고수준에서 직접 전송 가입자에 새 관계를 추가하려면 다음을 수행해야 합니다.

  1. 내보낸 데이터 목록에 새 관계 추가.
  2. 데이터 처리 지침을 포함하여 가져오는 쪽에 새 ETL(추출/변환/로드) 파이프라인 추가.
  3. 새로 추가된 파이프라인을 가져오는 단계 목록에 추가.
  4. 충분한 테스트 커버리지 보장.

소스로부터 내보내기

내보내는 몇 가지 유형의 관계가 있습니다.

  • ActiveRecord 연결. import_export.yml 파일에서 읽고 JSON으로 직렬화한 후 NDJSON 파일에 작성됩니다. 각 관계는 .gz 파일 또는 .tar.gz 파일(컬렉션의 경우)로 내보내어 업로드되고 GitLab의 대상 인스턴스의 REST API를 사용하여 다운로드하고 가져오는 데 사용됩니다.
  • 바이너리 파일. 예: 업로드 또는 LFS 객체.
  • 내보내지는 관계 몇 가지. 그러나 가져오기하는 동안 GraphQL API에서 직접 읽힙니다.

ActiveRecord 연결의 경우 성능 상의 이유로 GraphQL API 대신 NDJSON을 사용해야 합니다. 깊게 중첩된 연결은 많은 네트워크 요청을 생성할 수 있어 전체 마이그레이션을 느리게 할 수 있습니다.

ActiveRecord 관계 내보내기

직접 전송 가입자의 기본 동작은 파일 기반 가져오기 도구에 크게 의존하며, 가져오기에 포함할 Project 관계 목록을 설명하는 import_export.yml 파일을 사용합니다. Group에 대한 유사한 import_export.yml이 사용 가능합니다.

예를 들어 새로운 Project 관계인 documents가 있다고 가정해 보겠습니다. 이 새 관계를 가져오기에 지원하려면 다음을 수행해야 합니다:

  1. import_export.yml 파일에 추가.
  2. 새 관계에 대한 테스트 커버리지 추가.
  3. 추가된 관계가 예상대로 내보내는지 확인.

import_export.yml 파일에 추가

참고: 이 파일에 나열된 연결은 위에서 아래로 가져옵니다. 순서에 영향을 받는 연결이 있는 경우 해당 연결을 필요로 하는 연결 앞에 배치하세요. 예를 들어 문서는 병합 요청보다 먼저 가져와야 하며, 그렇지 않으면 유효하지 않습니다.

  1. import_export.ymltree.project에 관계를 추가합니다.

    diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
    index 43d66e0e67b7..0880a27dfce2 100644
    --- a/lib/gitlab/import_export/project/import_export.yml
    +++ b/lib/gitlab/import_export/project/import_export.yml
    @@ -122,6 +122,7 @@ tree:
             - label:
               - :priorities
         - :service_desk_setting
    +    - :documents
       group_members:
         - :user
    

    참고: 연결이 기업 에디션 전용 기능과 관련이 있다면 파일의 맨 끝에 ee.tree.project 트리에 추가하여 GitLab의 기업 에디션 인스턴스에서만 내보내고 가져옵니다.

    연결이 하위 관계를 포함할 필요가 없는 경우, 이것으로 충분합니다. 그러나 하위 관계가 더 필요한 경우(예: 노트), 해당 관계를 나열해야 합니다. 예를 들어 문서에는 노트(노트에 포함된 상이한 이모지)와 이모지(문서에 있는 이모지)가 포함될 수 있으며, 이들을 마이그레이션하려면 해당 내용을 나열해야 합니다. 따라서 관계는 다음과 같습니다:

    diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
    index 43d66e0e67b7..0880a27dfce2 100644
    --- a/lib/gitlab/import_export/project/import_export.yml
    +++ b/lib/gitlab/import_export/project/import_export.yml
    @@ -122,6 +122,7 @@ tree:
             - label:
               - :priorities
         - :service_desk_setting
    +    - documents:
           - :award_emoji
           - notes:
             - :award_emoji
       group_members:
         - :user
    
  2. 관계의 included_attributes 추가. 기본적으로 YAML 파일의 included_attributes에 나열되지 않은 모든 관계 속성은 내보내기와 가져오기 모두에서 필터링됩니다. 필요한 속성을 포함하려면 다음과 같이 included_attributes 목록에 추가해야 합니다:

    diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
    index 43d66e0e67b7..dbf0e1275ecf 100644
    --- a/lib/gitlab/import_export/project/import_export.yml
    +++ b/lib/gitlab/import_export/project/import_export.yml
    @@ -142,6 +142,9 @@ import_only_tree:
    
     # Only include the following attributes for the models specified.
     included_attributes:
    +  documents:
    +    - :title
    +    - :description
       user:
         - :id
         - :public_email
    
  3. 관계의 excluded_attributes 추가. 파일에도 excluded_attributes 목록이 있습니다. Project에는 제외된 속성을 추가할 필요가 없지만, Group에는 필요합니다. 이 목록은 가져오기에서 제외되어야 할 속성을 나타냅니다. 이러한 속성은 일반적으로 다음과 같습니다:

    • _id 또는 _ids로 끝나는 모든 항목
    • attributes 포함(사용자 정의 attributes 제외)
    • _html로 끝나는 모든 항목
    • 민감한 항목(예: 토큰, 암호화된 데이터)

    금지된 참조의 전체 목록은 여기에서 확인하세요.

  4. 관계의 methods 추가. 만약 관계에 document.signature와 같은 메소드가 있다면 해당 메소드를 methods 섹션에 추가할 수 있습니다. 내보낸 값은 내보내기에 나타나고 가져오기에서 해당 값에 대해 작업할 수 있습니다. 예를 들어 가져온 값을 필드에 할당할 수 있습니다.

예를 들어 우리는 note_diff_file.diff_export의 반환 값을 메소드로 내보내고 가져오는 동안에는 해당 메소드의 내보낸 값으로 note_diff_file.diff를 설정합니다.

새 관계에 대한 테스트 커버리지 추가하기

직접 전송은 내부적으로 파일 기반 가져오기를 사용하므로 파일 기반 가져오기의 범위에서 새 관계에 대한 테스트 커버리지를 추가해야 합니다. 또한 직접 전송 가져오기의 내보내기 측면을 커버하는 새 관계에 대한 테스트를 추가해야 합니다. 다음 위치에 테스트를 추가하세요.

  1. spec/lib/gitlab/import_export/project/tree_saver_spec.rb. Group에 대해서도 유사한 파일이 있습니다.
  2. EE 전용 관계에 대해서는 ee/spec/lib/ee/gitlab/import_export/project/tree_saver_spec.rb에 추가하세요.

다른 관계의 예시를 따라서 새로운 테스트를 추가하세요.

추가된 관계가 예상대로 내보내는지 확인하기

import_export.yml에 지정된 새로 추가된 관계는 자동으로 디스크에 작성된 내보내기 파일에 추가되므로 추가 조치가 필요하지 않습니다.

관계가 추가되고 테스트가 추가된 후에는 관계가 내보내는지 수동으로 확인할 수 있습니다. 다음 두 곳에 자동으로 포함되어야 합니다.

  • 파일 기반 가져오기 및 내보내기. 프로젝트 내보내기 기능을 사용하여 내보내기, 다운로드 및 내보낸 데이터를 검사하세요.
  • 직접 전송 내보내기. export_relations API를 사용하여 내보내기, 다운로드하고 내보낸 관계를 검사하세요 (일괄적으로 내보낼 수 있습니다).

이진 관계 내보내기

이진 관계를 지원하는 경우:

  1. 디스크에서 내보내기를 수행하는 새로운 내보내기 서비스를 생성하세요. 예시는 BulkImports::LfsObjectsExportService를 참조하세요.
  2. file_relations 목록에 관계를 추가하세요 (https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/bulk_imports/file_transfer/project_config.rb).
  3. 관계를 BulkImports::FileExportService에 추가하세요.

예시

대상에서 가져오기

앞에서 언급한대로 직접 전송 가져오기에는 세 가지 유형의 관계가 있습니다.

  1. export_relations API에서 다운로드한 NDJSON으로 내보낸 관계. 예를 들어 documents.ndjson.gz.
  2. GraphQL API 관계. 예를 들어 members 정보는 GraphQL을 사용하여 그룹 및 프로젝트 사용자 멤버십을 가져옵니다.
  3. export_relations API에서 다운로드한 이진 관계. 예를 들어 lfs_objects.tar.gz.

직접 전송 가져오기는 추출/변환/로드 데이터 처리 기술에 기반하므로 관계를 가져오기 시작하려면 다음을 정의해야 합니다.

  • 새로운 관계 가져오기 파이프라인. 예를 들어, DocumentsPipeline.
  • 파이프라인이 데이터를 추출할 위치 및 방법을 알기 위한 데이터 추출기. 예를 들어, NdjsonPipeline.
  • 데이터를 필요한 형식으로 변환할 일련의 클래스 (변환기 목록).
  • 데이터를 어딘가에 영구적으로 저장할 로더. 예를 들어, 데이터베이스에 행을 저장하거나 새 LFS 객체를 만들기.

가져오는 관계의 유형에 관계없이 파이프라인 클래스 구조는 동일합니다:

module BulkImports
  module Common
    module Pipelines
      class DocumentsPipeline
        include Pipeline

        def extract(context)
          BulkImports::Pipeline::ExtractedData.new(data: file_paths)
        end

        def transform(context, object)
          ...
        end

        def load(context, object)
          document.save!
        end
      end
    end
  end
end

NDJSON에서 관계 가져오기

파이프라인 정의하기

이전 예시에서 documents 관계는 NDJSON 파일로 내보내면서, 이 경우에는 두 가지를 모두 사용할 수 있습니다:

  • NdjsonPipeline는 JSON에서 ActiveRecord 객체로의 자동 데이터 변환을 포함합니다 (이것은 파일 기반 가져오기를 내부적으로 사용합니다).
  • NdjsonExtractor/export_relations/download REST API 엔드포인트를 사용하여 소스 인스턴스에서 .ndjson.gz 파일을 다운로드합니다.

ETL 파이프라인의 각 단계는 메서드나 클래스로 정의할 수 있습니다.

  class DocumentsPipeline
    include NdjsonPipeline

    relation_name 'documents'

    extractor ::BulkImports::Common::Extractors::NdjsonExtractor, relation: relation
end

새 파이프라인은 이제 다음을 수행합니다.

  1. 소스 인스턴스에서 documents.ndjson.gz 파일을 다운로드합니다.
  2. NDJSON 파일의 내용을 읽고 JSON을 역직렬화하여 ActiveRecord 객체로 변환합니다.
  3. 프로젝트의 범위 내에서 데이터베이스에 저장합니다.

파이프라인은 공유되고 그룹 및 프로젝트 마이그레이션에 사용될 경우 BulkImports::Common::Pipelines 네임스페이스 아래에 배치할 수 있습니다. 예를 들어 LabelsPipeline은 공통 파이프라인이며 그룹 및 프로젝트 단계 목록에서 모두 참조됩니다.

프로젝트 이동에 파이프라인이 속하는 경우 BulkImports::Projects::Pipelines 네임스페이스에 배치할 수 있습니다.

그룹 이동에 파이프라인이 속하는 경우 BulkImports::Groups::Pipelines 네임스페이스에 배치할 수 있습니다.

새 파이프라인을 단계에 추가하기

직접 전송 가져오기는 그룹 및 프로젝트를 단계별로 마이그레이션합니다. 단계 목록은 다음 위치에 정의됩니다:

  • 프로젝트용: lib/bulk_imports/projects/stage.rb.
  • 그룹용: lib/bulk_imports/groups/stage.rb.

각 단계는 다음 요구 사항을 충족해야 합니다.

  • 병렬로 실행될 수 있는 여러 파이프라인을 가질 수 있습니다.
  • 다음 단계로 이동하기 전에 완전히 완료되어야 합니다.

프로젝트 단계에 파이프라인을 추가해봅시다.

module BulkImports
  module Projects
    class Stage < ::BulkImports::Stage
      private

       def config
        {
          project: {
            pipeline: BulkImports::Projects::Pipelines::ProjectPipeline,
            stage: 0
          },
          repository: {
            pipeline: BulkImports::Projects::Pipelines::RepositoryPipeline,
            maximum_source_version: '15.0.0',
            stage: 1
          },
          documents: {
            pipeline: BulkImports::Projects::Pipelines::DocumentsPipeline,
            minimum_source_version: '16.11.0',
            stage: 2
          }
       end
    end
  end
end

다음을 지정했습니다.

  • stage: 2, 따라서 프로젝트 및 리포지토리 단계가 먼저 완료되어야 파이프라인이 2단계에서 실행됩니다.
  • minimum_source_version: '16.11.0'. 이번 마일스톤에서 documents 관계를 내보내기에 소개했으므로 이전 GitLab 버전에는 이 관계가 없습니다. 따라서 소스 버전이 16.11 이상인 경우에만이 파이프라인이 실행됩니다.

참고: 관계가 폐기될 경우 특정 버전까지만 파이프라인을 실행해야하는 경우 maximum_source_version 속성을 지정할 수 있습니다.

테스트로 파이프라인 커버하기

이미 익스포트 측면을 테스트로 커버했기 때문에, 이제는 임포트 측면도 똑같이 해야 합니다. 직접 전송 임포터의 경우, 각 파이프라인에는 다음 예시와 비슷한 별도의 스펙 파일이 있을 것입니다.

예시

GraphQL API에서 관계를 가져오기

만약 당신의 관계가 GraphQL API를 통해 이용 가능하다면, GraphQlExtractor를 사용하여 파이프라인 클래스 내에서 변환 및 로딩을 할 수 있습니다.

MembersPipeline 예시:

module BulkImports
  module Common
    module Pipelines
      class MembersPipeline
        include Pipeline

        transformer Common::Transformers::ProhibitedAttributesTransformer
        transformer Common::Transformers::MemberAttributesTransformer

        def extract(context)
          graphql_extractor.extract(context)
        end

        def load(_context, data)
          ...

          member.save!
        end

        private

        def graphql_extractor
          @graphql_extractor ||= BulkImports::Common::Extractors::GraphqlExtractor
            .new(query: BulkImports::Common::Graphql::GetMembersQuery)
        end
      end
    end
  end
end

위의 과정은 앞서 설명한 과정과 동일합니다.

이진 관계 가져오기

이진 관계 파이프라인은 다른 파이프라인과 동일한 구조를 갖고 있으며, 추출/변환/로드 단계 동안에 어떤 일이 발생하는지를 정의하면 됩니다.

LfsObjectsPipeline 예시:

module BulkImports
  module Common
    module Pipelines
      class LfsObjectsPipeline
        include Pipeline

        file_extraction_pipeline!

        def extract(_context)
          download_service.execute
          decompression_service.execute
          extraction_service.execute

          ...
        end

        def load(_context, file_path)
          ...

          lfs_object.save!
        end
      end
    end
  end
end

데이터 다운로드를 돕기 위해 여러 개의 도우미 서비스 클래스가 있습니다:

  • BulkImports::FileDownloadService: 주어진 위치에서 파일을 다운로드합니다.
  • BulkImports::FileDecompressionService: 필수적인 유효성 검사를 포함하는 Gzip 압축 해제 서비스입니다.
  • BulkImports::ArchiveExtractionService: Tar 압축 해제 서비스입니다.