foundationdb

4 개의 포스트

datadog4분 읽기큐레이션 요약

AI 지원 리팩토링을 활용해 실시간 라우팅 시스템을 마이그레이션한 방법

Datadog은 Stream Router의 기존 FoundationDB 기반 KV 모델이 트랜잭션 크기와 데이터 증가에 따른 한계에 도달하자, 운영 중단 없이 PostgreSQL·DuckDB 기반의 관계형 구조로 전환했습니다. 이 과정에서 Claude와 Cursor를 활용했지만, AI가 자율적으로 코드를 작성한 것이 아니라 사람이 새 스키마와 기존 구현, 실패 테스트를 제공하고 테스트 결과로 검증하는 방식으로 사용했습니다. 핵심 결론은 AI가 대규모 마이그레이션을 크게 가속할 수 있지만, 데이터 모델 설계와 안전성 판단은 여전히 사람의 전문성이 필요하다는 것입니다. ## Stream Router의 역할과 기존 아키텍처 - Datadog은 하루 100조 개가 넘는 이벤트를 처리하며, 각 메트릭 데이터를 올바른 Kafka 클러스터·토픽·파티션으로 라우팅해야 합니다. - Stream Router는 Kafka 메시지를 직접 생산하거나 소비하지 않고, 다른 서비스가 사용할 라우팅 결정을 관리하는 제어 평면 서비스입니다. - 라우팅 정보는 다음과 같은 용도로 사용됩니다. - Producer가 데이터를 기록할 위치 결정 - Querier가 데이터를 읽을 위치 결정 - 시간에 따른 데이터 위치 이력 관리 - 기존 구조는 쓰기와 읽기를 분리한 Eventually Consistent 아키텍처였습니다. - 쓰기 경로: FoundationDB의 키-값 모델 사용 - 읽기 경로: 주기적으로 생성된 스냅샷을 RocksDB와 메모리 데이터베이스에 적재 - Producer와 Querier는 쓰기 경로에 직접 접근하지 않음 ## 설정 파일에서 중앙 제어 평면으로의 발전 - 2016년에는 몇 줄짜리 설정 파일을 모든 서비스에 배포해 라우팅을 관리했습니다. - 인프라와 고객 규모가 커지면서 설정 파일이 수천 줄로 증가했고, 수동 편집과 배포가 운영 부담이 되었습니다. - Stream Router 도입 후에는 다음과 같이 개선되었습니다. - 설정 파일 대신 gRPC API로 라우팅 변경 - 자동화된 오케스트레이션 - 점진적이고 자동화된 롤아웃 - 고가용성과 장애 내성을 고려한 읽기·쓰기 분리 ## KV 모델의 확장 한계 - 라우팅 데이터는 단순한 키-값 목록이 아니라 서로 연결된 관계형 데이터였습니다. - Route는 특정 Kafka Stream을 참조 - Route는 Sharding Strategy를 참조 - Rule은 Route를 참조하고 활성화 시점과 적용 방식을 결정 - 기존 KV 구조에서는 데이터베이스가 제공해야 할 관계 검증을 애플리케이션이 직접 수행해야 했습니다. - 수만 개의 레코드를 Pod 프로세스로 가져옴 - 애플리케이션 내부에서 관계형 데이터베이스처럼 조인과 일관성 검사를 수행 - 데이터와 변경 규모가 커지면서 FoundationDB 트랜잭션 크기 제한에 걸리는 작업이 발생했습니다. - FoundationDB를 PostgreSQL로 단순 교체하는 방안도 해결책이 되지 못했습니다. - 기존 KV 접근 패턴을 그대로 유지하면 수천 번의 순차적인 데이터베이스 왕복이 필요 - 일부 작업은 약 45분이 걸릴 것으로 예상 - 따라서 병목의 원인은 특정 데이터베이스가 아니라, KV에 맞춰진 데이터 모델과 애플리케이션 로직 자체였습니다. ## 관계형 스키마로 재설계 - 팀은 AI를 사용하기 전에 도메인 관계를 직접 분석하고 새 스키마를 설계했습니다. - 관계형 구조에서는 다음 관계를 외래 키로 명시합니다. - Streams와 Sharding Strategies → Routes - Routes → Rules - 기존 애플리케이션 코드가 수동으로 복원하던 관계를 데이터베이스가 직접 표현하고 검증할 수 있게 되었습니다. - 쓰기 경로에는 PostgreSQL을 선택했습니다. - 관계형 의미론 지원 - 트랜잭션 처리 - Datadog의 자체 관리형 PostgreSQL 플랫폼 활용 가능 - 읽기 경로에는 DuckDB를 선택했습니다. - 스냅샷 기반 읽기 계층에 적합한 임베디드 데이터베이스 - 배열 컬럼을 기본 지원 - PostgreSQL과 유사한 SQL 문법 - PostgreSQL과 DuckDB 사이에서 쿼리 로직을 공유할 수 있음 - SQLite도 검토했지만 배열 컬럼을 기본 지원하지 않아 적합하지 않았습니다. ## AI를 활용한 테스트 중심 리팩터링 - Claude와 Cursor는 코드를 독립적으로 생성하도록 맡기지 않았습니다. - 각 메서드마다 사람이 다음 정보를 제공했습니다. - 기존 구현 - 새 데이터베이스 스키마 - 현재 실패하는 테스트 - AI는 이를 바탕으로 첫 번째 구현을 만들었고, 테스트가 코드의 정확성을 검증했습니다. - 이 방식의 장점은 다음과 같습니다. - 전체 마이그레이션을 한 번에 생성하지 않고 메서드 단위로 분할 - 실패 테스트가 요구사항과 오류를 구체적으로 제시 - 생성 코드가 실제 동작과 데이터 관계를 만족하는지 즉시 확인 - 사람이 설계와 판단을 담당하고 AI는 반복적인 변환 작업을 가속 ## 안전한 마이그레이션을 가능하게 한 조건 - 마이그레이션이 성공할 수 있었던 기반은 AI보다 기존 시스템의 구조와 개발 프로세스였습니다. - 특히 저장소 계층이 `Controller`라는 내부 인터페이스 뒤에 모듈화되어 있었습니다. - 이러한 추상화 덕분에 저장 엔진과 구현을 교체하더라도 상위 계층의 변경 범위를 줄일 수 있었습니다. - 글의 제공된 부분은 안전성을 뒷받침한 요소를 설명하는 도중 끝나므로, 이후 테스트 전략이나 실제 전환 절차의 상세 내용은 포함되어 있지 않습니다. 결국 AI는 관계형 스키마를 설계하거나 운영 위험을 판단하는 도구라기보다, 명확한 설계와 테스트가 준비된 상태에서 반복적인 코드 변환을 빠르게 수행하는 도구로 활용하는 것이 적절합니다. 대규모 운영 시스템에서는 먼저 데이터 모델과 인터페이스를 사람이 설계하고, 작은 단위의 실패 테스트를 기준으로 AI 생성 코드를 검증하는 방식을 추천할 수 있습니다.

