집계된 가치 스트림 분석

면책 조항: 이 페이지에는 예정된 제품, 기능 및 기능과 관련된 정보가 포함되어 있습니다. 제시된 정보는 정보 제공을 목적으로만 사용됨을 강조하는 것이 중요합니다. 구매 또는 계획 목적을 위해 이 정보에 의존해서는 안 됩니다. 어떠한 제품, 기능 또는 기능의 개발, 릴리스 및 타이밍은 변경되거나 지연될 수 있으며, 이는 GitLab Inc.의 단독 재량에 따라 남게 됩니다.

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

현재 상태

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

동기

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

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

이전 백엔드의 데이터베이스 쿼리는 코어 도메인 모델을 직접 사용하여 실행됩니다: (MergeRequestsFinderIssuesFinder). 날짜 범위 필터의 요청 된 변경으로 인해, 성능적인 측면에서 이러한 접근 방식은 더 이상 실현 가능하지 않았습니다.

집계된 VSA 백엔드의 이점:

  • 더 간단한 데이터베이스 쿼리(더 적은 JOIN).
  • 빠른 집계, 하나의 테이블에만 액세스.
  • 첫 페이지 로드 시간을 개선하기 위한 추가 집계를 도입할 수 있는 가능성.
  • 대규모 그룹(다수의 하위 그룹, 프로젝트, 이슈 및 병합 요청)에 대한 더 나은 성능.
  • 데이터베이스 분해를 위한 준비. VSA 관련 데이터베이스 테이블은 약간의 개발 노력으로 별도의 데이터베이스에 저장될 수 있습니다.
  • 데이터 내보내기에 유용한 키셋 페이징에 준비됨.
  • 더 복잡한 이벤트 정의를 구현할 수 있는 가능성.
    • 예를 들어, 시작 이벤트는 시스템에서 가장 이른 시간 값을 사용하는 두 타임스탬프 열일 수 있습니다.
    • 예: MIN(issues.created_at, issues.updated_at)

구성 예

VSA 객체 계층 구조 예시

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

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

예에서 각 가치 스트림 및 스테이지 항목은 데이터베이스에 유지됩니다. Deployment 스테이지가 두 가치 스트림에 대해 동일하게 사용되는 것에 주목하세요. 이는 기본 stage_event_hash_id가 두 스테이지 모두 같음을 의미합니다. stage_event_hash_id는 백엔드가 수집하는 데이터 양을 줄이고 데이터베이스 파티셔닝에 중대한 역할을 합니다.

가치 스트림 및 스테이지의 변경빈도가 적을 것으로 예상됩니다. 스테이지(시작 및 끝 이벤트)가 변경되면 집계된 데이터가 만료됩니다. 이는 매일 주기적 집계를 통해 해결됩니다.

기능 가용성

집계된 VSA 기능은 그룹 및 프로젝트 수준에서 사용할 수 있지만, 집계된 백엔드는 데이터 저장 및 데이터 계산 비용으로 인해 프리미엄 및 얼티메이트 고객에게만 제공됩니다. 정규화되고 집계 된 데이터를 저장하려면 상당한 디스크 공간이 필요합니다.

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

집계된 VSA 백엔드의 주요 아이디어는 분리입니다: VSA 데이터베이스 테이블 및 쿼리는 코어 도메인 모델(Issue, MergeRequest)을 직접 사용하지 않습니다. 이를 통해 VSA를 응용프로그램의 다른 부분과 독립적으로 확장하고 최적화할 수 있습니다.

아키텍처에는 두 가지 주요 메커니즘이 포함되어 있습니다.

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

데이터 로딩

VSA의 집계된 특성은 주기적 데이터 로딩에서 나옵니다. 시스템은 코어 도메인 모델을 쿼리하여 스테이지와 타임스탬프 데이터를 수집합니다. 이 데이터는 주기적으로 VSA 데이터베이스 테이블에 삽입됩니다.

