데이터베이스 설계

191 개의 포스트

datadog원문

밀리초 하나하나까지 단축하기: Datadog Lambda Extension을 Rust로 재구축한 방법 (새 탭에서 열림)

Datadog은 기존 Go 기반의 AWS Lambda 확장이 가진 높은 오버헤드를 해결하기 위해, 이를 Rust 언어로 완전히 재작성한 'Project Bottlecap'을 진행했습니다. 이를 통해 콜드 스타트 시간을 82% 단축하고 메모리 사용량을 40% 절감했으며, 바이너리 크기를 55MB에서 7MB로 줄이는 획기적인 성능 개선을 달성했습니다. 결과적으로 리소스가 제한된 서버리스 환경에서도 사용자 애플리케이션에 영향을 주지 않고 고정밀 텔레메트리 데이터를 수집할 수 있게 되었습니다. ### 기존 범용 에이전트 기반 설계의 한계 - 초기 Datadog Lambda 확장은 다중 호스트나 클러스터 환경에 최적화된 기존 Datadog 에이전트 코드를 기반으로 구축되었습니다. - 범용 에이전트는 대규모 처리량과 캐싱, 버퍼링에 초점이 맞춰져 있어 리소스가 극도로 제한된 람다의 단기 실행 환경에는 부적합했습니다. - 종속성 제거, 바이너리 압축(UPX), 지연 로딩 등 모든 최적화 수단을 동원했음에도 불구하고 콜드 스타트 지연 시간이 450~500ms 이하로 내려가지 않는 성능 한계에 직면했습니다. - 결국 범용 도구와 서버리스 전용 도구의 스케일 차이를 인정하고, 밑바닥부터 다시 작성하는 결정을 내렸습니다. ### Lambda 환경에서 Rust 언어의 전략적 이점 - **안정성 및 메모리 안전성:** 람다 확장이 충돌하면 함수 전체가 종료되고 샌드박스가 초기화되어 다시 콜드 스타트가 발생하는데, Rust는 컴파일 타임에 메모리 안전성을 보장하여 이러한 위험을 최소화합니다. - **바이너리 경량화:** 가비지 컬렉터와 대규모 런타임이 포함된 Go와 달리, Rust는 킬로바이트 또는 낮은 메가바이트 단위의 매우 작은 바이너리를 생성하여 초기 로딩 시간을 줄입니다. - **제한된 환경의 이점:** 람다는 아마존 리눅스와 x86/Arm 아키텍처라는 고정된 환경만 고려하면 되므로, 다양한 환경을 지원해야 하는 시스템 프로그래밍에서 Rust가 가질 수 있는 복잡성 문제가 크게 완화되었습니다. ### Project Bottlecap의 핵심 설계 원칙 - **철저한 성능 오버헤드 통제:** 모든 풀 리퀘스트(PR)마다 벤치마크를 수행하여 성능 저하를 감시했으며, 성능 향상을 위해 공식 AWS SDK 사용을 포기하고 직접 AWS API 호출과 서명 로직을 작성하는 트레이드오프를 감수했습니다. - **핸들러 영향 최소화:** 람다 확장 API를 활용하여 함수 핸들러가 결과를 반환한 후에 텔레메트리를 처리함으로써, 사용자 API 응답 속도에 미치는 영향을 제거했습니다. - **다양한 플러시(Flush) 전략:** 리소스 사용량이 적은 API 함수부터 대규모 배치 작업까지 대응할 수 있도록 데이터 전송 시점을 유연하게 설정할 수 있는 구조를 갖추었습니다. 범용 소프트웨어를 특정 환경에 맞춰 최적화하는 것에는 한계가 있습니다. 특히 실행 시간과 리소스 사용량이 곧 비용과 직결되는 서버리스 환경에서는, 해당 환경의 제약 조건을 반영한 전용 도구를 구축하는 것이 초기 개발 비용이 높더라도 장기적으로 성능과 안정성 측면에서 압도적인 이점을 제공합니다.

datadog원문

스트리밍 플랫폼으로 대규모 환경에서 흔들림 없는 Kafka 안정성 달성 (새 탭에서 열림)

데이터독(Datadog)은 매일 수백 조 건의 이벤트를 처리하기 위해 수천 개의 토픽과 수백 개의 아파치 카프카(Apache Kafka) 클러스터를 운영하고 있습니다. 기존의 정적인 카프카 설정으로는 대규모 환경에서 발생하는 하드웨어 장애나 트래픽 변동에 유연하게 대응하기 어렵기 때문에, 데이터독은 카프카 인프라를 추상화한 '스트리밍 플랫폼(Streaming Platform)'이라는 제어 계층을 구축했습니다. 이를 통해 애플리케이션의 재설정이나 배포 없이도 실시간으로 트래픽을 리디렉션하고 클러스터를 관리함으로써 시스템의 복원력과 확장성을 극대화했습니다. ### 스트림(Streams)을 통한 파이프라인 복원력 강화 - **논리적 추상화**: 물리적인 카프카 토픽 대신 '스트림'이라는 추상화된 단위를 사용합니다. 스트림은 여러 클러스터와 가용 영역(AZ)에 걸쳐 존재할 수 있으며, 생산자와 소비자는 실제 카프카 토폴로지를 알 필요 없이 안정적인 식별자를 통해 데이터에 접근합니다. - **인프라 디커플링**: 애플리케이션이 특정 카프카 리소스에 종속되지 않기 때문에, 인프라 구성을 실시간으로 변경하거나 트래픽을 새로운 토픽/클러스터로 원활하게 재라우팅할 수 있습니다. ### 실시간 트래픽 페일오버 및 리밸런싱 - **무중단 전환**: 특정 클러스터에 문제가 발생하면 제어 계층이 즉시 새로운 토픽을 생성하고 트래픽을 리디렉션합니다. 생산자는 즉시 새 토픽으로 데이터를 보내고, 소비자는 기존 토픽의 잔량을 처리한 후 새 토픽으로 넘어가는 방식을 통해 데이터 유실 없이 전환이 이루어집니다. - **유연한 운영**: 장애 대응뿐만 아니라 클러스터 폐기, 파티션 수 조정, 부하 분산 등의 작업을 애플리케이션 수정 없이 수 초 내에 수행할 수 있습니다. ### 대규모 환경에 최적화된 Assigner와 소비자 모델 - **자체 코디네이터 개발**: 카프카의 기본 그룹 코디네이터는 세션 타임아웃에 의존하여 반응이 느리다는 단점이 있습니다. 이를 대체하기 위해 개발된 'Assigner'는 클러스터 상태와 CPU 부하 등의 메트릭을 실시간으로 모니터링하여 수 초 내에 워크로드를 재분배합니다. - **병렬 처리 극대화**: 엄격한 순서 보장보다는 '최소 한 번(at-least-once)' 전달 모델을 채택하고 소비 단계에서의 순서 제약을 완화했습니다. 이를 통해 대규모 병렬 처리를 구현하고, 순서 보장이 필요한 경우 이벤트 저장소 하단에서 처리하도록 설계했습니다. ### Head-of-line Blocking 문제 해결 및 고급 커밋 로그 - **스트림 레인(Stream Lanes)**: 서비스 품질(QoS)에 따라 트래픽을 독립적인 '레인'으로 분리합니다. 우선순위가 높은 실시간 데이터가 일시적인 트래픽 급증이나 낮은 우선순위 데이터로 인해 지연되는 것을 방지합니다. - **Dead-letter Queue(DLQ)**: 특정 이벤트(Poison Pill)가 처리를 방해할 경우 이를 별도의 DLQ로 격리하여 파티션 전체가 멈추는 현상을 방지합니다. - **메타데이터 기반 커밋**: 카프카의 단일 포인터 오프셋 커밋 방식에서 벗어나, 커밋 메타데이터 공간을 활용해 파티션 내 여러 오프셋 범위를 동시에 추적합니다. 이를 통해 소비자가 이전 데이터를 재처리하는 동안에도 최신 트래픽을 동시에 처리할 수 있는 유연성을 확보했습니다. 카프카를 대규모로 운영할 때는 인프라를 고정된 자산이 아닌 '교체 가능한 소모품'으로 취급하는 제어 계층이 필수적입니다. 데이터독의 사례처럼 물리적 인프라와 논리적 데이터 흐름을 분리하는 추상화 계층을 구축함으로써, 운영 복잡성을 낮추고 대규모 장애 상황에서도 시스템의 연속성을 보장할 수 있습니다.

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원문

