OpenAI 온라인 스토리지 Habitat 확장 사례

OpenAI Habitat 온라인 스토리지 확장과 Azure Cosmos DB 멀티테넌시 개선
2026년 10월 23일
Jon Lee, Hrachya Hakobyan, Chaomin Yu, 기술 스태프 멤버
모든 OpenAI 제품은 빠르고 신뢰할 수 있는 데이터 접근을 필요로 하며, 이는 우리의 스토리지 플랫폼 Habitat가 제공합니다. Habitat는 40개 지리적 지역에 걸쳐 10억 명이 넘는 사용자에게 밀리초 단위로 응답해야 합니다. 우리 인프라는 전례 없는 과제에 직면했습니다. 바로 폭발적인 성장과 새로운 사용 사례를 지원하면서도 신뢰성, 성능, 제품 개발 속도를 계속 개선해야 했기 때문입니다. 이 과제를 해결하려면 온라인 스토리지 플랫폼의 설계와 운영 방식을 다시 생각해야 했습니다. 우리는 항상 다음과 같은 순서의 트레이드오프를 검토했습니다. 먼저 핵심 제품 트래픽에 충분히 신뢰할 수 있게 만들고, 그다음 전 세계 사용자를 위해 충분히 빠르게 만들며, 마지막으로 대규모 환경에서도 능숙하게 운영할 수 있게 만드는 것이었습니다.
이 2부작 시리즈는 단순한 클라이언트 라이브러리와 데이터베이스에서 시작해 500페타바이트가 넘는 데이터를 제공하는 전역 분산 스토리지 플랫폼으로 Habitat를 어떻게 발전시켰는지 설명합니다. 1부에서는 Python 클라이언트에서 서비스로 발전해 가는 Habitat의 과정을 다뤘습니다. 또한 많은 사람들이 실용적이라고 생각하는 범위를 훨씬 뛰어넘어 Python 기반 서비스를 확장할 수 있게 해 준 기법들도 소개했습니다.
이 글인 2부에서는 기본 저장소 구성 요소인 Azure Cosmos DB를 어떻게 확장했는지 살펴봅니다. Azure 팀과의 긴밀한 협업을 통해, 우리는 공동 관리되는 컨테이너(co-managed containers)를 사용하는 실행 가능한 멀티 테넌시 접근 방식을 마련했고, 대규모의 전역 환경에서 뛰어난 읽기 성능을 확보했습니다.
회사가 성장함에 따라 인프라 팀은 흔히 단일 테넌트 시스템을 운영하는 방식에서 멀티 테넌트 시스템으로 진화합니다. 각 제품 영역 팀이 자체 시스템을 유지하는 단일 테넌시는 영향 범위를 줄이고, 소유 경계를 명확하게 하며, 워크로드 격리를 쉽게 해 줍니다. 하지만 팀과 제품 영역이 커질수록 운영 비용이 커집니다. 단일 테넌시는 모든 제품 팀이 용량 계획을 책임지게 만들며, 그 결과 상당한 과잉 프로비저닝이 발생하는 경우가 많습니다.
반대로 멀티 테넌시는 리소스 프로비저닝에 대해 더 나은 지식과 경험을 가진 중앙 데이터베이스 팀에 제어권을 부여합니다. 하지만 멀티 테넌시는 노이즈 네이버 문제와 같은 자체적인 문제도 만듭니다. 예를 들어 한 테넌트가 비정상적으로 많은 양의 리소스를 소비해 다른 테넌트에 피해를 줄 수 있습니다.
Habitat에서는 확장 가능한 신뢰성 높고 효율적인 스토리지 시스템을 설계하기 위해 이런 모든 트레이드오프를 저울질해야 했습니다. 오늘날 Habitat는 많은 OpenAI 제품을 위한 멀티 테넌트 데이터베이스 계층이지만, 우리는 하나의 멀티 테넌트 Azure Cosmos DB 컨테이너를 사용하다가, 백엔드 컨테이너를 단일 테넌트로 프로비저닝하는 방식으로 바꿨다가, 다시 기본값으로 멀티 테넌트 컨테이너로 되돌아오는 순환적인 과정을 거쳤습니다. Habitat에서의 멀티 테넌시 여정은 아직 초기 단계입니다. 우리는 다양한 사용 사례를 지원하지만, 가능한 한 Azure Cosmos DB의 내장 프리미티브를 활용함으로써 이 영역에 대한 투자는 뒤로 미룰 수 있었습니다.
Azure Cosmos DB 여정을 살펴보기 전에, 몇 가지 기본적인 맥락을 짚고 넘어가겠습니다.
- Azure Cosmos DB 계정은 컨테이너 집합과 복제 정책을 정의합니다
- 각 컨테이너는 초당 요청 단위(RU/s)로 일정량의 처리량이 프로비저닝됩니다
- 각 컨테이너 내부의 데이터는 일반적인 방식으로, 지정된 논리적 파티션 키를 기준으로 샤딩되어 저장되며, 논리적 파티션 키당 저장 공간과 처리량에는 문서화된 제한이 있습니다
- Azure Cosmos DB는 논리적 파티션 키를 더 적은 수의 백엔드 물리적 파티션에 걸쳐 샤딩하고, 저장 공간이 임계값을 초과하거나 컨테이너가 프로비저닝된 처리량을 유지하기 위해 더 많은 물리적 파티션이 필요할 때 이를 여러 물리적 파티션으로 분할합니다
우리는 처음에 모든 클라이언트 로드를 그대로 허용하고, 논리적 파티션 키당 10K RU/s라는 명시된 한도를 백프레셔로 활용했습니다. 시작할 당시에는 각 계정마다 기본 Azure Cosmos DB 컨테이너를 하나만 사용하기도 했습니다. 이 방식은 어느 정도 잘 작동했지만, 몇 가지 이유로 문제가 드러났습니다.
초기 API 선택 때문에 우리는 논리적 파티션 키 선택을 클라이언트에 직접 노출했습니다. 그 결과 user_id나 workspace_id처럼 자주 사용되는 파티션 키에서 서로 다른 제품 사용 사례 간 충돌이 자주 발생했습니다. 이러한 사용 사례들은 같은 논리적 파티션 키에 대해 Azure Cosmos DB 제한을 공유했기 때문에, 이 설계는 특정 사용자에 대한 전체 처리량을 제한했습니다.
Postgres를 Habitat와 Azure Cosmos DB로 옮기기 전에 확장하면서 우리가 자주 겪었던 한 가지 문제는, 매우 핫한 조직이나 핫 키에서 나온 많은 요청이—즉, 한 테넌트가 지나치게 높은 부하를 만들어 전체 데이터베이스를 쓰러뜨릴 수 있다는 점이었습니다. 이 문제를 막기 위해 상당한 공동 노력이 투입되어, 그런 키들을 캐싱하고 캐시 미스에 리스를 적용해 결과적으로 발생하는 캐시 미스 부하가 하위 데이터베이스를 짓밟지 않도록 했습니다.
문서화된 데이터 크기 및 처리량 제한은 논리적 파티션 키에 적용되지만, 실제로 제한이 강제되는 곳은 물리적 파티션입니다. 실제로는 논리적 파티션이 10,000 RU/s에 도달할 수 없다는 뜻입니다. 대신 그들은 해당 물리적 파티션에 함께 배치된 다른 모든 논리적 파티션 키와 물리적 파티션의 제한을 공유합니다.
이로 인해 시끄러운 이웃(noisy neighbors)이 서로 관련 없는 많은 논리적 파티션 키에 영향을 주었고, 니치한 제품 기능의 데이터에서 동작하는 워크로드가 핵심 로그인 및 가입 흐름을 방해했습니다.
주어진 물리적 파티션의 처리량 한도는 Azure Cosmos DB 컨테이너에 할당된 총 처리량에 따라 달라지는 가변값이기도 합니다. 컨테이너에 프로비저닝된 처리량은 해당 컨테이너의 물리적 파티션들에 대략 균등하게 분배됩니다.
데이터 볼륨이 커지면 물리적 파티션이 분할되어 총 파티션 수는 증가하지만, 전체 프로비저닝된 처리량은 변하지 않습니다. 그 결과 데이터 크기가 커질수록 파티션당 실효 처리량 한도는 줄어듭니다.
저장소 비중이 크고 blob과 유사한 사용 사례에서는, 요청률과 사용량은 안정적이더라도 저장소 증가만으로 활성 워크로드의 처리량 용량이 줄어드는 실패 모드가 생깁니다.
별도의 컨테이너는 이러한 파티션 키 제한과 시끄러운 이웃 문제를 부분적으로 해결하는 데 도움이 됩니다. 별도 컨테이너를 사용하면 각 컨테이너에 자체적인 독립 물리적 파티션을 할당할 수 있고, 그 컨테이너 자체에서만 변동하는 워크로드가 시끄러운 이웃이 되거나 파티션 키 공간에서 겹칠 수 있습니다. 반면 새 컨테이너를 프로비저닝하는 데는 0이 아닌 오버헤드가 있습니다—각 컨테이너마다 개별적인 용량 계획이 필요합니다—그래서 우리는 서비스나 사용 사례 경계에서 의미가 있을 때만 컨테이너를 만들고자 했습니다.
우리는 강한 격리가 필요한 가장 중요한 신규 사용 사례들을 위해 별도의 Azure Cosmos DB 컨테이너를 사용하기 시작했습니다. 각 컨테이너의 총 프로비저닝 처리량을 물리적 파티션 수와 동기화해 약한 파티션을 관리하는 스크립트와 결합하자, 우리가 겪고 있던 문제의 대부분을 피할 수 있었습니다.
하지만 기본 컨테이너에서 시작했기 때문에, 안타깝게도 많은 레거시 핵심 사용 사례가 데이터 마이그레이션 없이 그곳에 묶여 있었습니다. 여전히 공유 기본 컨테이너의 데이터에 의존하는 핵심 서비스를 보호하기 위해, 우리는 팀들에게 새 서비스나 주요 제품 기능은 별도 컨테이너를 만들라고 자주 권장했습니다. 우리가 확장함에 따라 기본 컨테이너에 대한 압박은 커졌고 운영 사고도 늘어났습니다. 우리는 기본 컨테이너를 더 많은 부하로부터 보호하기 위해, 새 서비스나 큰 제품 기능마다 컨테이너를 프로비저닝하기 시작했습니다.
이로써 많은 전사적 장애를 막을 수 있었고, 영향 범위를 기능 또는 서비스 성능 저하 수준의 장애로 제한할 수 있었습니다. 그러나 새 컨테이너를 프로비저닝하는 오버헤드는 결국 우리를 따라잡았습니다:
옛 공유 기본 컨테이너에서는, 어떤 새로운 사용 사례가 0 QPS에서 100만 QPS로 증가하더라도 전체 누적 사용량을 크게 바꾸지는 않았을 가능성이 높았습니다. 하지만 격리된 컨테이너에서는, 0에서 100만 QPS로 증가하는 새로운 사용 사례가 내부 도그푸딩에만 기인한 기준선보다 1,000배 더 많은 부하를 의미할 수 있었습니다.
전용 컨테이너를 쓰게 되자, 팀들은 이제 출시 전에 컨테이너를 예상 처리량 수준으로 미리 프로비저닝해야 한다는 점을 기억해야 했습니다. 제품 팀들은 기능을 프로덕션에 배포한 뒤 사전 확장을 잊는 경우가 많았고, 우리는 출시 당일 SEV 룸에서 막판에 컨테이너를 스케일 업하려 애쓰곤 했습니다.
이것은 멀티테넌트 시스템의 핵심적인 워크로드 혼합 이점인데, 우리는 이 컨테이너 전략으로 더 나은 격리를 택하는 대신 그 이점을 잃게 됩니다.
시간이 지나면서, 전용 컨테이너의 사용량을 모니터링하려면 트래픽이 언제 이동하거나 증가할지를 제품 차원에서 깊이 이해해야 했습니다.
성장 추세를 바탕으로 각 컨테이너의 프로비저닝 처리량을 늘리는 기본적인 제어 평면 스크립트를 제공할 수는 있지만, 안정적인 집계 성장 없이는 이를 올바르게 스케일링하는 일이 사실상 OpenAI의 모든 제품 팀이 용량 계획에 관여해야 함을 의미했습니다.
이것은 각 서비스의 소유 팀이 Azure Cosmos DB 프로비저닝에 얼마나 주의를 기울였는지에 큰 편차가 있어, 조직 차원에서 확장되지 못했습니다.
조직이 성장하면서 이러한 운영상의 문제는 확장하기 어려워졌습니다. 계획을 전담하는 단일 중앙 팀이 없었기 때문에 많은 팀이 계획을 제대로 세우지 못했습니다. 그 결과, 우리는 다시 멀티테넌트 솔루션 쪽으로 방향을 틀었고, Azure 팀과 긴밀히 협력하여 공유 테넌시 설계로 기존 멀티테넌시 문제를 해결하는 방안을 마련했습니다.
가장 뜨거운 키를 먼저 제한하면 시끄러운 이웃(noisy neighbor)이 주어진 물리 파티션의 가용 처리량을 모두 소비하는 것을 막을 수 있습니다. 이렇게 하면 하나의 뜨거운 키가 사이트 전체에 영향을 미치는 범위를 갖는 시끄러운 이웃 효과가 줄어듭니다.
요청 계층화 QoS 역시 대량 삽입이나 데이터 추출 같은 오프라인 워크로드가 주어진 물리 파티션의 가용 처리량을 모두 사용하지 못하도록 막습니다. 이러한 워크로드의 요청에 더 낮은 우선순위를 부여하면 먼저 제한(throttling)되므로, 최종 사용자에게 서비스를 제공하는 더 높은 우선순위의 대화형 요청에 대한 제한을 방지할 수 있습니다.
Azure Cosmos DB에는 이미 프로비저닝 모드에서의 약한 파티션 문제를 해결해 줄 서버리스 모드가 있었습니다. 하지만 큰 단점이 있었습니다. 이 모드는 스토리지 사용량만 기준으로 확장되었고, RU 사용량은 물리 파티션당 5K RU/s로 제한했습니다. 이는 우리의 약한 파티션 문제를 해결해 주겠지만, RU 사용량이 많은 사용 사례를 확장하는 것은 막았을 것입니다.
Azure Cosmos DB 팀이 우리를 위해 만든 새롭고 개선된 서버리스 모드는 각 물리 파티션이 최대 10K RU/s까지 확장될 수 있게 했고, 스토리지 사용량 기반 분할뿐 아니라 RU 사용량 기반 분할도 도입했습니다. 덕분에 우리는 더 이상 컨테이너를 수동으로 모니터링하고 확장하지 않아도 되었습니다.
이러한 변경 덕분에 우리는 다시 하나의 공유 컨테이너를 사용할 수 있게 되었고, 워크로드 간 격리는 유지하면서 제품 팀이 사전에 또는 지속적으로 용량 계획을 세워야 하는 필요성을 크게 단순화했습니다. 제품 개발을 가속화하는 동안에도 이후 몇 달간 프로비저닝이나 시끄러운 이웃 관련 사고는 한 건도 없었습니다.
기업이 OpenAI 규모로 성장하면, 관리형 서비스는 덜 “관리형”이 됩니다. 많은 워크로드가 클라우드 제공업체 인프라에서 예외적인 존재가 됩니다. OpenAI가 Azure Cosmos DB의 최대 사용자 중 하나가 되면서, 이를 성공적으로 운영하려면 다른 모델이 필요했습니다. 즉, 공유 테스트, 공유 대시보드, 직접적인 엔지니어링 채널, 그리고 우리 규모에서만 나타나는 장애 유형에 대한 공동 책임입니다. 오늘날 우리의 가장 큰 Azure Cosmos DB 계정은 수십 페타바이트의 데이터를 저장하고 2,000만 QPS 이상을 처리합니다. 우리가 아는 한, 이 규모로 Azure Cosmos DB를 배포한 회사는 우리뿐입니다.
2024년 말, 지역 장애는 왜 이러한 협업이 필요한지를 분명히 보여주었습니다. 당시 우리 가장 큰 Azure Cosmos DB 계정 중 하나의 기본 Azure 지역에서 정전이 발생했습니다. 그에 따른 장애 조치 연습에서 우리는 가장 큰 계정들이 보조 지역으로 장애 조치하는 데 몇 시간이 걸린다는 사실을 깨달았습니다. 이는 우리 팀도, Azure Cosmos DB 팀도 우리의 규모에서 테스트해 본 적이 없는 일이었습니다.
약 한 달 뒤, 해당 계정들을 원래 지역으로 다시 장애 복구하던 중 또 다른 심각한 장애를 일으켰습니다. 원래 지역으로의 연결 급증이 원래 지역의 Azure Cosmos DB 게이트웨이를 압도한 것입니다. 게이트웨이 모드의 Azure Cosmos DB 클라이언트가 장애 조치를 처리하는 방식의 특성 때문에, 일부 물리 파티션만 그 지역으로 돌아왔음에도 모든 클라이언트가 즉시 원래 지역에 연결을 맺고 요청을 보내게 되었습니다.
각 장애 때마다 OpenAI와 Azure는 모두 사고 대응 절차를 수행했고, 여러 근본 원인이 식별되었으며 수정 사항이 배포되었습니다. 그러나 이러한 장애는 우리 팀과 Azure Cosmos DB 팀이 운영하고 테스트하는 방식 사이의 더 큰 시스템적 문제의 증상이었습니다. 처음에는 관리형 서비스를 활용함으로써 빠르게 움직이고 비즈니스 목표에 집중할 수 있었습니다. 그러나 규모가 커지면 관리형 서비스는 덜 관리형이 되며, 설계에서 운영에 이르기까지 모든 수준에서 서비스 제공업체와의 깊은 통합이 필요합니다.
오늘날 Habitat와 Azure 엔지니어링 팀은 서비스 계층부터 Azure Cosmos DB의 물리적 스토리지 계층에 이르기까지 Habitat의 모든 계층을 공동으로 모니터링합니다. 우리는 프로덕션 규모이지만 프로덕션은 아닌 Azure Cosmos DB 계정을 대상으로 변경 사항을 운영하고 테스트합니다. 또한 SDK를 함께 테스트하고 반복 개선하며, Azure Cosmos DB 계정을 어떻게 확장하고 어떤 크기로 구성할지에 대한 모범 사례를 함께 결정합니다. Teams를 통해 서로에게 직접 엔지니어링 접근 권한을 가지고 있으며, 지원 계층을 거치지 않고도 서로에게 직접 페이지를 보낼 수 있는 방법까지 마련해 두었습니다.
지난 1년 동안 Azure Cosmos DB의 제어 평면과 게이트웨이 확장성을 개선하기 위한 공동 작업이 상당히 많이 이루어졌습니다. 여기에는 Azure Cosmos DB 메타데이터 파티션을 보호하기 위해 게이트웨이에서 다양한 Azure Cosmos DB 메타데이터에 대한 부하 보호와 캐싱을 추가하는 작업, 연결 풀링과 더 높은 연결 재사용을 통해 Python habitat-service 프로세스 집합이 주는 압력을 줄이는 작업, 가능한 경우 워크로드를 같은 위치에 배치하는 작업, 그리고 서로 다른 VPC를 통해 오프라인 워크로드와 중요한 워크로드를 분리하는 작업이 포함됩니다.
이 수준의 공동 엔지니어링은 지난 1년간 Azure Cosmos DB의 안정성을 확보하는 데 핵심적이었습니다. 충분히 크거나 중요한 모든 시스템에는 이런 종류의 협업이 필요합니다. 즉, 소유권의 벽을 허물고 조직 경계를 넘어 운영하는 것입니다.
Part I에서 언급했듯이, 우리의 핵심 철학은 다음과 같습니다. 작동하게 만들고, 빠르게 만들고, 저렴하게 만든다. 신뢰성이 우선이고, 그다음이 성능, 그리고 효율성입니다. Habitat는 OpenAI의 핵심 인프라가 되었기 때문에 첫 번째 우선순위는 신뢰성을 확보하는 것이었습니다. 2025년에 시스템이 안정화되고 OpenAI가 전 세계로 확장하면서, 사용자와 제품 팀에게는 지연 시간이 다음 제약 조건이 되었습니다.
컴퓨팅과 스토리지 용량은 종종 서로 다른 지역에 위치해 있었기 때문에, 지역 간 요청은 피할 수 없었습니다.
지연 시간은 데이터가 저장된 위치와 요청이 처리된 위치에 따라 크게 달랐습니다. Habitat 요청의 성능을 높이기 위해 제품 개발자들은 추가적인 클라이언트 측 캐싱, 요청 배치 처리, 데이터베이스 읽기 및 쓰기 최소화, 그리고 가능한 한 중요한 경로에서 작업을 분리하는 데 의존했습니다. 이는 데이터베이스 문제에 더 강한 흥미로운 제품 설계를 이끌어내기도 했지만, 동시에 제품을 더 복잡하게 만들고 제품 반복 속도를 늦췄습니다. 즉, 빠른 제품 개발을 장려하고 데이터베이스를 이해할 필요가 없게 하려는 Habitat의 목표를 달성하지 못한 셈입니다.
이것은 2026년 우리의 큰 집중 분야였으며, 우리는 읽기 성능을 최적화하기 위한 계층화된 전략을 개발했습니다. 이는 Habitat 요청의 90% 이상을 차지합니다.
데이터 주권 요건이 있는 사용자를 위해 전 세계 서로 다른 Azure 지역에 여러 개의 Azure Cosmos DB 계정을 운영하고 있습니다. 엄격한 데이터 주권 요건이 없는 사용자라도, 사용자 가까이에 데이터를 배치하면 지연 시간이 줄어들어 경험이 개선됩니다.
실제로는 물리적인 클라우드 용량 제약 때문에 데이터 주권 사용 사례를 제외하고는 이를 광범위하게 달성할 수 없습니다. 데이터 세트의 규모 때문에 많은 지역은 사용자 트래픽의 비례 몫을 감당하는 데 필요한 막대한 스토리지 규모를 수용할 수 없습니다.
데이터센터 용량, 전력 또는 냉각이 제한된 지역에서는 때때로 직접적인 상충 관계에 직면합니다. 즉, 공존 배치된 스토리지와 더 낮은 Habitat 지연 시간을 위해 용량을 예약할 것인지, 아니면 그 용량을 추가 서비스용 컴퓨팅에 사용하고 스토리지 요청을 더 먼 곳으로 라우팅할 것인지의 문제입니다.
그 결과 배치 결정은 종종 다음 사이의 제품 트레이드오프가 됩니다. A) 서로 다른 제품과 사용자 하위 집합에 대한 지연 시간 최적화, B) 순수한 스토리지 및 컴퓨팅 용량.
모든 요청에서 필요한 인증 메타데이터처럼 지연 시간에 가장 민감한 워크로드의 경우, 여러 보조 지역을 갖춘 고도로 복제된 Azure Cosmos DB 계정을 사용하여 컴퓨트와 Habitat 간 RTT를 줄입니다. 일부 계정은 우리가 트래픽을 제공하는 40개 모든 Azure 지역으로 복제되고, 다른 계정은 최소한 대륙당 하나의 지역으로 복제됩니다.
우리는 원래 원했던 것보다 더 이른 시점에 Habitat에 투명한 데이터 이동 및 라우팅 계층을 구축해야 했습니다. 그래야 서로 다른 복제 토폴로지를 가진 계정 간에 데이터 세트를 이동할 수 있기 때문입니다.
하지만 이 접근 방식 역시 결국 용량 제약을 받으며, 따라서 소수의 엄선된 데이터 세트에만 적용할 수 있습니다.
Habitat 워크로드는 압도적으로 읽기 중심이며, 애플리케이션은 설정, 상태, 기타 속성을 끊임없이 가져옵니다. 자주 접근되는 타입의 비교적 작은 작업 집합에 트래픽이 불균형하게 집중됩니다. 이 때문에 이 시스템은 분산 캐싱에 특히 잘 적합합니다. 단일 리전 인프라에서는 캐싱이 성능을 개선하지만, 여러 리전에서 작업할 때는 개선 효과가 더 커집니다. 이때 캐싱을 사용해 리전 간 지연 시간을 “가리는” 데 활용할 수 있기 때문입니다.
우리는 Habitat에 분산 룩어사이드 캐싱 계층을 통합했습니다. 이 계층은 사용자 요청이 처리되는 위치와 가까운 인프라의 Valkey 서버에 최근 접근된 객체와 엣지의 복사본을 저장합니다. 일반적으로 이런 종류의 캐시는 적중률과 데이터 신선도 사이에서 절충하는 TTL을 사용합니다. 짧은 TTL은 오래된 데이터의 가능성을 줄이지만, 항목이 캐시에서 금방 만료되어 더 자주 다시 가져와야 하므로 캐싱의 긍정적인 효과를 약화시킵니다. 긴 TTL은 적중률을 높이지만, 오래된 데이터를 장기간 제공하게 되어 사용자 경험이 나빠지고 제품 동작이 예측하기 어려워집니다.
다른 접근법은 애플리케이션이 같은 기반 데이터에 대해 쓰기 작업을 수행할 때 명시적인 무효화 작업을 실행하는 것입니다. 이는 캐시된 데이터의 최신성을 크게 높이지만, 제대로 구현하기가 어려울 수 있습니다. 또한 부분 실패 같은 여러 엣지 케이스의 영향을 계속 받으며, 이 경우 캐시가 오래된 상태로 남습니다. 다중 리전 배포는 신뢰성이 더 낮은 리전 간 네트워크와, 기반 저장소 복제와 캐시 갱신이 경합하는 상황 때문에 이러한 문제를 더 복잡하게 만듭니다. 여기에 TTL을 함께 사용하면 최대 신선도 저하를 제한할 수는 있지만, 앞서 설명한 동일한 TTL 절충안의 한계를 여전히 겪게 됩니다.
우리는 Meta의 TAO 같은 시스템의 방식을 따라, 더 고급의 무효화 접근법을 채택했습니다. Habitat의 통합 캐시는 Azure Cosmos DB의 change feed를 통한 change data capture(CDC)에 의존해, 무효화를 전역으로 수초 내에 전파하며 높은 적중률과 낮은 신선도 저하를 동시에 달성합니다. 우리 인프라의 한 서비스가 이 change feed를 소비하고, 기반 데이터가 갱신될 때 로컬 캐시에 대해 무효화 작업을 실행합니다. 그러면 다음에 해당 데이터를 읽을 때 miss가 발생하고, 캐시는 최신 버전의 데이터로 채워집니다.
또한 Habitat 서비스 자체의 오버헤드를 줄이기 위해 코드를 Rust로 다시 작성하여 지연 시간을 크게 낮췄습니다. Rust는 기존 Python보다 더 높은 성능을 제공합니다.
이러한 변화들을 통해 올해 초 성능에 집중하기 시작한 이후 전체 전역 읽기 p50이 대략 120 ms에서 대략 10 ms로 줄었고, 12배 개선을 달성했습니다.
캐싱은 중간값 지연 시간을 극적으로 줄였지만, 가장 느린 요청은 여전히 기반 저장소에서 캐시되지 않은 데이터를 가져와야 하며, 이 과정은 종종 먼 리전을 거쳐야 합니다. 더 많은 용량이 가동되고 저장 효율성이 개선되면, 우리는 더 근본적인 과제, 즉 애초에 데이터가 사용될 위치에 더 가깝게 배치하는 문제를 해결할 계획입니다.
신뢰할 수 있고 확장 가능한 CDC—데이터베이스 변경을 하위 시스템으로 전파하는 기능—는 Habitat의 캐싱 전략뿐 아니라 그 이상에서도 중요한 구성 요소입니다. 초당 수백만 건의 캡처된 변경 사항이 수천 개의 컨테이너와 수백만 개의 컨테이너 파티션 전반에서 흐르며, 100개가 넘는 Kafka 토픽을 거쳐 결국 일관성 지연 허용 보조 인덱스, 데이터 이동 파이프라인, 오프라인 분석, 기타 실시간 제품 기능을 구동합니다. 우리의 백엔드 인프라가 전 세계적으로 성장하면서, 행 변경을 감지하는 일은 점점 더 복잡해졌습니다. 클라이언트가 자신이 가진 타입의 행이 변경될 때마다 실행될 콜백을 등록하고 싶다면, 우선 자신의 데이터가 어느 Cosmos 계정과 컨테이너에 있는지, 각 지역에서 해당 컨테이너를 감시하는 CDC 서비스 배포가 무엇인지 파악해야 하고, CDC 서비스 워커 플릿, Kafka, 그리고 자신의 소비자도 적절히 확장해야 합니다. 우리 CDC 플랫폼은 이러한 모든 복잡성을 제품 개발자에게서 추상화하고, 대신 클라이언트가 변경된 행에 대해 호출되길 원하는 함수를 등록하면 나머지 연결 작업과 확장은 우리가 처리하는 단순한 인터페이스를 제공하는 것을 목표로 합니다.
내부 제어 평면은 필요한 모든 컨테이너가 지속적으로 캡처되고 있는지 확인하고, 어떤 Cosmos 리전에서 읽을지 선택하며, 여러 지리적 리전에 걸쳐 수천 명의 워커에게 작업을 효율적으로 분배합니다. 복제본 리전과 낮은 우선순위의 피드 읽기를 사용함으로써 지연 시간에 민감한 Habitat 트래픽을 백그라운드 캡처 워크로드로부터 보호합니다. 이처럼 큰 플릿에서는 워커 충돌, 컨테이너 문제, lease-store 오류, 잘못된 구성, 대상 장애가 끊임없이 발생합니다. 우리는 CDC 워커 수명 주기와 lease 처리를 강화하고, 스트림 간 장애를 격리했으며, 구성 변경과 롤아웃을 위한 안전장치를 구축했습니다. 또한 영구적인 전달 실패로 인한 헤드오브라인 차단을 막기 위해 dead letter queue를 활용합니다. 리더를 대체 Cosmos 리전으로 전환함으로써 지역 장애도 감내합니다. 우리의 규모에서는 Azure의 CDC SDK에 변경을 가해, 체크포인트를 메모리에서 일괄 처리하고 주기적으로만 플러시하도록 만들어 체크포인팅 구현을 더 효율적으로 만들기도 했습니다.
Habitat의 Azure Cosmos DB 내부 사용 방식은 복잡하지만, 그 가치 제안은 언제나 제품 개발자에게 인프라 세부 사항을 숨겨 제품 개발 속도를 높이는 데 있었고, 우리의 CDC 플랫폼도 다르지 않습니다.
작은 팀도 저장소 API를 의도적으로 좁게 유지하고, Python 서비스를 계속 확장하며, 가장 시급한 엔지니어링 과제에 우선순위를 두는 방식으로 빠르게 성장하는 저장소 플랫폼을 지원할 수 있었습니다. 우리는 먼저 미션 크리티컬한 제품 트래픽의 신뢰성에 집중했고, 그다음 전 세계 사용자를 위한 읽기 성능에, 마지막으로 대규모 운영 효율성에 집중했습니다. 이 접근 방식은 신뢰성, 성능, 제품 개발 속도를 개선하면서도 빠른 성장과 새로운 사용 사례를 가능하게 했습니다.
Habitat와 대화 저장소를 다음 10억 명의 사용자까지 확장하려면, 우리의 인프라 전반에 데이터를 더 큰 규모로 복제하는 어려움을 해결해야 합니다. 이를 선제적으로 대응하기 위해, 우리는 대용량 Habitat 데이터 유형에 최적화된 맞춤형 사내 엑사바이트 규모 온라인 저장 시스템을 구축하고 있습니다. 현재 개발 중인 이 시스템은 Rockset의 RocksDB Cloud 코어와 Raft 기반 복제를 결합합니다. Azure Cosmos DB와 동일한 문서 모델을 구현하지만, 분리형 저장소와 물리적 복제를 사용해 필요한 플래시 용량을 줄이는 대신 더 낮은 최대 qps를 감수합니다.
이런 종류의 프런티어 규모 데이터 인프라 작업에 관심이 있다면, [공개 채용 공고를 확인해 보세요](https://openai.com/careers/software-engineer-habitat-(online-data%29-seattle/).
작성자
OpenAI
계속 읽기
출처
- 원문: https://openai.com/index/scaling-storage-one-billion-users-part-two (2026-10-23 · openai.com)