Husky: Datadog 규모의 효율적인 컴팩션 (새 탭에서 열림)
Husky는 대규모 관측(observability) 데이터를 처리하기 위해 객체 스토리지 위에 구축된 분산 저장 시스템으로, 매일 수조 개의 이벤트를 효율적으로 관리하는 데 최적화되어 있습니다. 이 시스템은 데이터를 '조각(fragment)' 단위로 저장하고 컴팩션(compaction) 과정을 통해 쿼리 성능과 스토리지 비용 사이의 최적의 균형을 맞추는 것을 핵심 전략으로 삼습니다. 특히 파운데이션DB(FoundationDB)를 활용한 원자적 메타데이터 관리와 병렬 워커 기반의 스캔 구조를 통해 데이터 가용성을 유지하면서도 대규모 분석 쿼리를 신속하게 처리합니다. ## Husky의 쿼리 실행 및 조각화 구조 * Husky는 유입된 이벤트를 조각(fragment)이라 불리는 파일로 묶어 객체 스토리지(S3, GCS 등)에 저장하며, 각 조각에 대한 메타데이터를 별도로 관리합니다. * 쿼리 실행 시 시스템은 메타데이터를 검색하여 관련 있는 조각들을 식별하고, 이를 워커(worker) 풀에 분산하여 병렬로 스캔합니다. * 전체 쿼리 비용은 객체 스토리지에서 가져와야 하는 조각의 수와 해당 파일 내에서 스캔해야 하는 이벤트 수에 비례합니다. * 따라서 효율적인 조회를 위해 파일 수를 제어하는 '스트리밍 머지' 방식의 컴팩션과 쿼리당 스캔 이벤트를 줄이는 데이터 조직화 전략을 사용합니다. ## 컴팩션의 "골디락스(Goldilocks)" 문제 컴팩션은 여러 작은 조각을 하나의 큰 조각으로 병합하는 과정으로, 시스템의 효율성을 결정하는 핵심 요소입니다. Husky는 다음 요소들 사이에서 최적의 균형점(Goldilocks)을 찾습니다. * **파일 크기의 상충 관계:** 파일이 너무 작으면 객체 스토리지 접근 지연 시간과 메타데이터 부하가 커지며, 반대로 너무 크면 쿼리 워커 간의 병렬 처리가 제한되어 대규모 쿼리 속도가 느려집니다. * **컴팩션 비용과 성능:** 컴팩션 작업 자체도 CPU와 객체 스토리지 I/O 비용을 발생시키므로, 작업을 최소화하면서도 쿼리 성능을 높일 수 있는 적정 수준의 병합이 필요합니다. * **데이터 레이아웃 최적화:** 컴팩션 시 시간적 혹은 공간적(태그 등) 유사성에 따라 데이터를 재배치하면 압축률이 향상되고 쿼리 시 스캔해야 할 데이터 범위를 좁힐 수 있습니다. * **벡터화 실행:** Husky 워커는 많은 행을 빠르게 스캔하기 위해 벡터화된 실행(vectorized execution) 방식을 사용하며, 이는 적절한 크기의 조각에서 가장 효율적으로 작동합니다. ## FoundationDB를 통한 원자적 상태 관리 * 데이터 유입이 빈번한 환경에서 사용자가 즉시 데이터를 조회할 수 있도록, Husky는 유입 경로에서 짧은 버퍼링 후 작은 조각들을 빠르게 생성합니다. * 수많은 조각의 메타데이터를 관리하기 위해 트랜잭션 보장이 강력한 FoundationDB를 메타데이터 저장소로 사용합니다. * 컴팩션이 완료되면 FoundationDB의 트랜잭션 기능을 이용해 이전 조각들을 새 조각으로 '원자적(atomic)으로 교체'합니다. * 이를 통해 쿼리 시스템은 컴팩션 진행 중에도 데이터 중복이나 누락 없이 항상 일관된 상태의 테이블을 조회할 수 있습니다. 대규모 시계열 및 관측 데이터를 다루는 시스템을 설계할 때는 무조건적인 데이터 병합보다는 쿼리 패턴과 객체 스토리지의 특성을 고려한 컴팩션 정책이 중요합니다. 특히 메타데이터 계층에서 원자성을 확보하여 데이터 일관성을 유지하고, 병렬 스캔의 이점을 극대화할 수 있는 '적정 크기'의 데이터 블록을 유지하는 설계가 권장됩니다.