집계된 가치 흐름 분석
면책 조항:
이 페이지는 향후 제품, 기능 및 기능과 관련된 정보를 포함하고 있습니다.
제시된 정보는 정보 제공의 목적만을 위한 것임을 유의하시기 바랍니다.
구매 또는 계획의 목적으로 이 정보를 의존하지 마십시오.
제품, 기능 또는 기능의 개발, 릴리즈 및 타이밍은 변경되거나 지연될 수 있으며 GitLab Inc.의 단독 재량에 따릅니다.
이 페이지는 가치 흐름 분석(Value Stream Analytics, VSA)을 위한 집계된 백엔드에 대한 고급 개요를 제공합니다.
현재 상태
집계된 백엔드는 GitLab 15.0부터 그룹 수준에서 기본적으로 사용됩니다.
동기
집계된 백엔드는 VSA 기능의 성능 제한 문제를 해결하고 장기적인 성장을 위해 설정하는 것을 목표로 합니다.
우리의 주요 데이터베이스는 분석 작업을 준비하지 않았습니다. 장기간 실행되는 쿼리를 실행하면 애플리케이션의 신뢰성에 영향을 줄 수 있습니다. 대규모 그룹의 경우 현재 구현(구 백엔드)은 느리고, 경우에 따라 구성된 쿼리 시간 초과(15초) 때문에 로드조차 되지 않습니다.
구 백엔드의 데이터베이스 쿼리는 IssuableFinders
클래스를 통해 코어 도메인 모델을 직접 사용합니다: (MergeRequestsFinder 및 IssuesFinder).
날짜 범위 필터의 요청된 변경으로 인해 이 접근 방식은 성능 관점에서 더 이상 실행 가능하지 않았습니다.
집계된 VSA 백엔드의 이점:
- 더 간단한 데이터베이스 쿼리(조인 수가 적음).
- 더 빠른 집계, 단일 테이블만 액세스됨.
- 첫 페이지 로드 시간을 개선하기 위해 추가 집계를 도입할 가능성.
- 대규모 그룹(많은 하위 그룹, 프로젝트, 이슈 및 병합 요청)의 더 나은 성능.
- 데이터베이스 분할에 대비됨. VSA 관련 데이터베이스 테이블은 최소한의 개발 노력으로 별도의 데이터베이스에서 운영될 수 있습니다.
- 데이터 내보내기에 유용할 수 있는 키셋 페이징에 대비됨.
- 더 복잡한 이벤트 정의를 구현할 가능성.
- 예를 들어, 시작 이벤트는 가장 이른 값이 시스템에 의해 사용될 두 개의 타임스탬프 열일 수 있음.
- 예제:
MIN(issues.created_at, issues.updated_at)
예제 구성
이 예제에서는 Test Group
(최상위 네임스페이스) 내에서 서로 다른 개발 워크플로를 사용하는 두 팀을 위해 두 개의 독립적인 가치 흐름이 설정되어 있습니다.
첫 번째 가치 흐름은 단일 타임스탬프 기반 이벤트를 사용하여 단계를 정의합니다. 두 번째 가치 흐름은 레이블 이벤트를 사용합니다.
예제의 각 가치 흐름 및 단계 항목은 데이터베이스에 지속됩니다. 두 가치 흐름 모두에 대해 배포
단계가 동일하다는 점에 유의하십시오. 이는 기본적인 stage_event_hash_id
가 두 단계 모두에 대해 동일함을 의미합니다. stage_event_hash_id
는 백엔드에서 수집하는 데이터 양을 줄이고 데이터베이스 분할에서 중요한 역할을 합니다.
우리는 가치 흐름과 단계가 드물게 변경될 것으로 예상합니다. 단계(시작 및 종료 이벤트)가 변경되면 집계된 데이터가 오래된 것입니다. 이는 매일 발생하는 주기적인 집계로 수정됩니다.
기능 가용성
집계된 VSA 기능은 그룹 및 프로젝트 수준에서 사용 가능하지만, 집계된 백엔드는 데이터 저장 및 데이터 계산 비용으로 인해 Premium 및 Ultimate 고객에게만 제공됩니다. 비정규화된 집계 데이터를 저장하는 데는 상당한 디스크 공간이 필요합니다.
집계된 가치 흐름 분석 아키텍처
집계된 VSA 백엔드의 주요 아이디어는 분리입니다: VSA 데이터베이스 테이블과 쿼리는 핵심 도메인 모델(Issue, MergeRequest)을 직접 사용하지 않습니다. 이를 통해 VSA를 애플리케이션의 다른 부분과 독립적으로 확장하고 최적화할 수 있습니다.
아키텍처는 두 가지 주요 메커니즘으로 구성됩니다:
- 주기적인 데이터 수집 및 로딩(백그라운드에서 발생).
- 수집된 데이터 쿼리(사용자에 의해 호출됨).
데이터 로딩
VSA의 집계된 성격은 주기적인 데이터 로딩에서 비롯됩니다. 시스템은 핵심 도메인 모델을 쿼리하여 단계 및 타임스탬프 데이터를 수집합니다. 이 데이터는 정기적으로 VSA 데이터베이스 테이블에 삽입됩니다.
프리미엄 또는 궁극적 라이센스를 가진 각 최상위 네임스페이스에 대한 고수준 개요:
- 그룹의 모든 단계를 로드합니다.
- 이슈 및 머지 요청 레코드를 반복합니다.
- 단계 구성(시작 및 종료 이벤트 식별자)에 따라 타임스탬프 데이터를 수집합니다.
-
INSERT
또는UPDATE
데이터를 VSA 데이터베이스 테이블에 삽입합니다.
데이터 로딩은 Analytics::CycleAnalytics::DataLoaderService
클래스 내에서 구현됩니다. 일부 그룹은 많은 데이터를 포함하고 있어 주요 데이터베이스에 과부하를 방지하기 위해 서비스는 배치 작업을 수행하고 엄격한 애플리케이션 한계를 enforced 합니다:
- 레코드를 배치로 로드합니다.
- 레코드를 배치로 삽입합니다.
- 한계에 도달하면 처리를 중지하고, 이후 처리를 계속하기 위해 백그라운드 작업을 스케줄합니다.
- 특정 지점부터 데이터를 계속 처리합니다.
데이터 로딩은 수동으로 수행됩니다. 기능이 준비되면 시스템에 의해 주기적으로 서비스가 호출됩니다(이 부분은 아직 구현되지 않았습니다).
레코드 반복
배치 반복은 효율적인 IN 연산자로 구현됩니다. 백그라운드 작업은 그룹 계층에서 updated_at
및 id
열에 따라 정렬된 모든 이슈 및 머지 요청 레코드를 스캔합니다. 이미 집계된 그룹의 경우, 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) -- ids는 배치 쿼리에서 나옵니다.
merged_at
열은 별도의 테이블(merge_request_metrics
)에 위치합니다. Gitlab::Analytics::CycleAnalytics::StagEvents::MergeRequestMerged
클래스는 타임스탬프 데이터를 로드하기 위한 범위에 자기를 추가하되, 행 수에 영향을 주지 않도록(LEFT JOIN
사용)합니다. 이 동작은 include_in
메서드를 가진 각 StageEvent
클래스에 대해 구현됩니다.
데이터 수집 쿼리는 이벤트 수준에서 작동합니다. 단계에서 이벤트 타임스탬프를 추출하고 같은 데이터를 여러 번 수집하지 않도록 보장합니다. 위에서 언급한 이벤트는 다음과 같은 단계 구성에서 올 수 있습니다:
- 머지 요청 생성됨 - 머지 요청 머지됨
- 머지 요청 생성됨 - 머지 요청 닫힘
다른 조합도 가능할 수 있지만, 의미가 없는 조합은 방지합니다. 예를 들어:
- 머지 요청 머지됨 - 머지 요청 생성됨
생성 시간은 항상 먼저 발생하므로 이 단계는 항상 부정적인 지속 시간을 보고합니다.
데이터 범위
데이터 수집은 상위 그룹부터 시작하여 그룹 계층의 모든 문제 및 병합 요청 레코드를 스캔하고 처리합니다. 이는 하위 그룹에 하나의 가치 흐름만 있는 경우에도 이 그룹의 계층에 있는 모든 문제 및 병합 요청의 데이터를 수집한다는 것을 의미합니다. 이는 데이터 수집 메커니즘을 단순화하는 것을 목표로 합니다. 게다가, 데이터 연구에 따르면 대부분의 그룹 계층은 상위 수준에서 단계가 구성되어 있습니다.
데이터 수집 과정에서 수집된 타임스탬프 데이터는 행으로 변환됩니다. 구성된 각 단계에 대해, 시작 이벤트 타임스탬프가 존재하면 시스템은 하나의 이벤트 레코드를 삽입하거나 업데이트합니다. 이를 통해 모든 문제 및 병합 요청의 수를 계산하고 합계를 단계 수로 곱함으로써 그룹당 삽입된 행의 상한선을 결정할 수 있습니다.
데이터 일관성 문제
비동기적 데이터 수집의 특성으로 인해 데이터 일관성 문제가 발생할 수 있습니다. 이는 쿼리 성능을 훨씬 더 빠르게 만드는 대가입니다. 우리는 분석 작업에서 데이터의 약간의 지연은 받아들일 수 있다고 생각합니다.
배포 전에 VSA 페이지에 최근 백엔드 활동을 보여주는 몇 가지 지표를 구현할 계획입니다. 예를 들어, 마지막 데이터 수집 타임스탬프와 마지막 일관성 점검 타임스탬프를 보여주는 지표가 있습니다.
데이터베이스 구조
VSA는 다음 도메인 모델에 대한 데이터를 수집합니다: Issue
및 MergeRequest
. 집계된 데이터를 분리하기 위해 두 개의 추가 데이터베이스 테이블을 사용합니다:
analytics_cycle_analytics_issue_stage_events
analytics_cycle_analytics_merge_request_stage_events
두 테이블 모두 stage_event_hash_id
로 해시 파티션화되어 있습니다. 각 테이블은 32개의 파티션을 사용합니다. 이는 임의의 숫자이며 변경될 수 있습니다. 중요한 것은 각 파티션의 크기가 100 GB를 초과하지 않도록 하는 것입니다 (이로 인해 기능에 많은 여유가 생깁니다).
열 | 설명 |
---|---|
stage_event_hash_id |
파티셔닝 키 |
merge_request_id 또는 issue_id
|
도메인 레코드(발행 가능)에 대한 참조 |
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 |
그룹 수준에서만 사용됩니다. |
예외: 이러한 필터는 다른 테이블에 적용되므로 JOIN
합니다.
필터 이름 | 설명 |
---|---|
label_name |
배열 필터, label_links 테이블을 사용합니다. |
assignee_username |
배열 필터, *_assignees 테이블을 사용합니다. |
데이터베이스를 완전히 분해하려면 필요한 ID 값을 VSA 데이터베이스 테이블에 복제해야 합니다. 이 변경은 배열 열을 사용하여 구현할 수 있습니다.
엔드포인트
이 기능은 프론트엔드에 데이터를 전달하기 위해 개인 JSON API를 사용합니다. 첫 페이지 로드 시
다음 요청이 호출됩니다:
- 대부분 비어 있는 초기 HTML 페이지 로드. 일부 구성 데이터는
data
속성을 통해 노출됩니다. -
value_streams
- 지정된 그룹에 대한 사용 가능한 value streams를 로드합니다. -
stages
- 현재 선택된 value stream에 대한 단계를 로드합니다. -
median
- 각 단계에 대해 중간 지속 시간을 요청합니다. -
count
- 각 단계에 대해 단계 내 항목 수를 요청합니다 (이는 제한 수이며, 최대 1000행입니다). -
average_duration_chart
- 지속 시간 차트에 대한 데이터. -
summary
,time_summary
- 상위 수준 집계, 대부분의 메트릭은 서로 다른 API/ 파인더를 사용하고 집계된 백엔드를 호출하지 않습니다.
특정 단계를 선택할 때, records
엔드포인트가 호출되며, 선택한 단계에 대한 관련
레코드(페이지네이션된)를 특정 순서로 반환합니다.
데이터베이스 분해
쿼리 로직을 주요 애플리케이션 코드와 분리함으로써, 이 기능은 데이터베이스 분해를 위해 준비되었습니다. VSA가 별도의 데이터베이스 인스턴스를 필요로 한다고 결정하면, 집계된 테이블을 이동하는 것은 큰 노력 없이도 수행될 수 있습니다.
성능을 더욱 향상시키기 위해 다른 데이터베이스 기술을 사용할 수도 있으며, 예를 들어 Timescale DB와 같은 기술이 있습니다.