Arm64 JIT 컴파일러 버그를 드러낸 PostgreSQL 세그멘테이션 오류 파헤치기 (새 탭에서 열림)

Postgres 서버에서 발생한 'Segmentation fault(signal 11)' 오류의 원인을 추적하여, 이것이 Postgres 자체의 결함이 아니라 Arm64 아키텍처 환경에서 작동하는 LLVM(JIT 컴파일러)의 버그임을 밝혀낸 과정을 다루고 있습니다. 대규모 파티션 테이블을 조회할 때 JIT가 활성화되면서 크래시가 발생했으며, 이를 통해 업스트림 LLVM의 문제를 해결하는 성과를 거두었습니다. ## JIT 컴파일과 크래시의 상관관계 * **세그멘테이션 폴트 발생**: 최신 버전의 Postgres를 사용 중임에도 특정 쿼리(죽음의 쿼리)를 실행하면 서버가 즉시 종료되는 현상이 발생했습니다. * **스택 오염 확인**: 코어 덤프 분석 결과, 호출 스택(Backtrace)이 비정상적으로 짧고 깨져 있었으며, 이는 JIT 실행 함수인 `ExecRunCompiledExpr` 부근에서 문제가 발생했음을 시사했습니다. * **트리거 조건**: 64개의 파티션과 160만 개 이상의 행을 가진 대규모 테이블을 스캔할 때, Postgres의 비용 기반 휴리스틱에 의해 JIT 컴파일이 활성화되면서 오류가 유발되었습니다. ## Postgres JIT와 LLVM의 역할 * **성능 최적화**: Postgres는 반복적인 SQL 표현식 평가 오버헤드를 줄이기 위해 LLVM을 사용하여 런타임에 네이티브 머신 코드를 생성합니다. * **튜플 디포밍(Tuple Deforming)**: 디스크상의 데이터를 메모리 표현으로 변환하는 과정을 네이티브 코드로 컴파일하여 처리 효율을 극대화합니다. * **컴파일 오버헤드**: JIT는 실행 속도를 높이지만 컴파일 시간이 추가되므로, Postgres는 실행 비용이 높은 쿼리에 대해서만 선택적으로 JIT를 적용합니다. ## 문제 해결 및 근본 원인 파악 * **임시 조치**: `SET jit = off;` 설정을 통해 JIT 기능을 비활성화함으로써 쿼리 지연 시간의 큰 손해 없이 프로덕션 환경의 크래시를 즉시 중단시켰습니다. * **디버깅 결과**: 데이터 복제 및 로컬 환경 재현을 통해 분석한 결과, 특정 조건에서 LLVM이 Arm64 아키텍처용 머신 코드를 잘못 생성하는 버그가 있음을 확인했습니다. * **업스트림 기여**: 이 조사는 단순히 설정을 변경하는 것에 그치지 않고, 어셈블리 수준의 분석을 통해 LLVM 프로젝트의 코드 수정까지 이끌어내는 계기가 되었습니다. ## 권장 사항 Arm64 기반 클라우드 환경에서 Postgres를 운영 중인데 원인을 알 수 없는 Segmentation fault가 발생한다면, 우선적으로 JIT를 비활성화하여 안정성을 확보하십시오. 이후 시스템의 LLVM 라이브러리 버전을 확인하고 관련 아키텍처 패치가 적용되었는지 점검하는 것이 필요합니다.

datadog원문

대규모 시계열 인덱싱 (새 탭에서 열림)