원문 읽기(새 탭에서 열림)
datadog원문

Husky 쿼리 엔진 내부: 100조 개 이벤트에 대한 실시간 액세스 (새 탭에서 열림)

Datadog은 매일 100조 개 이상의 이벤트와 수십억 개의 쿼리를 처리하기 위해 3세대 이벤트 저장소인 'Husky'를 구축했습니다. Husky의 쿼리 엔진은 고정된 스키마가 없고 데이터 볼륨이 가변적인 대규모 멀티테넌트 환경에서도 오브젝트 스토리지에 저장된 페타바이트급 데이터를 실시간으로 조회할 수 있도록 설계되었습니다. 이를 위해 시스템은 쿼리 플래너, 오케스트레이터, 메타데이터 서비스, 리더 서비스로 역할을 분산하여 성능과 비용 효율성을 동시에 달성했습니다. ### Husky의 데이터 모델과 주요 쿼리 패턴 Husky는 로그나 네트워크 트래픽과 같이 타임스탬프와 다양한 속성을 가진 '이벤트' 데이터를 저장하며, 서비스별로 상이한 데이터 형태를 유연하게 수용합니다. * **가변적인 스키마 지원:** 테넌트나 사용 사례(로그 vs 네트워크 데이터)에 따라 속성의 종류와 데이터의 크기가 극명하게 달성하더라도 효율적으로 처리할 수 있습니다. * **Needle-in-a-haystack 검색:** 수많은 데이터 중 특정 IP나 에러 메시지, 트레이스 ID 등을 찾아내는 고도로 선택적인 필터링 쿼리를 지원합니다. * **분석형(Analytics-style) 검색:** 특정 기간 동안의 서비스 지연 시간 추이나 지역별 매출 분석과 같이 대규모 데이터를 집계하여 시각화하는 쿼리를 최적화합니다. ### 쿼리 실행의 4단계 아키텍처 Husky의 쿼리 경로는 네 가지 핵심 서비스로 나뉘어 멀티테넌트 환경에서 안정적으로 동작합니다. * **쿼리 플래너(Query Planner):** 모든 쿼리의 진입점으로, 쿼리 유효성 검사 및 최적화를 수행합니다. 통계 데이터를 기반으로 쿼리를 시간 단위의 여러 단계로 분할하고 실행 결과를 최종 병합합니다. * **쿼리 오케스트레이터(Query Orchestrator):** 데이터 저장소의 관문 역할을 하며 메타데이터 조회, 프래그먼트(데이터 파일) 할당, 집계 조율을 담당합니다. 특히 '존 맵(Zone-map) 프루닝'을 통해 불필요한 데이터를 걸러냄으로써 다운스트림 작업량을 평균 30~60% 절감합니다. * **메타데이터 서비스(Metadata Service):** FoundationDB의 프런트엔드로서 데이터 컴팩션 중에도 쿼리 결과의 원자성(Atomicity)을 보장합니다. DB 내부 로직을 추상화하여 전체 쿼리 경로와 분리하는 역할을 합니다. * **리더 서비스(Reader Service):** 실제 오브젝트 스토리지에 저장된 프래그먼트에서 데이터를 읽어 응답을 반환하는 핵심 실행 엔진입니다. ### 리더(Reader) 서비스의 데이터 스캔 최적화 오브젝트 스토리지는 비용이 저렴하지만 읽기 속도가 느리므로, 리더 서비스는 "읽지 않아도 되는 데이터를 스캔하지 않는 것"을 최우선 목표로 삼습니다. * **행 그룹(Row Groups) 구조:** 수백만 행을 가진 대용량 프래그먼트를 '행 그룹' 단위로 물리적으로 배치하여 관리합니다. 이는 쿼리 실행 시 전체 파일을 메모리에 올리는 부담을 줄이고 메모리 부족(OOM) 오류를 방지합니다. * **입출력(I/O) 최소화:** 오브젝트 스토리지에 대한 GET 요청은 비용이 많이 들기 때문에, 쿼리에 꼭 필요한 행 그룹만 선택적으로 가져와 비용 효율성과 응답 속도를 극대화합니다. * **반복자 기반 실행 모델:** Volcano 모델에서 영감을 받은 반복자(Iterator) 방식을 사용하여 데이터를 효율적으로 스트리밍하며 처리합니다. Husky의 사례는 대규모 시계열 이벤트를 처리할 때 고정된 인덱스에 의존하기보다, 메타데이터 기반의 프루닝과 물리적인 데이터 레이아웃 최적화를 통해 오브젝트 스토리지의 한계를 극복할 수 있음을 보여줍니다. 저비용 고성능의 로그 분석 시스템을 설계한다면 데이터의 물리적 구조화와 단계별 쿼리 분산 처리가 핵심이 될 것입니다.

