데이터베이스 사례 연구: 네임스페이스 스토리지 통계
GitLab에서 네임스페이스 스토리지 통계를 집계하고 업데이트하기 위해 시도한 여러 접근 방식과 최종 비동기 업데이트 솔루션을 설명합니다.
소개 # 그룹의 스토리지 및 한도 관리 에서, 우리는 그룹이 사용하는 스토리지 양을 쉽게 확인하고 간편하게 관리할 수 있는 방법을 제공하고자 합니다. 제안 # 네임스페이스의 통계를 집계된 형태로 보관하는 새로운 ActiveRecord 모델을 생성합니다(루트 네임스페이스 전용). 이 네임스페이스에 속하는 프로젝트가 변경될 때마다 이 모델의 통계를 새로 고칩니다. 문제 # GitLab에서는 프로젝트가 저장될 때마다 콜백 을 통해 프로젝트 스토리지 통계를 업데이트합니다. 네임스페이스별 통계 요약은 Namespaces#with_statistics 스코프를 통해 조회됩니다. 이 쿼리를 분석한 결과: 15k 개 이상의 프로젝트가 있는 네임스페이스에서 최대 1.2 초가 소요됩니다. 타임아웃이 발생하기 때문에 ChatOps 로 분석할 수 없습니다. 또한, 프로젝트 통계를 업데이트하는 데 현재 사용 중인 패턴(콜백)은 적절히 확장되지 않습니다. 현재 이 패턴은 전체적으로 가장 많은 시간이 소요되는 프로덕션의 대규모 데이터베이스 쿼리 트랜잭션 중 하나입니다. 트랜잭션 길이가 증가하므로 쿼리를 추가할 수 없습니다. 위의 모든 이유로 인해, namespaces 테이블이 GitLab.com에서 가장 큰 테이블 중 하나이기 때문에 네임스페이스 통계를 저장하고 업데이트하는 데 동일한 패턴을 적용할 수 없습니다. 따라서 성능이 우수한 대안적인 방법을 찾아야 했습니다. 시도 # 시도 A: PostgreSQL 구체화된 뷰 # 구체화된 뷰(materialized view) 와 프로젝트 라우트 SQL에 기반한 새로 고침 전략을 통해 모델을 업데이트할 수 있습니다: SELECT split_part("rs".path, '/', 1) as root_path, COALESCE(SUM(ps.storage_size), 0) AS storage_size, COALESCE(SUM(ps.repository_size), 0) AS repository_size, COALESCE(SUM(ps.wiki_size), 0) AS wiki_size, COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size, COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size, COALESCE(SUM(ps.pipeline_artifacts_size), 0) AS pipeline_artifacts_size, COALESCE(SUM(ps.packages_size), 0) AS packages_size, COALESCE(SUM(ps.snippets_size), 0) AS snippets_size, COALESCE(SUM(ps.uploads_size), 0) AS uploads_size FROM "projects" INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project' INNER JOIN project_statistics p