Datadog은 5년 사이 데이터 규모가 30배 이상 급증함에 따라, 기존의 시계열 인덱싱 시스템에서 발생하는 성능 병목과 유지보수 문제를 해결하기 위해 아키텍처를 재설계했습니다. 수조 건의 이벤트를 효율적으로 처리하기 위해 인덱스 서비스를 시계열 데이터 저장소와 분리하였으며, 쿼리 로그를 분석해 인덱스를 자동으로 생성하는 전략을 취했습니다. 이 글은 RocksDB와 SQLite를 기반으로 한 초기 인덱싱 서비스의 구조와 대규모 시계열 데이터를 관리하기 위한 Datadog의 기술적 접근 방식을 다룹니다. ### 메트릭 플랫폼의 계층별 구조 * **수집(Intake) 계층:** 데이터 포인트는 메트릭 이름, 태그(env, host, service 등), 타임스탬프, 수치 값으로 구성됩니다. 수집된 데이터는 메시지 브로커인 Kafka로 전달되어 분석, 인덱싱, 아카이빙 등 다양한 용도로 독립적으로 소비됩니다. * **저장(Storage) 계층:** 데이터 저장소는 두 개의 서비스로 나뉩니다. '시계열 데이터베이스'는 `<시계열_ID, 타임스탬프, 값>` 튜플을 저장하고, '시계열 인덱스' 서비스는 RocksDB를 기반으로 `<시계열_ID, 태그>`를 매핑하여 쿼리 시 필터링과 그룹화를 담당합니다. * **쿼리(Query) 계층:** 분산 쿼리 계층은 인덱스 노드에서 검색된 식별자를 바탕으로 시계열 데이터베이스에서 실제 값을 가져와 병합하며, 필터와 집계 함수(avg 등)를 적용해 최종 결과를 도출합니다. ### 쿼리 로그 분석을 통한 자동 인덱싱 전략 * **풀 스캔 방지:** 특정 메트릭의 전체 데이터를 조회하는 비효율적인 스캔을 피하고자, 태그 기반의 인덱스를 생성하여 쿼리 실행 속도를 최적화했습니다. * **동적 인덱스 생성:** 시스템은 백그라운드 프로세스를 통해 실시간 쿼리 로그를 분석합니다. 쿼리 횟수, 실행 시간, 입력 대비 출력 식별자 비율을 따져 리소스 소모가 큰 '고선택성' 쿼리에 대해 자동으로 인덱스를 생성합니다. * **구체화된 뷰(Materialized Views):** 자주 사용되는 복잡한 쿼리를 미리 계산된 인덱스 형태로 저장함으로써, 반복되는 쿼리 요청을 단순한 키-값 조회로 변환해 CPU와 메모리 리소스를 획기적으로 절감합니다. ### 임베디드 데이터베이스를 활용한 시스템 설계 * **SQLite 기반의 메타데이터 관리:** 인덱스 정의와 쿼리 로그 등 읽기 중심의 데이터는 Go 애플리케이션 내에 임베디드된 SQLite에 저장됩니다. SQL의 유연성 덕분에 CLI를 통한 디버깅과 테이블 관리가 용이합니다. * **RocksDB를 통한 고성능 쓰기 처리:** 매일 발생하는 수조 건의 인덱싱 데이터는 고성능 키-값 저장소인 RocksDB가 처리합니다. 별도의 서버 프로세스 없이 애플리케이션에 직접 통합되어 성능 극대화를 꾀했습니다. * **인덱스 수명 주기 관리:** 일정 기간 쿼리가 발생하지 않아 쓸모없어진 인덱스는 시스템이 자동으로 삭제하여 저장 공간을 효율적으로 관리합니다. 대규모 분산 환경에서 모든 데이터에 대해 미리 인덱스를 생성하는 것은 불가능에 가깝습니다. Datadog의 사례처럼 실제 사용자의 쿼리 패턴을 모니터링하고, 리소스 집약적인 쿼리에 대해 인덱스를 동적으로 생성하는 '쿼리 기반 최적화' 방식은 폭발적인 데이터 성장세 속에서 시스템 가용성을 유지하는 매우 실용적인 전략입니다.

datadog원문

정적 분석기를 Java에서 Rust로 마이그레이션한 방법 (새 탭에서 열림)

Datadog은 정적 분석 도구의 성능 병목 현상을 해결하고 제한된 CI 환경에서의 효율성을 극대화하기 위해 기존 Java 기반의 엔진을 Rust로 완전히 재작성했습니다. 이 과정에서 ANTLR 대신 Tree-sitter를 도입하고 JavaScript 규칙 실행 엔진을 GraalVM에서 Deno(V8)로 교체함으로써, 분석 속도는 3배 향상시키고 메모리 사용량은 10배 절감하는 성과를 거두었습니다. 결과적으로 이번 전환은 고성능 정적 분석을 위해 언어와 런타임 수준의 근본적인 변화가 필수적이었음을 보여줍니다. **Java 기반 정적 분석기의 한계와 환경적 제약** * **CI 자원 최적화 문제:** 고객의 CI 환경(예: GitHub Actions)에서 분석기를 실행할 때, 2코어 및 7GB RAM과 같은 제한된 자원 내에서 Java 분석기는 수천 개의 파일을 스캔하는 데 5분 이상 소요되어 목표치(3분 이내)를 충족하지 못했습니다. * **환경 충돌 및 오버헤드:** Java 기반 분석기는 최신 JVM(17+)을 요구하는데, 이는 고객의 기존 CI 환경에 설치된 Java 버전과 충돌을 일으키거나 불필요한 설정 부담을 주었습니다. * **파싱 성능 저하:** 기존에 사용하던 ANTLR은 대규모 저장소에서 파싱 속도가 느렸고, 특정 언어에 대한 지원이 부분적이라는 기술적 한계가 있었습니다. **Tree-sitter 도입과 Rust로의 전환 결정** * **파서 교체:** 성능 향상을 위해 C로 구현되어 속도가 빠르고 오픈소스 커뮤니티가 활발한 Tree-sitter를 채택했습니다. * **Java 라이브러리의 한계:** Tree-sitter용 Java 라이브러리는 분석기 최적화에 필수적인 패턴 매칭 기능을 지원하지 않는 등 기능이 제한적이었습니다. * **언어 선택의 기로:** Tree-sitter가 가장 견고하게 지원하는 언어가 Rust라는 점에 착안하여, 예측 가능한 성능을 위해 Rust로의 전체 재작성이라는 도전적인 경로를 선택했습니다. **Rust 기반의 새로운 아키텍처 구성** * **AST 구축(Tree-sitter):** Rust는 Tree-sitter 생태계의 "일등 시민(First-class citizen)"으로, 직접적인 라이브러리 연동을 통해 별도의 Java 바인딩 유지보수 없이도 강력한 파싱 기능을 확보했습니다. * **자바스크립트 규칙 실행(Deno):** 정적 분석 규칙은 자바스크립트로 작성되는데, 기존 Java의 GraalVM 대신 Deno(deno-core)를 런타임으로 도입했습니다. * **보안 및 효율성:** Deno의 V8 엔진을 활용하되, 분석 규칙이 디스크나 네트워크에 접근하지 못하도록 `deno-core` 크레이트만 통합하여 보안이 강화된 샌드박스 환경을 구축했습니다. **마이그레이션 결과 및 기술적 권고** 성공적인 Rust 전환을 통해 동일한 프로그램에 대해 Java 버전과 일치하는 분석 결과를 보장하면서도 성능은 3배, 메모리 효율은 10배 개선되었습니다. 특히 리소스가 제한된 CI/CD 파이프라인에서 정적 분석 도구를 운영해야 한다면, JVM과 같은 무거운 런타임보다는 Rust와 같이 저수준 제어가 가능하고 메모리 오버헤드가 적은 언어를 선택하는 것이 장기적으로 유리합니다.

datadog원문

엔지니어링 부문 VP 스포트라이트: 이보 디미트로프 (새 탭에서 열림)

