직접 전송 가져오기에 새로운 관계 추가하기

전반적으로 직접 전송 가져오기에 새로운 관계를 추가하려면 다음을 수행해야 합니다:

  1. 내보낸 데이터 디렉터리에 새 관계 추가.
  2. 데이터 처리 지시 사항이 있는 가져오기 단계에 새 ETL(추출/변환/로드) 파이프라인 추가.
  3. 새로 만든 파이프라인을 가져오기 단계 디렉터리에 추가.
  4. 충분한 테스트 커버리지 보장.

원본에서 내보내기

우리가 내보내는 몇 가지 관계 유형이 있습니다:

  • ActiveRecord 연결. import_export.yml 파일에서 읽은 후 JSON으로 직렬화하여 NDJSON 파일에 기록됩니다. 각 관계는 .gz 파일에 내보내어집니다. 컬렉션의 경우 .tar.gz 파일로 업로드되어 GitLab 대상 인스턴스의 REST API를 사용하여 내보내고 가져오기 위해 서비스됩니다.
  • 바이너리 파일. 예를 들어, 업로드나 LFS 객체.
  • 내보내지는 관계는 아니지만 가져오는 동안 GraphQL API에서 직접 읽히는 소수의 관계.

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

ActiveRecord 관계 내보내기

직접 전송 가져오기의 기본 동작은 파일 기반 가져오기를 많이 기반으로 하며, 이는 내보내기에 포함될 Project 연결 디렉터리을 설명하는 import_export.yml 파일을 사용합니다. Group에 대한 유사한 import_export.yml도 사용할 수 있습니다.

예를 들어 새로운 Project 연결인 documents가 있다고 가정해 봅시다. 이 새로운 연결을 가져오기를 지원하려면 다음을 해야 합니다:

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

import_export.yml 파일에 추가

note
이 파일에 나열된 관계들은 위에서 아래로 가져 오짐. 순서에 의존하는 연결이 있는 경우, 해당 연결을 필요로하는 연결들 앞에 의존성을 두어야 합니다. 예를 들어, documents는 Merge Request보다 먼저 가져와야 하며, 그렇지 않으면 유효하지 않습니다.
  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
    
    note
    연결이 기업용 전용 기능과 관련이 있다면 파일 끝에 해당 기업용 파일에만 내보내고 가져오도록하기 위해 ee.tree.project 트리에 추가합니다.

    연결에 하위 관계를 포함할 필요가 없는 경우 여기까지입니다. 그러나 더 많은 하위 관계가 필요한 경우(예: 노트), 해당 관계를 나열해야 합니다. documents에서 award emojis(노트의 어워드 이모지 포함) 및 award emojis(문서의 어워드 이모지 포함)가 있을 수 있다고 가정해 봅시다. 이 경우 우리의 관계는 다음과 같습니다:

    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 디렉터리에 나열되지 않은 관계 속성은 내보내기와 가져오기 양쪽에서 필터링됩니다. 필요한 속성을 포함하려면 다음과 같이 디렉터리에 추가해야 합니다:

    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를 포함하는 것(단, custom_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/spec/lib/ee/gitlab/import_export/project/tree_saver_spec.rb을 확인합니다.

새로운 테스트를 추가하기 위해 다른 관계 예시를 따릅니다.

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

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

관계가 추가되고 테스트도 추가되면 관계가 내보내는 것을 매뉴얼으로 확인할 수 있습니다. 관계는 자동으로 다음 두 곳에 모두 포함되어야 합니다:

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

이진 관계 내보내기

이진 관계를 지원하려면:

  1. 디스크에서 내보내기를 수행하는 새로운 내보내기 서비스를 생성합니다. 예시는 BulkImports::LfsObjectsExportService를 참조하세요.
  2. list of file_relations에 관계를 추가합니다.
  3. 관계를 BulkImports::FileExportService에 추가합니다.

예시

대상지에서 가져오기

앞서 언급했듯이, 직접 전송 가져오기에는 세 가지 유형의 관계가 있습니다.

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

직접 전송 가져오기 프로그램은 Extract/Transform/Load 데이터 처리 기술에 기반하므로, 관계를 가져오기 시작하려면 다음을 정의해야 합니다:

  • 새 관계 가져오기 파이프라인. 예를 들어, 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 네임스페이스.

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

직접 전송 가져오기 프로그램은 그룹 및 프로젝트를 단계별로 마이그레이션 실행합니다. 각 단계는 다음 단계로 이동하기 전에 완전히 완료되어야 합니다.

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

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 이상일 때만이 파이프라인이 실행됩니다.
note
관계가 폐기되고 특정 버전까지만 파이프라인을 실행해야 하는 경우 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 압축 해제 서비스입니다.