datadog원문

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)으로 교체'합니다. * 이를 통해 쿼리 시스템은 컴팩션 진행 중에도 데이터 중복이나 누락 없이 항상 일관된 상태의 테이블을 조회할 수 있습니다. 대규모 시계열 및 관측 데이터를 다루는 시스템을 설계할 때는 무조건적인 데이터 병합보다는 쿼리 패턴과 객체 스토리지의 특성을 고려한 컴팩션 정책이 중요합니다. 특히 메타데이터 계층에서 원자성을 확보하여 데이터 일관성을 유지하고, 병렬 스캔의 이점을 극대화할 수 있는 '적정 크기'의 데이터 블록을 유지하는 설계가 권장됩니다.

datadog원문

신뢰할 수 있는 분산 시스템을 설계하기 위해 형식 모델링, 경량 시뮬레이션, 카오스 테스트를 활용하는 방법 (새 탭에서 열림)

분산 시스템의 복잡성으로 인해 발생하는 시스템 수준의 설계 오류를 해결하기 위해, 데이터독(Datadog)은 차세대 메시지 큐 서비스인 'Courier'의 설계 과정에서 포멀 모델링(Formal Modeling)과 경량 시뮬레이션을 도입했습니다. 이 방식은 전통적인 단위 테스트나 카오스 테스트가 발견하기 어려운 고차원적인 설계 결함을 설계 단계에서 미리 검증하고, 시스템의 성능 특성을 통계적으로 예측할 수 있게 해줍니다. 결과적으로 이러한 접근법은 가용성과 신뢰성이 필수적인 핵심 인프라 서비스가 복잡한 실패 모드에서도 안정적으로 동작함을 확인하는 강력한 도구가 되었습니다. **포멀 모델링과 경량 시뮬레이션의 도입** - **포멀 모델링(Formal Modeling):** 고수준의 명세 언어를 사용해 시스템의 속성을 기술하고, 모델 체커를 통해 발생 가능한 모든 상태를 전수 조사함으로써 설계상의 논리적 결함이 없는지 검증합니다. - **경량 시뮬레이션(Lightweight Simulation):** 포멀 모델링이 확인하기 어려운 지연 시간(Latency), 비용, 확장성 등의 통계적 성능 지표를 실제 부하 환경과 유사한 조건에서 실행하여 분석합니다. - **도입 배경 및 트레이드오프:** 구현 자체를 검증하지는 못하고 모델 유지 보수의 오버헤드가 발생하지만, 대규모 장애(2023년 3월 사례)를 방지하고 설계의 정확성을 보장하기 위해 도입되었습니다. **차세대 메시지 큐 서비스: Courier** - **배경:** 기존 Redis 기반 시스템의 처리량 및 확장성 한계를 극복하기 위해 설계된 멀티테넌트 메시지 큐 서비스입니다. - **최소 1회 전달(At-least-once delivery):** 메시지 손실 없이 전송을 보장하며, 실패 시 데드 레터 큐(DLQ)로 이동하여 알림 누락을 방지합니다. - **점진적 성능 저하(Graceful Degradation):** 가용 컴퓨팅 자원이 줄어들더라도 처리량이 급격히 추락하지 않고 선형적으로 감소하도록 설계하여 전체 서비스 마비를 방지합니다. - **수평적 확장성:** 컴퓨팅 자원 추가에 따라 처리량이 선형적으로 증가하는 구조를 목표로 합니다. **멀티테넌시 및 고가용성을 위한 아키텍처** - **FoundationDB 기반 샤딩:** 여러 개의 FoundationDB 클러스터를 구축하고, 각 테넌트를 특정 클러스터 조합(예: 8개 중 4개 선택)에 샤딩하여 테넌트 간 간섭을 최소화합니다. - **폭발 반경(Blast Radius) 제어:** 특정 테넌트가 4개의 클러스터에 부하를 주더라도, 다른 테넌트는 최소 25% 이상의 가용 용량을 확보할 수 있도록 격리 수준을 높였습니다. - **브로커 레이어(Broker Layer):** gRPC API를 통해 샤딩 로직을 처리하고, 백엔드 클러스터의 상태 점검(Health Check)을 수행하며 3개의 가용 영역(AZ)에 분산 배치되어 고가용성을 유지합니다. 이러한 포멀 모델링과 시뮬레이션 기법은 복잡한 분산 시스템을 구축할 때 직관에 의존하는 대신 수학적·통계적 근거를 바탕으로 의사결정을 내릴 수 있게 합니다. 특히 Courier와 같이 신뢰성이 최우선인 기반 시스템을 설계할 때, 초기 단계에서의 철저한 검증은 추후 발생할 수 있는 막대한 수정 비용과 대규모 장애 위험을 줄이는 데 매우 효과적인 투자입니다.