데이터독(Datadog)의 엔지니어링 VP 이보 디미트로프(Ivo Dimitrov)는 30년 이상의 경력을 가진 베테랑으로서, 고성능 저수준 시스템 개발자에서 대규모 분산 시스템을 총괄하는 리더로 성장해 온 인물입니다. 그는 마이크로소프트와 링크드인에서 쌓은 대규모 스토리지 인프라 구축 경험을 바탕으로, 현재 데이터독에서 메트릭과 이벤트 플랫폼의 기술적 혁신을 이끌고 있습니다. 기술적 깊이와 조직적 비전 사이의 균형을 강조하는 그의 여정은 복잡한 데이터 시스템을 다루는 엔지니어와 관리자들에게 중요한 통찰을 제공합니다. ### 저수준 시스템 개발에서 엔지니어링 리더십으로의 전환 * 전기 공학 전공 중 실시간 운영체제(RTOS) 커널 기여를 통해 소프트웨어 개발에 입문했으며, 이후 10년간 C/C++ 기반의 고성능 시스템 프로그래밍에 집중했습니다. * 마이크로소프트의 Azure Blob Storage 초기 팀에서 근무하던 중 조직 개편을 계기로 관리직을 맡게 되었으며, 현장에서의 실무 교육과 멘토링을 통해 리더십 역량을 쌓았습니다. * 자신의 업무에만 집중하는 단계를 넘어 타인의 성장을 촉진하고 조직 간의 경계를 조율하는 '오너십(Ownership)'의 가치를 발견하며 매니지먼트의 매력을 느꼈습니다. ### 대규모 분산 스토리지 플랫폼 구축 경험 * 링크드인 재직 당시, 현재까지 전체 데이터셋의 95% 이상을 처리하는 독점 키-값(Key-Value) 저장소인 'Espresso'를 초기 단계에서 성숙한 플랫폼으로 성장시켰습니다. * 파생 데이터 서빙을 위한 'Venice', 오픈소스 블록 스토리지인 'Ambry', 클러스터 관리자인 'Helix' 등 인터넷 규모의 스토리지 인프라 프로젝트들을 주도했습니다. * 이러한 경험을 통해 대규모 레거시 환경의 제약에서 벗어나, 기술적 위험을 감수하고 빠르게 혁신할 수 있는 문화적 유연성의 중요성을 깨달았습니다. ### 데이터독의 분산 데이터 시스템과 기술적 지향점 * 현재 데이터독에서 메트릭(Metrics), 이벤트(Events), 로그 및 트레이스 등 반정형 데이터를 처리하는 분산 데이터 시스템 조직을 총괄하고 있습니다. * 온라인 분석 워크로드에 최적화된 특수 메인 메모리 데이터베이스인 'Driveline'을 통해 시계열 데이터 처리의 효율성을 극대화하고 있습니다. * 서로 다른 도메인별 API를 통합하는 '교차 플랫폼 쿼리(Cross-Platform Queries)' 팀을 운영하여, 고객과 엔지니어 모두가 통일된 인터페이스로 데이터에 접근할 수 있도록 추상화 계층을 구축 중입니다. * 전체 쿼리의 80% 이상을 차지하는 알람(Alerts) 플랫폼을 메트릭 및 이벤트 플랫폼과 통합하여 시스템의 일관성을 높이고 있습니다. **실용적인 제언** 개인 기여자(IC)에서 관리자로 전환할 때는 기술적 전문성을 포기하는 것이 아니라, 그 전문성을 바탕으로 조직의 비전을 설계하고 팀원들이 역량을 발휘할 수 있는 환경을 조성하는 데 집중해야 합니다. 특히 데이터독의 사례처럼 쿠버네티스(Kubernetes) 기반의 현대적 인프라와 실험을 장려하는 문화를 결합할 때, 기술적 부채를 최소화하면서도 폭발적인 성장을 뒷받침하는 시스템을 구축할 수 있습니다.

datadog원문

임의의 규모에서 시간에 따른 분포를 시각화하는 Datadog 히트맵을 구축한 방법 (새 탭에서 열림)

단순한 백분위수(Percentile) 선 그래프는 데이터의 전체적인 형상과 그 안에 숨겨진 다양한 패턴(Mode)을 왜곡하거나 가릴 수 있습니다. Datadog은 DDSketch 알고리즘을 활용한 히트맵(Heatmap) 시각화를 통해 수조 개의 데이터 포인트를 성능 저하 없이 고해상도로 구현하여, 집계된 지표 뒤에 숨겨진 시스템의 실제 동작을 명확하게 드러냅니다. 이를 통해 엔지니어는 단순 수치 이상의 풍부한 컨텍스트를 파악하고 대규모 인프라의 복잡한 성능 문제를 효과적으로 해결할 수 있습니다. **집계 데이터 시각화의 한계와 히트맵의 이점** * 선 그래프(p50, p99 등)는 수많은 이벤트를 단일 값으로 집계하여 특정 시점의 성능은 보여주지만, 데이터 분포의 전체적인 모습은 설명하지 못함. * 히트맵은 데이터를 과도하게 집계하지 않고 시각화하여, 서로 다르게 동작하는 여러 시스템 그룹(Modes)을 시각적 아티팩트로 분리해 보여줌. * 이를 통해 특정 벤치마킹 서비스로 인한 주기적 지연이나 헬스 체크 요청의 패턴 등 백분위수 그래프에서는 노이즈로 보일 수 있는 현상을 직관적으로 식별 가능함. **무한한 확장을 위한 엔지니어링: DDSketch** * DDSketch를 사용하여 정밀도를 미세하게 희생하는 대신, 방대한 양의 데이터를 '실제 값에 충분히 가까운' 형태로 효율적으로 표현함. * 프론트엔드 전송 시 전체 포인트 목록 대신 '빈(bin)과 카운트(count)' 구조를 사용하여 데이터 페이로드 크기를 일정하게 유지함. * 각 빈의 카운트 저장에 `float32` 타입을 채택하여, 이론적으로 수조 년 동안 매초 발생하는 호출도 수용할 수 있는 수치적 확장성을 확보함. **고해상도 구현 및 데이터 정렬 기술** * 수백조 개의 데이터 포인트를 시각화하기 위해 각 시간 범위(Time bucket)의 경계 값을 일렬로 정렬하고 저장 구조를 최적화함. * 데이터 보고 주기와 히트맵의 시간 버킷 간격이 일치하지 않을 때 발생하는 에일리어싱(Aliasing) 현상을 방지하기 위해 데이터 정렬 알고리즘을 적용함. * 선형 스케일 외에도 로그 스케일을 지원하여 소스 데이터의 해상도에 근접한 시각적 정밀도를 제공함. **색상 설계와 인지적 다이내믹 레인지 유지** * 색상 팔레트는 가독성을 위해 연한 파란색에서 보라색을 거쳐 주황색(Hot)으로 전환되도록 설계하며, 경고 느낌을 주는 빨간색은 의도적으로 배제함. * 인간의 시각이 밝기 차이를 비선형적으로 인지한다는 '스티븐스의 멱법칙(Stevens' Power Law)'을 시각화 로직에 반영함. * 데이터가 멱법칙 분포(롱테일)를 따를 때 선형 색상 보간을 사용하면 정보가 손실되므로, 비선형 보간법을 통해 미세한 빈도의 차이도 눈으로 식별할 수 있게 함. **실용적인 제언** 성능 분석 시 단순히 선 그래프의 추세에만 의존하기보다는 히트맵을 병행하여 사용하는 것이 권장됩니다. 특히 대규모 분산 시스템에서 발생하는 간헐적인 지연이나 특정 노드 그룹의 이상 행동은 히트맵을 통해서만 명확한 '시각적 패턴'으로 드러나기 때문에, 근본 원인 분석(RCA) 시간을 획기적으로 단축할 수 있습니다.

