집계된 가치 스트림 분석

면책 성명: 본 페이지에는 예정된 제품, 기능 및 기능과 관련된 정보가 포함되어 있습니다. 제시된 정보는 정보 제공을 목적으로 합니다. 구매 또는 계획 목적으로 이 정보를 신뢰해서는 안 됩니다. 제품, 기능 또는 기능의 개발, 출시 및 타이밍은 변경되거나 지연될 수 있으며 GitLab Inc.의 전적 재량에 따라 달려 있습니다.

이 페이지는 가치 스트림 분석 (VSA)에 대한 집계된 백엔드의 고수준 개요를 제공합니다.

현재 상태

집계된 백엔드는 그룹 수준에서 GitLab 15.0 이후로 기본적으로 사용됩니다.

동기

집계된 백엔드는 VSA 기능의 성능 제한을 해결하고 장기 성장을 위해 설정하는 것을 목표로 합니다.

주요 데이터베이스는 분석 워크로드에 대비되어 있지 않습니다. 장기 실행되는 쿼리를 실행하면 응용프로그램의 신뢰성에 영향을 미칠 수 있습니다. 대규모 그룹의 경우 현재 구현(이전 백엔드)은 느리며 경우에 따라 구성된 문장 시간 제한 (15초) 때문에 로드되지 않을 수도 있습니다.

이전 백엔드의 데이터베이스 쿼리는 IssuableFinders 클래스를 통해 핵심 도메인 모델을 직접 사용합니다: (MergeRequestsFinderIssuesFinder). 날짜 범위 필터의 요청된 변경으로 인해 성능적으로는 더 이상 실현 가능하지 않았습니다.

집계된 VSA 백엔드의 이점:

  • 더 간단한 데이터베이스 쿼리 (더 적은 JOIN).
  • 빠른 집계, 단일 테이블만 액세스됩니다.
  • 첫 번째 페이지 로드 시간을 개선하기 위해 추가 집계를 도입할 수 있는 가능성.
  • 대규모 그룹에 대한 더 나은 성능 (다수의 서브그룹, 프로젝트, 이슈, Merge Request).
  • 데이터베이스 분해 준비. VSA 관련 데이터베이스 테이블은 최소한의 개발 노력으로 별도의 데이터베이스에 저장할 수 있습니다.
  • 데이터 내보내기에 유용한 키셋 페이지네이션 준비.
  • 더 복잡한 이벤트 정의를 구현할 수 있는 가능성.
    • 예: 시작 이벤트는 시스템에서 가장 초기 값이 사용되는 두 개의 타임스탬프 열일 수 있습니다.
    • 예: MIN(이슈 생성일, 이슈 업데이트일)

구성 예시

VSA 객체 계층 구조 예시

이 예에서 두 개의 독립적인 가치 스트림이 Test그룹(최상위 네임스페이스) 내에서 서로 다른 개발 워크플로우를 사용하는 두 팀을 위해 설정됩니다.

첫 번째 가치 스트림은 단계를 정의하기 위해 표준 타임스탬프 기반 이벤트를 사용합니다. 두 번째 가치 스트림은 레이블 이벤트를 사용합니다.

예시의 각 가치 스트림 및 단계 항목은 데이터베이스에 유지됩니다. 배포 단계가 두 가치 스트림 모두에 대해 동일한 것임에 유의하십시오. 이는 백그라운드가 수집하는 데이터 양을 줄이고 데이터베이스 분할에서 중요한 역할을 합니다.

가치 스트림 및 단계가 드물게 변경될 것으로 예상됩니다. 단계(시작 및 종료 이벤트)가 변경되면 집계된 데이터가 오래되어집니다. 이는 일반적으로 매일 일어나는 집계로 해결됩니다.

기능 가용성

집계된 VSA 기능은 그룹 및 프로젝트 수준에서 사용할 수 있지만, 집계된 백엔드는 데이터 저장 및 데이터 계산 비용 때문에 Premium 및 Ultimate 고객에게만 제공됩니다. 비정규화된 집계 데이터의 저장에는 상당한 디스크 공간이 필요합니다.

집계된 가치 스트림 분석 아키텍처

집계된 VSA 백엔드의 주요 아이디어는 분리입니다: VSA 데이터베이스 테이블 및 쿼리에서는 핵심 도메인 모델 (이슈, Merge Request)을 직접 사용하지 않습니다. 이를 통해 애플리케이션의 다른 부분과 독립적으로 VSA를 확장하고 최적화할 수 있습니다.

아키텍처는 두 가지 주요 메커니즘으로 구성됩니다.

  • 주기적인 데이터 수집 및 적재 (백그라운드에서 발생).
  • 수집된 데이터 쿼리 (사용자에 의해 호출).

데이터 적재

VSA의 집계된 특성은 주기적인 데이터 적재에서 나오는 것입니다. 시스템은 핵심 도메인 모델을 쿼리하여 단계 및 타임스탬프 데이터를 수집합니다. 이 데이터는 주기적으로 VSA 데이터베이스 테이블에 삽입됩니다.

