Dimensionar rapidamente o armazenamento online para mais de mil milhões de utilizadores do ChatGPT
Como adaptámos em Python a nossa plataforma de armazenamento de aplicações, o Habitat, para gerir um crescimento sem precedentes.
Por Jon Lee, Chaomin Yu e Ben Ries, membros da equipa técnica
Todos os produtos da OpenAI dependem de um acesso rápido e fiável aos dados, quer alguém esteja a iniciar sessão, a consultar as definições do Codex ou a iniciar uma nova conversa no ChatGPT. Cada uma dessas ações pode exigir muitas consultas de dados distintas antes de o produto poder responder. Se esses pedidos forem lentos, o produto parece lento. Se esses pedidos falharem, o produto deixa completamente de funcionar.
O Habitat é a plataforma de armazenamento online que criámos para os produtos da OpenAI acederem de forma rápida e fiável às informações necessárias. Atualmente, o Habitat processa mais de 70 milhões de pedidos por segundo e dá suporte a produtos utilizados semanalmente por mais de mil milhões de pessoas, em quase 40 regiões geográficas. O Habitat foi lançado inicialmente para dar suporte aos GPTs no DevDay 2023, começando como uma simples biblioteca Python do lado do cliente ligada a uma única base de dados. Hoje, é um sistema distribuído complexo que serve mais de 500 petabytes de dados.
Figura 01 · O que é o Habitat?
Plataforma de armazenamento online
O Habitat é a plataforma de armazenamento online que criámos para os produtos da OpenAI acederem de forma rápida e fiável às informações necessárias.
- Pedido
- Resposta
- Alterações (CDC)
Criar e operar uma infraestrutura a esta escala não é tarefa fácil, mas também não é particularmente difícil. O que tornou a nossa situação única foi a velocidade sem precedentes a que tivemos de crescer para acompanhar o extraordinário aumento de utilizadores e da procura dos produtos, enquanto construíamos simultaneamente uma plataforma madura. Muitas vezes, os engenheiros de sistemas projetam para uma escala dez vezes superior e esperam que seja suficiente durante alguns anos, enquanto se preparam para o aumento seguinte de dez vezes. No nosso caso, crescemos mais de dez vezes por ano em cada um dos últimos três anos. Por isso, criar e operar o Habitat implicou uma série de decisões táticas e de sequenciamento: compreender cada componente ao mais baixo nível para extrair o máximo da nossa pilha existente, enquanto evitávamos limitações de capacidade de armazenamento e computação para ganhar tempo para investimentos estruturais.
- 70 M+
pedidos por segundo
- 1 mil M+
pessoas por semana
- 500 PB+
dados
À medida que a OpenAI crescia, o Habitat teve de acompanhar: primeiro, tornando-se suficientemente fiável para o tráfego crítico dos produtos; depois, suficientemente rápido para utilizadores de todo o mundo; e, por fim, capaz de operar com agilidade a uma escala maciça. Esta publicação é a primeira de uma série de duas partes sobre a forma como dimensionámos o armazenamento online. Nesta publicação, explicamos como o Habitat evoluiu, porque o transformámos de biblioteca em serviço e como levámos um serviço escrito numa linguagem pouco habitual para este fim — Python — a tornar-se uma camada fiável da plataforma de armazenamento.
Numa futura publicação, explicaremos em pormenor como garantimos a fiabilidade da multilocação em grande escala, a nossa estratégia por camadas para otimizar o desempenho das leituras e como ampliámos a parceria com o Azure Cosmos DB para responder com fiabilidade a uma procura sem precedentes.
O Habitat nasceu de uma ideia simples: os engenheiros de produto não deveriam ter de pensar na gestão de bases de dados. O Habitat foi lançado inicialmente para dar suporte aos GPTs no DevDay 2023, como uma pequena biblioteca Python que interagia com o servidor principal do ChatGPT. Suportava um pequeno conjunto de operações que, nos bastidores, eram associadas à aplicação de base de dados Azure Cosmos DB.
A função da biblioteca era proporcionar às equipas de produto uma forma simples de armazenar e obter dados, sem terem de dominar os pormenores subjacentes. O Habitat tratava do trabalho necessário: determinar o tipo de dados em causa, a sua origem ou destino, se o pedido era permitido, entre outros aspetos.
Os engenheiros de produto não precisam de se preocupar com a consulta do schema, o encaminhamento, a autorização, a encriptação, a serialização, a formatação dos pedidos e o agrupamento de ligações. Nem sequer tinham de considerar a origem dos dados: Azure Cosmos DB, caches ou outros tipos de armazenamento.
Figura 02 · Serviço Habitat
Fluxo simplificado de pedidos do Habitat
Ao separar a lógica de armazenamento num serviço autónomo, criámos um ponto de controlo único para implementações, observabilidade e melhorias da plataforma.
- Pedido
- Resposta
Esta biblioteca Python funcionou bem e o Habitat foi rapidamente adotado pelos engenheiros de produto da OpenAI, apesar de não ter existido um esforço centralizado para abandonar a utilização autónoma do Postgres e do Azure Cosmos DB.
À medida que as necessidades dos produtos evoluíam, também era fácil para os programadores acrescentarem à biblioteca partilhada suporte para funcionalidades como colocação em cache do lado do cliente, compressão ou encriptação.
Em meados de 2025, o Habitat atingira os limites da sua implementação do lado do cliente. Com o aumento da complexidade da camada do Habitat e do número de serviços da OpenAI, tornou-se inviável alterar os protocolos com retrocompatibilidade.
Num dos casos, pretendíamos reduzir o impacto da indisponibilidade de uma única região nos nossos conjuntos de dados mais críticos, migrando-os para várias contas Azure Cosmos DB distribuídas regionalmente. Esta alteração exigia adicionar lógica de encaminhamento ao cliente, inicialmente desativada por um sinalizador de funcionalidade, implementá-la em todos os clientes e, depois, ativar o sinalizador.
Coordenar implementações em dezenas de serviços e colaborar com cada equipa na sua distribuição demorou vários dias. Antes da ativação, percebemos que queríamos introduzir alguma duplicação de tráfego para garantir que a lógica de fragmentação estava correta. A implementação demorou mais dois dias. E uma correção para algo que descobrimos estar errado? Mais dois dias. Por fim, estávamos prontos para ativar o sinalizador, mas uma das equipas reverteu o seu serviço por motivos alheios para uma versão anterior e defeituosa do cliente, provocando a interrupção que tanto nos esforçáramos por evitar.
As alterações à biblioteca do cliente exigiam uma coordenação complexa entre dezenas de serviços, num processo cada vez mais frágil, ineficiente e sujeito a falhas operacionais. Para reduzir esta dispersão operacional em futuras implementações, decidimos transformar o Habitat num serviço independente.
Ao separar a lógica de armazenamento num serviço autónomo, criámos um ponto de controlo único para implementações, observabilidade e melhorias da plataforma. Em vez de gerirmos atualizações fragmentadas, passámos a poder implementar melhorias de forma centralizada, beneficiando imediatamente todos os produtos da OpenAI.
Um serviço centralizado também nos proporciona um único ponto de controlo para oferecer as mais robustas primitivas de segurança e privacidade dos dados. É no serviço Habitat que podemos aplicar centralmente políticas de controlo de acesso, efetuar registos de auditoria e limitar o acesso a recursos de armazenamento subjacentes, como o Azure Cosmos DB. O Habitat desempenha um papel essencial na proteção dos dados dos utilizadores e na prevenção do acesso não autorizado por intervenientes externos, internos e agentes.
Sabíamos que precisávamos de um serviço, mas ainda não queríamos abandonar o Python, apesar da sobrecarga adicional de o usar num serviço. Usar Python num serviço com uma elevada taxa de processamento aumentou a latência da rede e implicou custos substanciais de escalabilidade da CPU e da memória, comparativamente à execução numa biblioteca local. Além disso, reconhecemos que as ineficiências do Python não seriam aceitáveis a uma escala 100 vezes superior, tornando quase certa uma futura reescrita.
No entanto, encarámos isto como uma incursão estratégica em dívida técnica. Nessa altura, o nosso principal objetivo não era otimizar custos ou recursos, mas sim desbloquear o trabalho dos programadores de produto e estabilizar a plataforma. Ao aceitarmos, a curto prazo, as desvantagens de desempenho de um serviço Python, pudemos dar prioridade a desafios mais imediatos, estabelecer as nossas APIs principais e criar uma infraestrutura robusta.
Também apostámos, de forma calculada, que a rápida evolução dos nossos próprios modelos de programação simplificaria o percurso técnico no futuro. Apostámos que, quando fosse necessário migrar totalmente do Python, o Codex e o GPT tornariam essa migração viável. Essa aposta acabou por se revelar acertada.
Em termos de desempenho, executar o Habitat como um serviço Python não seria ideal, mas era uma escolha necessária. O Python permite-nos avançar rapidamente, mas isso não significa que pudéssemos ignorar os riscos e aceitar latências significativamente piores. Quando um pedido médio de um utilizador origina centenas de chamadas à base de dados, é a chamada mais lenta que o utilizador sente. Concluímos que o principal desafio de executar um serviço Python a esta escala é gerir estas latências de cauda.
O asyncio ajuda o Python a executar em simultâneo cargas de trabalho limitadas por E/S, mas não permite contornar o GIL do Python nem obter paralelismo da CPU. Além do encaminhamento de pedidos com muita E/S, o Habitat trata de muitas tarefas intensivas para a CPU e em segundo plano: encaminhamento, compressão, encriptação, somas de verificação, verificação do estado de serviços a jusante, duplicação de pedidos e hedging.
Com tantas cargas intensivas para a CPU e tarefas em segundo plano no nosso serviço, o atraso de agendamento do asyncio pode facilmente dominar a latência de cauda dos pedidos. Antes de afinarmos o lançamento inicial do serviço, observámos nos rastreios de pedidos com latência p99 ou superior que, embora o armazenamento a jusante respondesse rapidamente, os pedidos ficavam muitas vezes bloqueados enquanto aguardavam que a corrotina responsável fosse reagendada para analisar a resposta.
Figura 03 · Monitorizar o atraso do asyncio
Concorrência não é paralelismo da CPU
O asyncio do Python permite processar pedidos em simultâneo, mas apenas um pedido é executado de cada vez no thread da CPU. Isto tem um grande impacto nas latências dos pedidos quando existe muito trabalho de CPU.
Carga reduzida da CPU
Etapas breves do Python; as esperas de E/S sobrepõem-seCarga elevada da CPU
Etapas longas do Python deixam respostas prontas à esperaNos serviços Python da OpenAI, além de medir as métricas habituais de utilização e saturação da memória, CPU, rede e disco, consideramos essencial monitorizar também o ciclo do asyncio e a respetiva ocupação, ajustando-o em conformidade.
Ao agendarmos periodicamente tarefas em segundo plano e registarmos a diferença entre o tempo de execução previsto e o real, conseguimos medir empiricamente e em tempo real o atraso de agendamento do ciclo de eventos. Com uma utilização elevada e muitas tarefas dispendiosas, mesmo um número modesto de pedidos simultâneos por processo basta para gerar uma variação significativa no agendamento, que pode atingir centenas de milissegundos e, em alguns casos extremos, vários segundos.
Por isso, limitamos cada processo a um pequeno número de pedidos simultâneos e aumentamos, em contrapartida, de forma maciça o número de processos de trabalho Python.
No lançamento inicial do serviço, a análise em produção do desempenho da CPU revelou uma causa fundamental do elevado atraso do asyncio — e das consequentes latências de cauda elevadas: a análise periódica, através do Statsig, do JSON das nossas configurações de sinalizadores de funcionalidades (uma ferramenta que gere estes sinalizadores e permite realizar testes A/B, entre outras funções).
Por predefinição, o Statsig procurava configurações atualizadas a cada minuto, sem variação aleatória, e a configuração incluía todas as regras de produção de todos os serviços. Noutra área, tomou-se a decisão arquitetónica de executar até oito processos Python por pod, para aumentar a utilização da CPU e reduzir as latências. Em conjunto, isto significava que, a cada minuto, todos os processos de trabalho de cada pod ficavam momentaneamente impedidos de processar pedidos em curso e gastavam os ciclos da CPU a analisar um enorme ficheiro de configuração.
Quando a análise do desempenho da CPU nos ajudou a identificar a causa, a solução foi simples: implementar uma configuração mais pequena e específica, aumentar o intervalo de atualização e introduzir alguma variação aleatória em tarefas em segundo plano como estas.
Para manter reduzido o atraso do asyncio, também é essencial distribuir bem os pedidos pelos processos de servidor; sem afinação, o agrupamento de ligações pode acabar por contrariar este objetivo.
Com o agrupamento de ligações do lado do cliente, um único processo cliente que efetue muitos pedidos simultâneos pode estabelecer apenas algumas ligações aos servidores e, por isso, enviar toda a sua carga para apenas alguns processos. Antes de ajustarmos o balanceamento de carga, a utilização do nosso serviço variava muito, com alguns processos de cauda a servir entre cinco e dez vezes mais pedidos simultâneos do que a média.
Descobrimos isto por acaso num incidente em que, apesar de termos parado o cliente que sobrecarregava parte do serviço, um subconjunto de processos continuou degradado muito depois do pico de tráfego. Na verdade, reparámos que esses processos sofriam uma degradação descontrolada, recebendo cada vez mais pedidos até os reiniciarmos. Quando um pod ficava sobrecarregado, determinado comportamento fazia convergir ainda mais tráfego para esse pod. Era uma classe de falhas que alguns colegas conheciam bem de trabalhos anteriores: uma falha metaestável(abre numa nova janela).
Suspeitámos do conjunto de ligações e testámos essa hipótese limitando a duração máxima de reutilização das ligações, o que efetivamente limitou a degradação e confirmou o rumo da investigação. Uma investigação mais aprofundada revelou que o TCPConnector do aiohttp para Python utiliza por predefinição a reutilização LIFO das ligações: a ligação devolvida mais recentemente é selecionada para o pedido seguinte. Normalmente, esta é uma predefinição razoável: reutilizar ligações recentes permite que as ligações adicionais criadas para lidar com picos de tráfego expirem por inatividade, reduzindo o custo de manter ligações adicionais. Neste caso, criou uma falha metaestável. Durante um pico de pedidos, os pedidos enviados para servidores mais lentos e sobrecarregados devolviam as ligações ao conjunto mais tarde, pelo que eram selecionadas com maior frequência pelos pedidos seguintes, concentrando gradualmente mais tráfego nos pods que já enfrentavam dificuldades. Alterar o conjunto de ligações para utilizar a reutilização FIFO interrompeu este ciclo de realimentação e reduziu também a variação dos pedidos no estado estável.
Figura 04A · Agrupamento de ligações do lado do cliente
O LIFO devolve novo trabalho ao processo lento
Após um pico de pedidos, os servidores mais lentos são os últimos a devolver ligações ao conjunto. O LIFO faz com que mais trabalho se concentre nesses mesmos servidores lentos.
Um pico inicial chega a A, B e ao processo C, que é mais lento.
Figura 04B · Agrupamento de ligações do lado do cliente
O FIFO interrompe o ciclo de realimentação da reutilização de ligações
O FIFO mantém mais ligações ativas após um pico, mas distribui as cargas de trabalho de forma equilibrada por todos os servidores.
Um pico inicial chega a A, B e ao processo C, que é mais lento.
Atualmente, dependemos sobretudo do Istio e do Envoy para oferecer agrupamento de ligações e melhores estratégias de balanceamento sensíveis à carga dos servidores em toda a infraestrutura da OpenAI, evitando por completo este problema.
Um efeito secundário da afinação para reduzir o atraso do asyncio e da existência de tantos processos Python é a grande facilidade com que se sobrecarregam as dependências a jusante com o enorme número de ligações, fenómeno conhecido como «manada trovejante».
Uma implementação diária normal, se não for configurada para avançar lentamente, pode causar uma utilização instável e significativa da CPU devido à renovação das ligações. Ou uma fuga de ligações pode deixar a rede indisponível ao saturar o gateway NAT. Estes problemas também não são raros noutros serviços, mas o limiar que os desencadeia é significativamente reduzido quando existem dez vezes mais processos, saturando muitas vezes recursos de rede que, com base apenas na taxa de processamento, os clientes não esperam ter de suportar num estado estável.
Também recorremos ao Envoy para maximizar a convergência das ligações. Utilizamo-lo para atualizar as ligações HTTP/1 do Python para HTTP/2, tirando partido da multiplexagem, e depois agrupar essas ligações e prolongar a sua duração. O Envoy também nos proporciona um local central para implementar limites de taxa e disjuntores, que seriam menos eficazes em cada processo Python autónomo.
Figura 05 · Convergência de ligações
Os mesmos pedidos, menos ligações
O agrupamento de ligações e a multiplexagem de ligações HTTP/2 ajudam a reduzir a carga de ligações nos serviços a jusante.
Uma das razões pelas quais conseguimos levar o Python a esta escala foi a API restrita do Habitat, que mantém previsível o custo dos pedidos. Em vez de permitir que os clientes criem consultas SQL arbitrárias, potencialmente geradoras de grandes pesquisas em tabelas ou junções entre muitas tabelas, o Habitat disponibiliza uma API NoSQL simples. A ausência de uma API avançada é uma opção deliberada na conceção do Habitat.
Procuramos otimizar pedidos simples, previsíveis e com uma carga de trabalho constante. Segundo a nossa experiência, estes sistemas são substancialmente mais fáceis de dimensionar e difíceis de configurar ou utilizar incorretamente. Os pedidos com expansão imprevisível são perigosos do ponto de vista operacional: complicam o isolamento e o balanceamento de carga, além de criarem aumentos abruptos de latência difíceis de acomodar tanto pelo serviço como pelos clientes.
Antes de migrarmos para o Habitat e o Azure Cosmos DB, a maioria dos dados online da OpenAI estava armazenada no Postgres. Na altura, era fácil rever todas as alterações a consultas e ao schema, garantindo que se comportavam devidamente e operavam sobre dados indexados antes de entrarem em produção. À medida que a equipa e os produtos cresceram, isto tornou-se rapidamente incomportável e passou a causar interrupções frequentes, nas quais uma única consulta nova e dispendiosa num caminho crítico deixava a base de dados indisponível.
O problema reside no desequilíbrio de custos: é barato e fácil escrever consultas SQL cuja execução é dispendiosa e difícil. No Habitat, evitamos esta situação e tornamos as consultas dispendiosas extremamente óbvias do lado do cliente. Não existem consultas ilimitadas que possam sobrecarregar o Habitat, e as junções complexas e travessias de grafos exigem que as equipas de produto assumam parte do trabalho pesado, o que favorece conceções globalmente mais eficientes.
O Habitat disponibiliza uma API NoSQL concebida em torno de tipos de objetos e de arestas definidos pelo cliente, inspirada no TAO(abre numa nova janela). Os clientes predefinem os objetos e as arestas, bem como as relações entre eles, mas não o conteúdo de cada tipo. As relações resultantes assemelham-se a um grafo, mas o próprio Habitat não suporta consultas típicas de travessia de grafos, exceto consultas às arestas diretas de um determinado objeto.
Dividimos este grafo de modo que cada objeto e as respetivas arestas fiquem juntos numa partição ao nível do armazenamento, mas não fazemos qualquer esforço deliberado, ao nível da base de dados, para colocar juntos os objetos e os objetos remotos para os quais apontam as suas arestas. Consequentemente, o modelo é facilmente particionado para permitir escalabilidade horizontal, mas as travessias de grafos são ineficientes, pois qualquer salto entre objetos pode exigir a obtenção de dados em duas contas Azure Cosmos DB totalmente distintas, armazenadas em regiões diferentes.
Para clientes com necessidades de consulta mais complexas, disponibilizamos uma vista secundária offline do Habitat através do Rockset. Utilizamos captura de dados alterados (CDC) para transmitir, quase em tempo real, as alterações do armazenamento online para instâncias isoladas do Rockset. Cada equipa cliente é responsável por dimensionar a sua própria instância do Rockset em função das suas necessidades de consultas complexas.
O aprovisionamento do Rockset cria dificuldades adicionais para os clientes, mas consideramos que, neste momento, é a opção certa: fazer das consultas simples a predefinição e oferecer uma alternativa a quem precisa de consultas complexas. Esta conceção isola o nosso armazenamento online de cargas de trabalho analíticas e de pesquisa com muitas leituras.
Adiar durante um ano a reescrita do Python permitiu-nos concentrar em desafios mais urgentes e importantes durante o nosso hipercrescimento. Com o amadurecimento da plataforma, a aceleração contínua do crescimento e o facto de sermos o segundo maior serviço da OpenAI em número de núcleos — e o quarto na nossa presença no Envoy —, chegara finalmente a altura de deixar o Python para trás. No seu auge, o Python ajudou-nos a processar mais de 20 milhões de pedidos por segundo.
No segundo trimestre de 2026, com apenas dois engenheiros, o Codex e o GPT‑5.5, conseguimos reescrever todo o serviço em Rust. Este novo serviço Rust já processa 95% dos nossos pedidos de produção; nas próximas semanas, iremos descontinuar totalmente o Python. Os nossos dados mostram que o serviço Rust é seis vezes mais eficiente em termos de CPU e 15 vezes mais eficiente em termos de memória do que a versão Python, com latências médias e de cauda significativamente inferiores. Planeamos partilhar mais conclusões numa futura publicação do blogue.
O serviço Python — e agora Rust — é apenas uma das facetas do Habitat. Na segunda parte desta série, que explica como dimensionámos rapidamente o nosso armazenamento online para servir mais de mil milhões de utilizadores do ChatGPT, abordaremos a camada de armazenamento e a forma como o Habitat gere mais de 500 petabytes e mais de 70 milhões de pedidos por segundo.
Se pretende trabalhar em sistemas OLTP à escala de fronteira e tem interesse neste tipo de engenharia, consulte esta vaga na nossa equipa.