datadog원문

.NET Continuous Profiler: 내부 동작 원리 (새 탭에서 열림)

Datadog의 .NET 프로파일러는 운영 환경에서 24시간 내내 실행될 수 있도록 설계된 상시(continuous) 프로파일링 도구로, 애플리케이션 성능에 미치는 영향을 최소화하면서 CPU, 메모리, 잠금 경합 등 다양한 런타임 지표를 수집합니다. 이를 통해 개발자는 별도의 재현 환경을 구축하지 않고도 실제 운영 상황의 성능 병목 현상을 정밀하게 분석할 수 있으며, 수집된 데이터는 효율적인 집계와 가독성 높은 호출 스택 변환 과정을 거쳐 백엔드로 전달됩니다. ## .NET 프로파일러의 구조와 데이터 수집 * 개별 리소스(CPU, 예외, 잠금 경합 등)를 담당하는 여러 독립적인 프로파일러와 샘플러, 데이터를 노출하는 프로바이더로 구성된 모듈형 아키텍처를 가집니다. * 수집된 샘플은 호출 스택(Call Stack), 컨텍스트 정보(Labels), 수치 데이터 벡터를 포함하며, 동일한 스택과 레이블을 가진 샘플은 하나로 병합하여 데이터 크기를 최적화합니다. * 샘플 집계 및 `.pprof` 형식으로의 직렬화 로직은 성능 향상과 타 언어 프로파일러와의 공유를 위해 Rust 언어로 구현되었습니다. ## 분산 추적 연동 및 백엔드 처리 * 'Runtime ID'를 사용하여 하나의 프로세스 내에서 여러 서비스(예: IIS AppDomain)가 실행되더라도 각 서비스를 정확히 식별하고 분산 추적(Traces/Spans) 데이터와 프로파일을 정교하게 연결합니다. * 트레이서(Tracer)가 런타임 ID와 서비스 이름(DD_SERVICE)의 매핑 정보를 프로파일러에 전달함으로써, 백엔드에서 특정 서비스에 해당하는 프로파일을 정확히 찾아낼 수 있게 합니다. ## 호출 스택(Call Stack) 가독성 개선 * 닷넷 런타임 API가 제공하는 로우(raw) 데이터를 개발자가 소스 코드 수준에서 즉시 이해할 수 있도록 정제(Clean-up)하는 과정을 거칩니다. * 생성자(`.ctor`)는 실제 클래스 이름으로, 익명 메서드나 람다식은 원래 메서드명에 `_AnonymousMethod`나 `_Lambda` 접미사를 붙여 변환합니다. * 로컬 메서드나 컴파일러가 생성한 비동기 상태 머신의 `MoveNext` 메서드 등 복잡한 이름들도 소스 코드 구조를 반영하도록 가공하여 분석의 혼선을 줄입니다. ## 네이티브 구현을 통한 성능 최적화 * 프로파일러 자체의 메모리 할당이 대상 애플리케이션의 가비지 컬렉션(GC)에 압박을 주지 않도록 핵심 로직을 C#(Managed code)이 아닌 네이티브 수준에서 구현하는 방향을 선택했습니다. * 이러한 설계는 운영 환경에서 성능 저하를 무시할 수 있는 수준으로 유지하면서도 상세한 런타임 성능 데이터를 지속적으로 수집할 수 있는 기반이 됩니다. 운영 환경의 부하를 최소화하면서 실제 트래픽 상황의 성능 문제를 정확히 진단하고 싶다면, 네이티브 수준에서 최적화되고 소스 코드 가독성까지 고려된 상시 프로파일링 도구를 도입하는 것이 가장 효과적인 전략입니다.

datadog원문

엔지니어링 스포트라이트: 제로미 캐리어 (새 탭에서 열림)

데이터독(Datadog)의 제품 엔지니어링 SVP인 제로미 캐리어(Jeromy Carriere)는 엔지니어링 조직의 성패가 단순한 속도가 아닌, 명확한 방향성을 가진 '지속 가능한 속도(Sustained Velocity)'에 달려 있다고 강조합니다. 그는 구글과 페이스북에서의 경험을 바탕으로, 엔지니어링 리더십은 팀의 자율성을 보장하는 것과 결과에 대한 책임을 묻는 것 사이의 균형을 잡는 과정임을 설명합니다. 결론적으로 그는 관측성(Observability)의 미래가 단순한 현상 파악을 넘어 엔지니어가 무엇을 해야 하는지 알려주는 처방적 단계로 진화해야 한다고 주장합니다. **엔지니어링 리더의 다각적 실행 사이클** * **계획 및 실행의 조화:** 분기별 계획 사이클을 통해 팀 간의 협업 기회를 테마별로 연결하고, 실행 사이클에서는 리소스 지원이나 의사결정 정체를 해소하여 팀의 병목 현상을 제거합니다. * **엔지니어링 메타 관리:** 엔지니어링 조직이 어떻게 작동하는지에 대한 프로세스 정의, 채용, 성과 관리 등 조직의 운영 효율성을 높이는 '메타 수준'의 업무에 집중합니다. * **기술적 현장감 유지:** 고위 임원임에도 불구하고 설계 문서와 코드를 읽고, 인시던트 및 사후 분석(Post-mortems)을 관찰하며 전체 스택에서 조직이 어떻게 실행되고 있는지 파악합니다. **데이터독을 선택한 이유: 지속 가능한 속도** * **성장 궤적:** 구글 클라우드 플랫폼 초기 시절 클라우드 모니터링 제품을 구축하며 관측성 분야의 중요성을 체감했고, 이후 페이스북을 거치며 개발자 생산성에 미치는 영향력에 매료되었습니다. * **전략적 속도:** 단순히 빨리 움직이는 것이 아니라, 의도된 전략과 방향성을 가지고 10년 넘게 혁신을 유지하는 데이터독의 '지속 가능한 속도'를 유지하고 확장하는 데 주력하고 있습니다. **실수를 통한 학습과 리더십 철학** * **새로운 실수의 장려:** 과거의 실수를 반복하지 않되, 새로운 실수를 통해 배우고 성장하는 문화를 지향합니다. * **자율성과 책임의 균형:** 초기 커리어의 지나치게 지시적인 태도가 팀의 창의성을 해친다는 것을 깨닫고, 현재는 최대한의 자율성을 부여하되 약속한 결과에 대해서는 엄격히 책임을 묻는 균형점을 추구합니다. * **만족 중심의 커리어 개발:** 5년 단위의 경력 계획보다는 개인이 어떤 업무에서 만족감을 느끼는지 파악하는 것이 중요하며, 리더는 팀원이 이를 발견할 수 있도록 시야를 넓혀주는 역할을 해야 합니다. **관측성 기술의 미래와 실전 경험의 가치** * **처방적 관측성으로의 전환:** 관측성은 "무슨 일이 일어나고 있는가?"라는 질문에서 "내가 무엇을 해야 하며, 어떻게 고쳐야 하는가?"라는 처방적인 단계로 진화하고 있습니다. * **실무 중심의 인재 양성:** 워털루 대학교의 코옵(Co-op) 프로그램이나 데이터독의 인턴십처럼, 학생들이 실제 업무 환경에서 책임감을 가지고 기여할 때 진정한 전문 소프트웨어 개발자로 성장할 수 있습니다. 성공적인 엔지니어링 조직을 구축하기 위해서는 기술적 탁월함만큼이나 조직 운영의 프로세스를 정교하게 다듬는 노력이 필요합니다. 특히 리더는 팀이 스스로 방향을 찾을 수 있도록 자율성을 부여하는 동시에, '지속 가능한 속도'를 유지할 수 있는 전략적 가이드를 끊임없이 제시해야 합니다.