Premium 또는 Ultimate 라이선스가 있는 각 최상위 네임스페이스에 대한 고수준 개요:

  1. 그룹 내의 모든 단계를 적재합니다.
  2. 이슈 및 Merge Request 레코드를 반복합니다.
  3. 단계 구성(시작 및 종료 이벤트 식별자)에 따라 타임스탬프 데이터를 수집합니다.
  4. VSA 데이터베이스 테이블에 데이터를 INSERT 또는 UPDATE합니다.

데이터 적재는 매뉴얼으로 이루어집니다. 기능이 준비되면 시스템에 의해 일정된 후크론 작업을 통해 주기적으로 서비스가 호출됩니다 (이 부분은 아직 구현되지 않았습니다).

레코드 반복

일괄적인 반복은 효율적인 IN 연산자와 함께 구현됩니다. 백그라운드 작업은 updated_atid 열에 따라 그룹 계층구조에서 모든 이슈 및 Merge Request 레코드를 스캔합니다. 이미 집계된 그룹의 경우, DataLoaderService는 특정 지점부터 집계를 계속하므로 시간이 절약됩니다.

타임스탬프 데이터 수집은 각 반복에서 수행됩니다. DataLoaderService는 그룹 계층구조 내에서 구성된 단계 이벤트를 결정하고 필요한 타임스탬프를 선택하는 쿼리를 작성합니다. 단계 레코드는 구성된 이벤트 및 이벤트의 타임스탬프 열 선택 방법을 알고 있습니다.

수집된 단계 이벤트의 쿼리:

SELECT
  -- 구성된 단계에 따라 열 디렉터리이 다릅니다
  "merge_request_metrics"."merged_at",
  "merge_requests"."created_at",
  "merge_request_metrics"."latest_closed_at"
  FROM "merge_requests"
  LEFT OUTER JOIN "merge_request_metrics" ON "merge_request_metrics"."merge_request_id" = "merge_requests"."id"
  WHERE "merge_requests"."id" IN (1, 2, 3, 4) -- ID는 일괄 쿼리에서 가져옵니다.

merged_at 열은 별도 테이블에 위치합니다 (merge_request_metrics). Gitlab::Analytics::CycleAnalytics::StagEvents::MergeRequestMerged 클래스가 자체를 로드하도록 스코프에 추가되어있는 타임스탬프 데이터를 처리합니다. 각 StageEvent 클래스에 대해 include_in 메서드를 사용하여 이러한 동작이 구현됩니다.

데이터 수집 쿼리는 이벤트 수준에서 동작합니다. 이는 라인의 이벤트 타임스탬프를 추출하고 동일한 데이터를 여러 번 수집하지 않도록 합니다. 위에서 언급된 이벤트는 다음과 같은 단계 구성에서 나올 수 있습니다:

  • Merge Request 생성 - Merge Request Merge
  • Merge Request 생성 - Merge Request 종료

다른 조합 또한 가능하지만, 말이 되지 않는 조합을 방지합니다. 예를 들어:

  • Merge Request Merge - Merge Request 생성

생성 시간이 항상 우선되므로 이 단계는 항상 음수 기간을 보고합니다.

데이터 범위

데이터 수집은 그룹 계층구조의 모든 이슈 및 Merge Request 레코드를 스캔하고 처리합니다. 이는 하위 그룹에서 하나의 값 스트림만 있는 경우에도 해당 그룹의 계층구조의 모든 이슈 및 Merge Request 데이터를 수집합니다. 이는 데이터 수집 메카니즘을 간단하게 만들기 위한 목적입니다. 또한 데이터 연구에 따르면 대부분의 그룹 계층구조는 최상위 수준에서 단계가 구성되어 있습니다.

데이터 수집 프로세스 중에 수집된 타임스탬프 데이터가 행으로 변환됩니다. 각 구성된 단계에 대해 시작 이벤트 타임스탬프가 있는 경우 시스템은 하나의 이벤트 레코드를 삽입 또는 업데이트합니다. 이를 통해 각 단계 당 삽입된 행의 상한선을 그룹당 이슈 및 Merge Request의 모든 것을 계산하고 합계에 단계 수를 곱해 구합니다.

데이터 일관성에 대한 고려사항

데이터 수집의 비동기적인 성격으로 인해 데이터 일관성 문제가 발생할 수 있습니다. 이는 쿼리 성능을 상당히 향상시키는 것과 관련이 있습니다. 분석 워크로드에 대해서는 데이터의 약간의 지연이 허용될 수 있다고 생각합니다.

롤아웃 전에 VSA 페이지에 몇 가지 지표를 구현하여 최근의 백엔드 활동을 표시할 계획입니다. 예를 들어, 마지막 데이터 수집 타임스탬프와 마지막 일관성 확인 타임스탬프를 표시하는 지표가 있을 것입니다.

데이터베이스 구조

VSA는 다음 도메인 모델에 대한 데이터를 수집합니다: IssueMergeRequest. 집계된 데이터를 분리하기 위해 두 가지 추가 데이터베이스 테이블을 사용합니다:

  • analytics_cycle_analytics_issue_stage_events
  • analytics_cycle_analytics_merge_request_stage_events

