Escalando o armazenamento online para mais de 1 bilhão de usuários do ChatGPT
Como adaptamos o Habitat, nossa plataforma de armazenamento de aplicativos em Python, para lidar com um crescimento sem precedentes.
Por Jon Lee, Chaomin Yu e Ben Ries, membros da equipe técnica
Todos os produtos da OpenAI dependem de acesso rápido e confiável aos dados, seja quando alguém entra na conta, verifica as configurações do Codex ou inicia uma nova conversa no ChatGPT. Cada uma dessas ações pode exigir muitas consultas de dados separadas antes que o produto consiga responder. Se essas solicitações forem lentas, o produto parecerá lento. Se essas solicitações falharem, o produto deixará de funcionar completamente.
O Habitat é a plataforma de armazenamento online que criamos para os produtos da OpenAI acessarem as informações necessárias com rapidez e confiabilidade. Hoje, o Habitat processa mais de 70 milhões de solicitações por segundo e sustenta produtos usados semanalmente por mais de 1 bilhão de pessoas em quase 40 regiões geográficas. O Habitat foi lançado inicialmente para viabilizar os GPTs no DevDay 2023, como uma simples biblioteca Python do lado do cliente conectada a um único banco de dados. Hoje, ele é um sistema distribuído complexo que atende mais de 500 petabytes de dados.
Figura 01 · O que é o Habitat?
Plataforma de armazenamento online
O Habitat é a plataforma de armazenamento online que criamos para os produtos da OpenAI acessarem as informações necessárias com rapidez e confiabilidade.
- Solicitação
- Resposta
- Alterações (CDC)
Construir e operar uma infraestrutura nessa escala não é tarefa fácil, mas também não é algo particularmente desafiador. O que tornou nossa situação singular foi a velocidade sem precedentes com que precisamos escalar para acompanhar o enorme crescimento de usuários e da demanda pelos produtos, ao mesmo tempo que construíamos uma plataforma madura. Em geral, engenheiros de sistemas projetam para uma escala dez vezes maior e esperam que ela seja suficiente por alguns anos enquanto se preparam para o próximo salto de dez vezes. No nosso caso, crescemos mais de dez vezes ao ano em cada um dos últimos três anos. Por isso, construir e operar o Habitat exigiu uma sequência de decisões táticas: compreender cada componente no nível mais básico para extrair o máximo da nossa pilha existente, enquanto contornávamos limitações de armazenamento e computação para ganhar tempo para investimentos fundamentais.
- Mais de 70 mi
solicitações por segundo
- Mais de 1 bi
pessoas por semana
- Mais de 500 PB
dados
À medida que a OpenAI cresceu, o Habitat precisou acompanhá-la: primeiro, tornando-se confiável o suficiente para o tráfego de produtos de missão crítica; depois, rápido o bastante para usuários globais; e, por fim, capaz de operar com eficiência em escala gigantesca. Esta publicação é a primeira de uma série em duas partes sobre como escalamos o armazenamento online. Aqui, mostraremos como o Habitat evoluiu, por que o transformamos de uma biblioteca em um serviço e como levamos um serviço escrito em uma linguagem incomum para essa finalidade — Python — a se tornar uma camada confiável da plataforma de armazenamento.
Em uma publicação futura, explicaremos em detalhes como tornamos confiável a multilocação em escala, nossa estratégia em camadas para otimizar o desempenho de leitura e como ampliamos a parceria com o Azure Cosmos DB para atender com confiabilidade a uma demanda sem precedentes.
O Habitat nasceu de uma ideia simples: engenheiros de produto não deveriam precisar se preocupar com o gerenciamento de bancos de dados. O Habitat foi lançado inicialmente para viabilizar os GPTs no DevDay 2023, como uma pequena biblioteca Python que interagia com o servidor principal do ChatGPT. Ela oferecia um pequeno conjunto de operações que, nos bastidores, eram mapeadas para o aplicativo de banco de dados Azure Cosmos DB.
A função da biblioteca era oferecer às equipes de produto uma maneira simples de armazenar e recuperar dados sem precisar dominar os detalhes subjacentes. O Habitat cuidava do trabalho necessário: identificar o tipo de dado envolvido, de onde ele deveria vir ou para onde deveria ir, se a solicitação era permitida e assim por diante.
Os engenheiros de produto não precisavam se preocupar com consulta de schema, roteamento, autorização, criptografia, serialização, formatação de solicitações e pooling de conexões. Eles nem sequer precisavam considerar de onde vinham os dados: Azure Cosmos DB, caches ou outros tipos de armazenamento.
Figura 02 · Serviço Habitat
Fluxo simplificado de solicitações do Habitat
Ao separar a lógica de armazenamento em um serviço independente, estabelecemos um único ponto de controle para implantações, observabilidade e melhorias da plataforma.
- Solicitação
- Resposta
Essa biblioteca Python funcionou bem, e o Habitat foi rapidamente adotado pelos engenheiros de produto da OpenAI, mesmo sem uma iniciativa central coordenada para abandonar o uso autônomo do Postgres e do Azure Cosmos DB.
À medida que as necessidades dos produtos evoluíam, também era fácil para os desenvolvedores adicionar à biblioteca compartilhada recursos como cache, compactação ou criptografia no cliente.
Em meados de 2025, o Habitat havia chegado ao limite como implementação do lado do cliente. Com o aumento da complexidade da camada Habitat e do número de serviços da OpenAI, mudanças de protocolo compatíveis com versões anteriores se tornaram inviáveis.
Em uma ocasião, queríamos reduzir o impacto de uma eventual indisponibilidade regional sobre nossos conjuntos de dados mais críticos, migrando-os para um conjunto de contas do Azure Cosmos DB distribuídas regionalmente. Essa mudança exigia adicionar ao cliente uma lógica de roteamento extra, inicialmente desativada por um sinalizador de recurso, garantir sua implantação em todos os clientes e, então, ativar o sinalizador.
Coordenar implantações em dezenas de serviços e trabalhar com cada equipe para concluí-las levou dias. Antes de ativar o recurso, percebemos que queríamos introduzir algum espelhamento para confirmar que a lógica de fragmentação estava correta. A implantação disso levou mais alguns dias. E uma correção para algo que descobrimos estar errado? Mais alguns dias. Por fim, estávamos prontos para ativar o sinalizador, mas uma das equipes reverteu seu serviço por motivos não relacionados para uma versão anterior e defeituosa do cliente, causando justamente a interrupção que tanto havíamos trabalhado para evitar.
As mudanças na biblioteca cliente exigiam uma coordenação complexa entre dezenas de serviços, um processo que se mostrava cada vez mais frágil, ineficiente e sujeito a falhas operacionais. Para reduzir essa dispersão operacional em futuras implantações, decidimos transformar o Habitat em um serviço próprio.
Ao separar a lógica de armazenamento em um serviço independente, estabelecemos um único ponto de controle para implantações, observabilidade e melhorias da plataforma. Em vez de gerenciar atualizações fragmentadas, passamos a implementar melhorias centralmente, beneficiando de imediato todos os produtos da OpenAI.
Um serviço centralizado também nos oferece um único ponto de controle para aplicar os mecanismos mais robustos de segurança e privacidade de dados. No serviço Habitat, podemos aplicar centralmente políticas de controle de acesso, registrar auditorias e limitar o acesso a recursos de armazenamento subjacentes, como o Azure Cosmos DB. O Habitat exerce um papel crítico na proteção dos dados dos usuários e na prevenção de acessos não autorizados por agentes externos, internos e de IA.
Sabíamos que precisávamos de um serviço, mas ainda não queríamos abandonar o Python, mesmo com a sobrecarga adicional de usá-lo como serviço. Usar Python em um serviço com alta taxa de processamento aumentou a latência da rede e elevou significativamente os custos de escalabilidade de CPU e memória em comparação com a execução de uma biblioteca local. Além disso, reconhecemos que as ineficiências do Python não seriam aceitáveis em uma escala 100 vezes maior, o que tornava praticamente certa uma futura reescrita.
No entanto, encaramos isso como uma adoção estratégica de dívida técnica. Naquele momento, nosso principal objetivo não era otimizar custos ou recursos, mas destravar o trabalho dos desenvolvedores de produto e estabilizar a plataforma. Ao aceitarmos no curto prazo as concessões de desempenho de um serviço Python, conseguimos priorizar desafios mais imediatos, estabelecer nossas APIs centrais e construir uma infraestrutura robusta.
Também apostamos, de forma calculada, que o rápido avanço de nossos próprios modelos de programação simplificaria o caminho técnico no futuro. Apostamos que, quando fosse necessária uma migração completa do Python, Codex e GPT a tornariam viável. Com o tempo, essa aposta se mostrou correta.
Executar o Habitat como serviço Python não seria ideal em termos de desempenho, mas era uma escolha necessária. O Python nos permite avançar rapidamente, mas isso não significa que podíamos ignorar os riscos e aceitar latências muito piores. Quando uma solicitação média do usuário gera centenas de chamadas ao banco de dados, é a chamada mais lenta que o usuário percebe. Descobrimos que o principal desafio de operar um serviço Python nessa escala é gerenciar essas latências de cauda.
O asyncio ajuda o Python a executar simultaneamente cargas de trabalho limitadas por E/S, mas não contorna o GIL do Python nem oferece paralelismo de CPU. Além de encaminhar solicitações com uso intenso de E/S, o Habitat executa muitas tarefas em segundo plano e responsabilidades que exigem muita CPU: roteamento, compactação, criptografia, cálculo de somas de verificação, verificação da integridade de serviços downstream, espelhamento e redundância de solicitações.
Com tantas cargas de trabalho intensivas em CPU e tarefas em segundo plano no serviço, o atraso de agendamento do asyncio pode facilmente dominar a latência de cauda das solicitações. Antes dos ajustes para o lançamento inicial, os rastreamentos de solicitações com latência p99 ou superior mostravam que, embora o armazenamento downstream respondesse rapidamente, as solicitações frequentemente ficavam paradas à espera de que a corrotina responsável fosse reagendada para analisar a resposta.
Figura 03 · Monitoramento do atraso do asyncio
Concorrência não é paralelismo de CPU
O asyncio do Python permite processar solicitações simultaneamente, mas apenas uma solicitação é executada por vez na thread da CPU. Isso afeta muito a latência das solicitações quando há bastante trabalho de CPU.
Baixo uso de CPU
Etapas breves do Python; esperas de E/S se sobrepõemAlto uso de CPU
Etapas longas do Python mantêm respostas prontas em esperaNos serviços Python da OpenAI, além de medir métricas padrão de utilização e saturação de memória, CPU, rede e disco, consideramos essencial monitorar o loop do asyncio e seu nível de ocupação, fazendo os ajustes necessários.
Ao agendar periodicamente tarefas em segundo plano e registrar a diferença entre o horário de execução esperado e o real, conseguimos medir empiricamente e em tempo real o atraso de agendamento do loop de eventos. Com alta utilização e muitas tarefas dispendiosas, mesmo um número modesto de solicitações simultâneas por processo basta para gerar uma variação significativa no agendamento, chegando a centenas de milissegundos e, em alguns casos extremos, a vários segundos.
Por isso, mantemos cada processo atendendo apenas a um pequeno número de solicitações simultâneas e, em vez disso, ampliamos enormemente a quantidade de processos de trabalho Python.
No lançamento inicial do serviço, o perfilamento de CPU em produção revelou uma causa raiz do alto atraso do asyncio — e das consequentes latências de cauda elevadas: a análise periódica em JSON das configurações de sinalizadores de recursos pelo Statsig, uma ferramenta que gerencia esses sinalizadores e permite executar testes A/B, entre outras funções.
Por padrão, o Statsig consultava configurações atualizadas a cada minuto, sem variação de intervalo, e a configuração incluía todas as regras de produção de todos os serviços. Em outra frente, uma decisão de arquitetura estabeleceu a execução de até oito processos Python por pod para aumentar o uso de CPU e reduzir as latências. Combinados, esses fatores faziam com que, a cada minuto, todos os processos de trabalho de cada pod parassem em algum momento de processar solicitações em andamento e usassem seus ciclos de CPU para analisar um enorme arquivo de configuração.
A correção foi simples depois que o perfilamento de CPU revelou a causa raiz: implantar uma configuração menor e direcionada, ampliar o intervalo de atualização e adicionar alguma variação de intervalo a tarefas em segundo plano como essas.
Para manter baixo o atraso do asyncio, também é essencial distribuir bem as solicitações entre os processos de servidor; sem ajustes, o pooling de conexões pode acabar prejudicando esse objetivo.
Com o pooling de conexões no cliente, um único processo cliente que faça muitas solicitações simultâneas pode estabelecer apenas algumas conexões com o servidor e, assim, enviar toda a sua carga a apenas alguns processos. Antes de ajustarmos o balanceamento de carga, a utilização do serviço variava muito, e alguns processos na cauda atendiam de cinco a dez vezes mais solicitações simultâneas que a média.
Descobrimos isso por acaso durante um incidente: mesmo depois de interrompermos o cliente que sobrecarregava parte do serviço, um subconjunto de processos continuou degradado muito além do pico de tráfego. Na verdade, percebemos que esses processos sofriam uma degradação descontrolada e recebiam cada vez mais solicitações até serem reiniciados. Quando um pod ficava sobrecarregado, algum comportamento passava a direcionar ainda mais tráfego para ele. Era uma classe de falha que alguns colegas conheciam bem de trabalhos anteriores: a falha metaestável(abre em uma nova janela).
Suspeitamos do pool de conexões e testamos essa hipótese limitando a duração máxima de reutilização das conexões. Isso de fato conteve a degradação e confirmou o rumo da investigação. Uma investigação mais aprofundada revelou que o TCPConnector do aiohttp em Python usa por padrão a reutilização LIFO: a conexão devolvida mais recentemente é escolhida para a próxima solicitação. Em geral, esse é um padrão razoável: reutilizar conexões recentes permite que as conexões extras criadas para atender picos de tráfego expirem quando ociosas, reduzindo a sobrecarga de mantê-las. Nesse caso, porém, isso gerou uma falha metaestável. Durante um pico, as solicitações enviadas a servidores mais lentos e sobrecarregados devolviam conexões ao pool mais tarde. Por isso, essas conexões eram selecionadas com mais frequência pelas solicitações seguintes, concentrando gradualmente ainda mais tráfego nos pods que já enfrentavam dificuldades. Alterar o pool para usar reutilização FIFO interrompeu esse ciclo de retroalimentação e também reduziu a variação das solicitações em estado estável.
Figura 04A · Pooling de conexões no cliente
O LIFO envia novos trabalhos de volta ao processo lento
Após um pico de solicitações, os servidores mais lentos devolvem as conexões ao pool por último. O LIFO faz com que mais trabalho se concentre nesses mesmos servidores mais lentos.
Um pico inicial chega a A, B e ao processo mais lento, C.
Figura 04B · Pooling de conexões no cliente
O FIFO interrompe o ciclo de retroalimentação da reutilização de conexões
O FIFO mantém mais conexões ativas após um pico, mas distribui as cargas de forma equilibrada entre todos os servidores.
Um pico inicial chega a A, B e ao processo mais lento, C.
Hoje, dependemos principalmente do Istio e do Envoy para oferecer pooling de conexões e estratégias melhores de balanceamento com reconhecimento da carga dos servidores em toda a infraestrutura da OpenAI, evitando completamente esse problema.
Um efeito colateral de otimizar para um baixo atraso do asyncio e manter tantos processos Python é a facilidade de sobrecarregar dependências downstream com um número enorme de conexões, fenômeno conhecido como "efeito manada".
Uma implantação diária comum, se não for ajustada para ocorrer lentamente, pode causar uma rotatividade significativa de CPU devido ao ciclo de conexões. Ou um vazamento de conexões pode derrubar a rede ao saturar o gateway NAT. Esses problemas também não são incomuns em outros serviços, mas o limite para acioná-los cai muito quando há uma ordem de grandeza a mais de processos. Isso costuma saturar recursos de rede que, considerando apenas a taxa de processamento, os clientes não esperam precisar suportar em estado estável.
Também usamos o Envoy para maximizar a concentração de conexões. Nós o usamos para atualizar as conexões HTTP/1 do Python para HTTP/2, aproveitando a multiplexação, e depois agrupá-las e prolongar sua duração. O Envoy também oferece um ponto central para implementar limites de taxa e disjuntores, que seriam menos eficazes em cada processo Python independente.
Figura 05 · Concentração de conexões
As mesmas solicitações, menos conexões
O pooling de conexões e a multiplexação de conexões HTTP/2 ajudam a reduzir a carga de conexões nos serviços downstream.
Um dos motivos pelos quais conseguimos levar o Python tão longe foi a API limitada do Habitat, que mantém previsível o custo das solicitações. Em vez de permitir que os clientes criem consultas SQL arbitrárias, capazes de gerar grandes varreduras ou junções entre muitas tabelas, o Habitat oferece uma API NoSQL simples. A ausência de uma API avançada é uma concessão explícita no design do Habitat.
Buscamos otimizar solicitações simples, previsíveis e com volume de trabalho constante. Em nossa experiência, esses sistemas são muito mais fáceis de escalar e difíceis de usar de forma incorreta. Solicitações com distribuição imprevisível são perigosas do ponto de vista operacional: complicam o isolamento e o balanceamento de carga, além de introduzir aumentos abruptos de latência difíceis de acomodar tanto no serviço quanto nos clientes.
Antes da migração para o Habitat e o Azure Cosmos DB, a maior parte dos dados online da OpenAI ficava armazenada no Postgres. Na época, era fácil revisar todas as alterações de consultas e schema para garantir que fossem adequadas e usassem dados indexados antes da implantação em produção. Com o crescimento da equipe e dos produtos, isso logo se tornou inviável e passou a causar interrupções frequentes: uma única consulta nova e dispendiosa em um caminho crítico podia derrubar o banco de dados.
O problema está no desequilíbrio de custos: escrever consultas SQL caras e difíceis de executar é barato e fácil. No Habitat, evitamos isso e tornamos as consultas caras extremamente evidentes para o cliente. Não há consultas sem limites que possam sobrecarregar o Habitat. Junções complexas e percursos de grafos exigem que as equipes de produto façam parte do trabalho pesado, o que favorece designs mais eficientes no conjunto.
O Habitat oferece uma API NoSQL baseada em tipos de objetos e arestas definidos pelo cliente, inspirada no TAO(abre em uma nova janela). Os clientes predefinem objetos, arestas e as relações entre eles, mas não o conteúdo de cada tipo. As relações resultantes se assemelham a um grafo, mas o próprio Habitat não aceita consultas típicas de percurso de grafos, exceto consultas às arestas diretas de um objeto específico.
Particionamos esse grafo para que cada objeto e suas arestas correspondentes fiquem juntos em uma partição no nível de armazenamento, mas não fazemos um esforço coordenado no banco de dados para manter juntos os objetos e os objetos remotos apontados por suas arestas. Com isso, o modelo é facilmente particionado para escalabilidade horizontal, mas os percursos do grafo são ineficientes, pois qualquer salto entre objetos pode exigir buscas em duas contas totalmente distintas do Azure Cosmos DB, armazenadas em regiões diferentes.
Para clientes com necessidades de consulta mais complexas, oferecemos uma visualização secundária offline do Habitat pelo Rockset. Usamos captura de dados de alteração (CDC) para transmitir, quase em tempo real, as mudanças do armazenamento online para instâncias isoladas do Rockset. Cada equipe cliente é responsável por escalar sua própria instância do Rockset para atender às suas necessidades de consultas complexas.
Esse provisionamento do Rockset cria atrito adicional para nossos clientes, mas acreditamos que seja a concessão certa neste momento: tornar as consultas simples o padrão e oferecer uma alternativa a quem precisa de consultas complexas. Esse design isola nosso armazenamento online de cargas analíticas e de pesquisa com uso intenso de leitura.
Adiar por um ano a reescrita do Python nos permitiu priorizar desafios mais urgentes e relevantes durante nosso hiper crescimento. Com o amadurecimento da plataforma, a aceleração contínua do crescimento e o Habitat já sendo o segundo maior serviço da OpenAI em número de núcleos — e o quarto em presença do Envoy —, finalmente chegou a hora de superar o Python. Em seu auge, o Python nos ajudou a atender mais de 20 milhões de solicitações por segundo.
No segundo trimestre de 2026, com apenas dois engenheiros, Codex e GPT‑5.5, conseguimos reescrever todo o serviço em Rust. O novo serviço em Rust já processa 95% das solicitações de produção; nas próximas semanas, desativaremos completamente o Python. Nossos dados mostram que o serviço em Rust é seis vezes mais eficiente no uso de CPU e 15 vezes mais eficiente no uso de memória que a versão em Python, além de apresentar latências médias e de cauda significativamente menores. Pretendemos compartilhar mais aprendizados em uma futura publicação.
O serviço em Python — e agora em Rust — é apenas uma das facetas do Habitat. Na segunda parte desta série sobre como escalamos rapidamente nosso armazenamento online para atender mais de 1 bilhão de usuários do ChatGPT, falaremos sobre a camada de armazenamento e sobre como o Habitat atende mais de 500 petabytes e 70 milhões de solicitações por segundo.
Se você quer trabalhar com sistemas OLTP em escala de fronteira e se interessa por esse tipo de engenharia, confira esta vaga em nossa equipe.