datadog원문

2023-03-08 사건: 플랫폼 수준 복구에 대한 심층 분석 | Datadog (새 탭에서 열림)

2023년 3월 발생한 대규모 장애 당시 Datadog은 전체 컴퓨팅 용량의 60%를 상실했으며, 이를 복구하기 위해 계층화된 쿠버네티스 구조에 따른 체계적인 재부팅 전략을 수행했습니다. EU1 리전의 복구 과정에서 팀은 단순한 노드 재가동을 넘어 클라우드 제공업체의 피어링 그룹 제한과 서브넷 IP 고갈이라는 예상치 못한 인프라 한계에 직면했습니다. 이 글은 대규모 인프라 장애 시 제어 평면(Control Plane)의 복구 순서와 백로그 처리를 위한 과도한 스케일 아웃이 유발하는 2차 병목 현상을 상세히 다룹니다. **계층적 쿠버네티스 구조와 복구 전략** * Datadog은 관리 효율성을 위해 '부모(Parent)-자식(Child)' 형태의 계층적 클러스터 구조를 사용합니다. 부모 클러스터는 자식 클러스터의 제어 평면을 포드(Pod) 형태로 호스팅하며, 자식 클러스터는 실제 애플리케이션 워크로드를 실행합니다. * 장애의 원인이 된 시스템 패치(Ubuntu 22.04의 systemd-networkd 관련 이슈)로 인해 네트워크 연결이 끊긴 노드들을 복구하기 위해 엄격한 순서에 따른 재부팅을 진행했습니다. * 복구는 (1) 부모 클러스터 제어 평면 노드 재시작, (2) 부모 노드 위에서 실행되는 자식 클러스터 제어 평면 포드 복구, (3) 수천 개의 자식 클러스터 애플리케이션 노드 재시작 순으로 이루어졌습니다. * 특히 제어 평면에 과부하가 걸리지 않도록 노드 재시작 속도를 조절했으며, 워크로드의 중요도에 따라 클러스터별 복구 우선순위를 설정했습니다. **인프라 확장 제한으로 인한 복구 지연** * 모든 컴퓨팅 용량을 복구한 후, 장애 동안 쌓인 대규모 데이터 백로그를 처리하기 위해 급격한 스케일 아웃(Scale-out)을 시도하는 과정에서 예상치 못한 제한에 부딪혔습니다. * **GCP 네트워크 피어링 제한:** EU1 리전 내 인스턴스 수가 15,500개에 도달하며 구글 클라우드의 네트워크 피어링 그룹 제한에 걸려 약 4시간 동안 추가 인스턴스 생성이 차단되었습니다. 이는 구글 측과의 긴급 협력을 통해 한도를 증설하여 해결했습니다. * **서브넷 IP 주소 고갈:** 로그 및 트레이스 처리를 담당하는 특정 클러스터들이 평상시보다 2배 이상 스케일 아웃을 시도하면서 서브넷 내 사용 가능한 IP 주소가 바닥났습니다. * 평소 IP 사용률을 66% 이하로 유지하도록 모니터링해왔으나, 백로그 처리를 위한 폭발적인 수요는 평상시 변동 폭을 훨씬 상회하는 수준이었습니다. 결과적으로 특정 클러스터들은 약 6시간 동안 최적의 속도로 데이터를 처리하지 못했습니다. **교훈 및 실용적 권장사항** 복구 계획을 세울 때는 단순히 시스템을 정상화하는 것을 넘어, 장애 이후 발생할 '데이터 백로그 처리'를 위한 초과 용량 확보 시나리오를 반드시 고려해야 합니다. 클라우드 제공업체의 하드웨어 리소스 한계뿐만 아니라 네트워크 피어링, 서브넷 IP 할당 범위와 같은 소프트웨어적/구성적 제한 사항을 사전에 파악하고, 극단적인 스케일링 상황에서도 유연하게 대처할 수 있는 여유 용량(Headroom) 설계가 필수적입니다.

datadog원문

단순한 또 하나의 네트워크 지연 문제가 아니었다: 숨겨진 병목 현상의 연쇄를 파헤친 과정 (새 탭에서 열림)