프리미엄 또는 얼티메이트 라이센스를 보유한 각 최상위 네임스페이스에 대한 고수준 개요:

  1. 그룹 내 모든 스테이지를 로드합니다.
  2. 이슈 및 병합 요청 레코드를 반복합니다.
  3. 스테이지 구성(시작 및 끝 이벤트 식별자)에 기초하여 타임스탬프 데이터를 수집합니다.
  4. 수집한 데이터를 VSA 데이터베이스 테이블에 삽입하거나 업데이트합니다.

데이터 로딩은 Analytics::CycleAnalytics::DataLoaderService 클래스 내에서 구현되었습니다. 일부 그룹에는 많은 데이터가 포함되어 있어 주요 데이터베이스를 과부하하지 않도록 하기 위해, 서비스는 배치로 작업을 수행하고 엄격한 응용 프로그램 제한을 집행합니다.

  • 배치로 레코드를 로드합니다.
  • 배치로 레코드를 삽입합니다.
  • 한도에 도달하면 처리를 중단하고, 처리를 나중에 계속하기 위해 백그라운드 작업을 예약합니다.
  • 특정 지점부터 데이터 처리를 계속합니다.

데이터 로딩은 수동으로 수행됩니다. 기능이 준비되면 시스템에 의해 주기적으로 cron 작업을 통해 서비스가 호출됩니다(아직 구현되지 않은 부분입니다).

레코드 반복

배치 반복은 효율적인 IN 연산자를 사용하여 구현됩니다. 백그라운드 작업은 updated_atid 열을 기준으로 그룹 계층 구조에서 모든 이슈 및 병합 요청 레코드를 스캔합니다. 이미 집계된 그룹의 경우, DataLoaderService는 특정 지점부터 집계를 계속하여 시간을 절약합니다.

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

수집된 스테이지 이벤트 예시: 병합 요청 병합됨, 병합 요청 생성됨, 병합 요청 종료됨

타임스탬프 로딩에 대한 생성된 SQL 쿼리:

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 클래스는 자체를 스코프에 추가하여 타임스탬프 데이터를 로드하지만 행 수를 영향을 주지 않습니다(LEFT JOIN 사용). 이 동작은 StageEvent 클래스의 include_in 메서드로 구현되어 있습니다.

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

  • 병합 요청 생성됨 - 병합 요청 병합됨
  • 병합 요청 생성됨 - 병합 요청 종료됨

다른 조합도 가능하지만, 예를 들어 의미가 없는 조합은 방지합니다.

  • 병합 요청 병합됨 - 병합 요청 생성됨

생성 시간이 항상 먼저 발생하므로 이 스테이지는 항상 음수 기간을 보고합니다.

데이터 범위

데이터 수집은 그룹 계층 구조의 모든 이슈 및 병합 요청 레코드를 탑 레벨 그룹부터 시작하여 처리합니다. 이는 하위 그룹에 값 스트림이 하나만 있는 경우에도 이 그룹의 계층 구조 내 모든 이슈 및 병합 요청 데이터를 수집하는 것을 의미합니다. 이는 데이터 수집 메커니즘을 간소화하기 위한 것입니다. 더불어 데이터 조사에 따르면 대부분의 그룹 계층은 상위 수준에서 단계가 구성되어 있다는 것을 보여줍니다.

데이터 수집 과정에서 수집된 타임스탬프 데이터는 행으로 변환됩니다. 각 구성된 단계에 대해 시작 이벤트 타임스탬프가 있는 경우, 시스템은 이벤트 레코드를 하나 삽입하거나 업데이트합니다. 이를 통해 각 그룹 당 삽입된 행의 상한선을 결정할 수 있으며, 모든 이슈 및 병합 요청을 계산하여 합계에 단계 수를 곱함으로써 이루어집니다.

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

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

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

데이터베이스 구조

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

  • analytics_cycle_analytics_issue_stage_events
  • analytics_cycle_analytics_merge_request_stage_events

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

설명
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: 이슈 또는 병합 요청 레코드 목록.

필터

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

필터 이름 설명
milestone_title 백엔드는 milestone_id 필터로 변환합니다
author_username 백엔드는 author_id 필터로 변환합니다
project_ids 그룹 수준에서만 사용됨

예외: 이 필터는 다른 테이블에 적용되어 추가 조인이 필요합니다.

필터 이름 설명
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와 같은 데이터베이스 기술을 사용할 수 있습니다.