두 테이블 모두 stage_event_hash_id에 의해 해시로 분할됩니다. 각 테이블은 32개의 파티션을 사용합니다. 이는 임의의 숫자이며 변경할 수 있습니다. 중요한 점은 파티션 크기를 100GB 미만으로 유지하는 것입니다 (이는 기능에 많은 여유 공간을 제공합니다).

Column Description
stage_event_hash_id 분할 키
merge_request_id 또는 issue_id 도메인 레코드 (Issuable)에 대한 참조
group_id 그룹에 대한 참조 (비정규화)
project_id 프로젝트에 대한 참조
milestone_id 도메인 레코드 테이블로부터 복제된 데이터
author_id 도메인 레코드 테이블로부터 복제된 데이터
state_id 도메인 레코드 테이블로부터 복제된 데이터
start_event_timestamp 스테이지 구성에서 파생된 타임스탬프
end_event_timestamp 스테이지 구성에서 파생된 타임스탬프

데이터 분리 요구사항을 준수하기 위해 테이블에는 외래 키가 없습니다. 데이터 일관성은 백그라운드 작업(최종적으로 일관성 유지)에 의해 보장됩니다.

데이터 질의

기본 쿼리에는 항상 다음 필터가 포함됩니다:

  • stage_event_hash_id - 분할 키
  • project_id 또는 group_id - 프로젝트 또는 그룹 수준 쿼리인지에 따라 다름
  • end_event_timestamp - 날짜 범위 필터 (지난 30일간)

예: GitLab 프로젝트의 리뷰 스테이지 기간 선택

SELECT end_event_timestamp - start_event_timestamp
FROM analytics_cycle_analytics_merge_request_stage_events
WHERE
stage_event_hash_id = 16 AND -- 특정 파티션을 사용
project_id = 278964 AND
end_event_timestamp > '2022-01-01' AND end_event_timestamp < '2022-01-30'

쿼리 생성

쿼리 백엔드는 이전 백엔드 구현이 사용하는 동일한 인터페이스 뒤에 숨겨져 있습니다. 따라서 우리는 쉽게 이전 및 새로운 쿼리 백엔드 간에 전환할 수 있습니다.

  • DataCollector: VSA 데이터를 쿼리하는 진입점
    • BaseQueryBuilder: 기본 ActiveRecord 스코프를 제공합니다(여기에 필터가 적용됨).
    • average: 평균 집계
    • median: 중간값 집계
    • count: 행 카운팅
    • records: 이슈 또는 Merge Request 레코드 디렉터리

필터

VSA는 기본 쿼리에 다양한 필터를 지원합니다. 대부분의 필터는 추가적인 JOIN이 필요하지 않습니다:

Filter name Description
milestone_title 백엔드에서 이를 milestone_id 필터로 변환함
author_username 백엔드에서 이를 author_id 필터로 변환함
project_ids 그룹 수준에서만 사용됨

예외: 이러한 필터는 다른 테이블에 적용되기 때문에 우리는 JOIN합니다.

Filter name Description
label_name 배열 필터, label_links 테이블 사용
assignee_username 배열 필터, *_assignees 테이블 사용

데이터베이스를 완전히 분해하려면 필요한 ID 값이 VSA 데이터베이스 테이블에 복제되어야 합니다. 이 변경은 배열 열을 사용하여 구현할 수 있습니다.

엔드포인트

이 기능은 데이터를 프론트엔드로 전달하기 위해 개인용 JSON API를 사용합니다. 첫 번째 페이지 로드 때 다음 요청이 호출됩니다:

  • 대부분 비어 있는 초기 HTML 페이지 로드. 일부 구성 데이터는 data 속성을 통해 노출됩니다.
  • value_streams - 주어진 그룹의 사용 가능한 가치 스트림 로드
  • stages - 현재 선택된 가치 스트림에 대한 스테이지 로드
  • median - 각 스테이지에 대해 중간 기간을 요청
  • count - 각 스테이지에 대해 항목 수를 요청 (이는 제한된 카운트입니다, 최대 1000행)
  • average_duration_chart - 기간 차트의 데이터
  • summary, time_summary - 최상위 집계, 대부분의 지표는 서로 다른 API/파인더를 사용하고 집계된 백엔드를 호출하지 않습니다.

특정 스테이지를 선택할 때 records 엔드포인트가 호출되며, 선택한 스테이지에 대한 관련 레코드(페이지별)가 특정 순서로 반환됩니다.

데이터베이스 분해

쿼리 로직을 주 애플리케이션 코드에서 분리함으로써, 이 기능은 데이터베이스 분해에 준비되어 있습니다. VSA가 별도의 데이터베이스 인스턴스가 필요하다고 결정한다면, 집계된 테이블을 이동하는 것이 쉽게 수행될 수 있습니다.

이 기능의 성능을 더욱 향상시키기 위해 다른 데이터베이스 기술을 사용할 수도 있습니다. 예를 들어 Timescale DB와 같은 것이 있습니다.