사용량 추정 서비스의 배포 시마다 반복되는 높은 시작 지연 시간(Startup Latency) 문제를 해결하기 위해, 시스템 전반의 네트워크 경로와 인프라 계층을 다각도로 조사했습니다. 단순히 애플리케이션 코드를 수정하는 수준을 넘어, 사이드카 프록시 설정, 리눅스 커널 버그, 클라우드 인스턴스의 네트워크 대역폭 한계 등 복합적인 병목 현상을 단계별로 추적해 해결했습니다. 최종적으로 인프라 최적화와 우아한 종료(Graceful Shutdown) 메커니즘을 결합하여 서비스 안정성을 확보하고 팀의 경보 피로도를 대폭 낮추는 결론에 도달했습니다. **Envoy 사이드카의 CPU 병목 해소** * 배포 단계에서 원격 캐시 데이터를 대량으로 불러올 때, 사이드카 프록시인 Envoy가 모든 쿼리를 배치 처리하며 과도한 CPU를 사용함을 확인했습니다. * Envoy가 할당된 CPU 자원(2코어)을 모두 소진하여 쓰로틀링(Throttling)이 발생했고, 이로 인해 패킷 처리 지연과 TCP 재전송(Retransmit)이 급증했습니다. * Envoy에 할당되는 CPU 자원을 늘려 1차적인 지연 시간 수치를 개선했으나, 여전히 배포 중 지연 시간이 300ms에서 1s 사이를 진동하는 문제가 남았습니다. **리눅스 커널 버그 패치 및 트래픽 분산** * 조사 과정에서 AWS의 Elastic Network Adapter(ENA)를 사용할 때 발생하는 리눅스 커널 버그를 발견했습니다. * 해당 버그는 네트워크 트래픽을 8개의 전송 큐(Transmit Queue)에 분산하지 않고 첫 번째 큐에만 몰아넣어 병목을 유발하고 있었습니다. * 트래픽을 모든 큐에 골고루 분산시키는 핫픽스를 적용하여, 배포 기간 외에 간헐적으로 발생하던 지연 시간 스파이크 문제를 해결했습니다. **AWS 인스턴스 네트워크 대역폭 최적화** * 커널 수정 후에도 배포 중 지연이 지속되자 AWS 전용 메트릭인 `bw_in_allowance_exceeded`와 `bw_out_allowance_exceeded`를 분석했습니다. * 분석 결과, 배포 시 발생하는 급격한 트래픽이 인스턴스 유형별로 할당된 최대 네트워크 대역폭을 초과하여 하이퍼바이저 수준에서 패킷 드롭이 발생하고 있었습니다. * 이를 해결하기 위해 더 높은 대역폭을 제공하는 네트워크 최적화(Network-optimized) EC2 인스턴스로 마이그레이션하여 대역폭 제한 문제를 해결했습니다. **종료되는 파드로의 요청 라우팅 방지** * 모든 인프라 개선 후에도 남아있던 1초 가량의 지연 스파이크가 원격 캐시 파드의 종료(Terminating) 시점과 일치함을 포착했습니다. * 기존의 우아한 종료 로직이 Envoy 클라이언트의 처리 중인 요청(In-flight requests)을 충분히 기다리지 못해, 종료 중인 파드에 요청이 전달되어 타임아웃과 재시도가 발생하고 있었습니다. * 파드에 `preStop` 훅을 구현하여 종료 전 유지 관리 모드 상태임을 클라이언트에 알리고, 모든 요청이 완료될 때까지 대기하도록 설정하여 지연 시간을 최종적으로 안정화했습니다. 성능 최적화 과정에서 단일 원인을 찾기보다 네트워크 스택의 각 계층(프록시, OS 커널, 클라우드 인프라, 애플리케이션 생명주기)을 체계적으로 검증하는 접근 방식이 중요합니다. 특히 대규모 트래픽이 발생하는 배포 시점에는 시스템의 숨겨진 한계치가 드러나기 쉬우므로, 클라우드 제공업체의 전용 메트릭과 네트워크 큐 상태를 면밀히 모니터링할 것을 권장합니다.

datadog원문

fetch를 현실로 만들기: 범용 쿼리 및 렌더링 스케줄러 구축 (새 탭에서 열림)

데이터독(Datadog)은 복잡한 대시보드의 성능 최적화를 위해 쿼리와 렌더링 작업을 효율적으로 관리하는 범용 스케줄러를 개발했습니다. 기존의 복잡한 규칙 기반 시스템을 단순화하고 브라우저 네이티브 API를 활용함으로써, UI 응답성을 높이고 백엔드 부하를 안정화하는 성과를 거두었습니다. 이 글은 기존 스케줄러의 한계를 극복하고 모든 애플리케이션에 적용 가능한 유연한 스케줄링 모델로 전환한 과정을 다룹니다. ### 기존 스케줄러(v1)의 문제점 * **지나친 복잡성:** 약 20여 개의 매개변수가 얽힌 복잡한 휴리스틱(heuristics)으로 운영되어, 개발자가 시스템의 동작 방식을 예측하거나 추론하기 어려웠습니다. * **로직의 결합:** 쿼리(데이터 호출)와 렌더링(화면 표시) 스케줄링이 명확히 분리되지 않았습니다. 예를 들어, 렌더링 대기 작업이 많다는 이유로 연관 없는 쿼리 요청이 지연되는 등의 비효율이 발생했습니다. * **낮은 범용성:** 대시보드 환경에만 특화되어 설계되었기 때문에, 데이터독의 다른 제품군이나 일반적인 위젯 컴포넌트에서 재사용하기 어려운 구조적 한계가 있었습니다. ### 데이터 쿼리 스케줄링의 단순화 * **가시성 기반 우선순위:** 화면에 보이는(visible) 위젯의 쿼리는 요청 즉시 실행하고, 화면 밖(offscreen) 위젯은 별도의 대기열에 추가하여 지연 처리합니다. * **고정 시간 윈도우 알고리즘:** 복잡한 계산 대신 2,000ms라는 고정된 시간 창(window) 동안 최대 10개의 태스크만 실행하도록 제한하여 작업 분포를 평탄화했습니다. * **대기열 관리 최적화:** 백엔드에 펜딩된 요청이 너무 많으면 실행을 일시 중단하며, 화면 밖 작업들은 대기열에 들어온 순서대로 처리하여 공정성을 유지합니다. * **도입 결과:** 로직이 단순해졌음에도 불구하고 서버의 '429(Too many requests)' 에러가 크게 감소했으며, 쿼리 재시도 횟수가 줄어들어 전체적인 데이터 로딩 속도가 향상되었습니다. ### Browser Scheduling API를 활용한 렌더링 최적화 * **자원 상태 인지:** 기존 스케줄러는 브라우저의 CPU나 메모리 가용 상태를 알지 못한 채 작업을 할당했으나, 새로운 시스템은 브라우저 네이티브 기능을 활용합니다. * **우선순위 세분화:** 최신 `Browser Scheduling API`의 `postTask` 메서드를 도입하여 작업의 성격에 따라 우선순위(user-blocking, user-visible, background)를 부여합니다. * **효율적인 메인 스레드 사용:** 브라우저가 직접 작업의 우선순위를 제어하게 함으로써, 중요한 UI 업데이트는 즉시 처리하고 덜 중요한 렌더링은 브라우저가 유휴 상태일 때 실행하도록 최적화했습니다. 복잡한 웹 애플리케이션에서 성능을 확보하려면 스케줄링 로직을 최대한 단순화하고 브라우저가 제공하는 표준 API를 적극적으로 활용해야 합니다. 이는 개발자에게는 시스템에 대한 통제력을 제공하며, 사용자에게는 더 매끄러운 인터랙션 경험을 선사합니다.

datadog원문

Husky: 대규모 환경에서의 정확히 한 번 데이터 수집과 멀티테넌시 (새 탭에서 열림)

Datadog의 3세대 이벤트 저장소인 Husky는 대규모 멀티테넌트 환경에서 데이터 중복 없는 '정확히 한 번(Exactly-once)'의 인입을 보장하기 위해 데이터 지역성(Locality) 기반의 라우팅 아키텍처를 도입했습니다. 스캔과 집계에 최적화된 Husky의 특성상 고성능 포인트 조회가 어렵다는 점을 극복하기 위해, 결정론적 샤딩을 통해 중복 제거의 범위를 샤드 단위로 한정하여 시스템 복잡도를 낮췄습니다. 이를 통해 테넌트별 데이터 격리 비용을 최소화하고, 가변적인 트래픽 상황에서도 효율적으로 스토리지와 컴퓨팅 자원을 확장할 수 있는 기반을 마련했습니다. ## 샤드 라우터를 통한 데이터 지역성 확보 * **결정론적 매핑**: 업스트림 서비스인 'Shard Router'를 사용하여 이벤트의 ID와 타임스탬프를 기반으로 특정 샤드(파티션 그룹)에 이벤트를 할당합니다. * **샤드 할당 전략**: 각 테넌트에게 고정된 리스트의 샤드를 할당하고, 해당 리스트 내에서 이벤트 ID를 해싱하여 샤드를 선택함으로써 무작위 라우팅을 방지합니다. * **테넌트 격리**: 개별 워커 노드가 노출되는 테넌트와 인덱스의 수를 최소화하여, 시스템 전체의 복잡도를 관리 가능한 수준으로 유지합니다. ## 데이터 지역성 도입의 기술적 이점 * **중복 제거(Deduplication) 효율화**: 동일한 ID를 가진 이벤트는 항상 같은 샤드로 라우팅되므로, 전체 시스템이 아닌 샤드 내부에서만 중복 여부를 확인하면 됩니다. 이벤트 ID 세트가 메모리에 수용 가능한 크기로 유지되어 처리 속도가 비약적으로 향상됩니다. * **스토리지 비용 절감**: Husky는 테넌트별로 데이터를 전용 테이블에 격리하며 파일 단위로 저장합니다. 지역성을 통해 워커당 처리하는 테넌트 수를 제한하면 생성되는 파일 수가 줄어들어, 클라우드 스토리지 비용과 후속 컴팩션(Compaction) 작업의 부하를 동시에 낮출 수 있습니다. * **성능 최적화**: 워커 노드가 처리해야 할 논리적 네임스페이스의 카디널리티(Cardinality)가 낮아짐에 따라 쓰기 성능이 개선되고 리소스 효율성이 높아집니다. ## 동적 환경에서의 라우팅 도전 과제 * **할당 변경 관리**: 특정 테넌트의 트래픽이 갑자기 수십 배 증가하거나 전체 샤드 수가 변경될 때, 기존의 결정론적 규칙을 유지하면서도 유연하게 샤드 배정을 변경해야 합니다. * **분산 노드 간 합의**: 모든 샤드 라우터 노드가 동일한 라우팅 규칙을 공유해야 중복 데이터 유입을 방지할 수 있으며, 이를 위해 노드 간의 일관성 있는 결정 메커니즘이 필수적입니다. * **부하 분산(Load Balancing)**: 모든 샤드에 인입 트래픽이 균등하게 배분되도록 설계하여 특정 워커 노드에 부하가 집중되는 '핫스팟' 현상을 방지해야 합니다. 대규모 분산 시스템에서 데이터 일관성을 유지하며 비용을 최적화하려면, 무상태(Stateless) 라우팅보다는 데이터의 특성에 맞춘 지역성 설계를 우선 고려해야 합니다. 특히 테넌트 수가 많은 SaaS 환경에서는 워커가 처리하는 테넌트의 카디널리티를 물리적으로 제한하는 것이 스토리지 관리 비용을 결정짓는 핵심 요소가 됩니다.

datadog원문

Datadog 에이 (새 탭에서 열림)

Datadog은 에이전트가 더 적은 CPU를 사용하면서도 더 많은 데이터를 빠르게 처리할 수 있도록 메트릭 식별 키(Metric Context) 생성 알고리즘을 최적화했습니다. Go 언어의 프로파일링 도구를 활용해 병목 지점인 태그 정렬 과정을 찾아냈으며, 특수화된 알고리즘과 해시 전략 수정을 통해 처리량을 대폭 개선했습니다. 결과적으로 동일한 리소스 내에서 더 많은 DogStatsD 메트릭을 수집하고 처리할 수 있는 성능 효율성을 달성했습니다. ## CPU 프로파일링을 통한 병목 지점 파악 * Go 언어의 런타임 도구와 플레임그래프(Flamegraph)를 사용하여 고부하 상황에서의 CPU 사용량을 분석했습니다. * 분석 결과, DogStatsD 서버가 샘플을 수신할 때 호출되는 `addSample`과 `trackContext` 함수가 가장 많은 CPU를 점유하고 있음을 확인했습니다. * 구체적으로 메트릭의 고유성을 보장하기 위해 수행하는 태그 정렬 알고리즘(`util.SortUniqInPlace`)이 전체 성능의 주요 병목 원인으로 지목되었습니다. ## 기존 메트릭 컨텍스트 생성 방식의 한계 * 메트릭 컨텍스트는 메트릭 이름과 태그 조합을 해시화하여 RAM 내 저장소의 키로 사용하며, 동일한 메트릭은 항상 같은 키를 생성해야 합니다. * 일관된 해시 생성을 위해 모든 태그를 정렬하고 중복을 제거하는 과정을 거치는데, 이 정렬 작업의 비용이 메트릭 양에 비례해 급격히 증가합니다. * 해시 충돌을 방지하면서도 수천 개의 메트릭을 초당 처리할 수 있을 만큼 알고리즘의 원시 성능이 매우 중요한 구조였습니다. ## 성능 향상을 위한 단계적 최적화 전략 * **코드 특수화(Specialization):** 태그의 개수에 따라 서로 다른 정렬 알고리즘을 적용하도록 최적화하여, 가장 빈번하게 발생하는 케이스에 대해 최상의 성능을 내도록 개선했습니다. * **해시 알고리즘 교체:** 마이크로 벤치마크를 통해 속도와 고유성이 뛰어난 **Murmur3** 알고리즘을 채택했습니다. * **Go 런타임 최적화 활용:** 기존 128비트 해시 대신 64비트 메트릭 컨텍스트를 사용하도록 변경했습니다. 이를 통해 Go 런타임의 최적화된 맵 접근 함수(`mapassign_fast64`, `mapaccess2_fast64`)가 작동하게 되어 맵 조작 속도를 높였습니다. * **근본적인 디자인 재설계:** 정렬이 성능의 가장 큰 장애물임을 인지하고, 정렬과 중복 제거에 의존하던 기존 알고리즘을 완전히 대체하는 새로운 설계 방식을 도입했습니다. 성능 최적화를 위해서는 단순히 하드웨어 사양을 높이는 대신, Go의 `pprof`와 같은 도구로 핫 패스(Hot path)를 정확히 진단하는 것이 우선입니다. 특히 대규모 데이터를 처리하는 시스템이라면 언어 런타임이 제공하는 하위 수준의 최적화(예: 특정 비트 수에 따른 맵 최적화)를 적극적으로 활용하고, 당연하게 여겨지던 정렬과 같은 알고리즘을 의심하여 재설계하는 과정이 필요합니다.