Sempre que surge um artigo sobre a infraestrutura de uma grande empresa, metade dos comentários no Hacker News são variações de “claro, eles usam Kubernetes com 47 microsserviços e um banco de dados distribuído com protocolo de consenso próprio”. Mas quando não, quando descobrem que usam PostgreSQL simples com um único primary e disciplina, o silêncio se torna constrangedor.

Foi justamente o que aconteceu com a OpenAI.

Os números que ninguém esperava

Bohan Zhang, engenheiro de infraestrutura da OpenAI, publicou os detalhes de como eles escalam PostgreSQL para o ChatGPT. Os números:

  • 800 milhões de usuários
  • Um único PostgreSQL primary (writer) no Azure
  • ~50 read replicas
  • Milhões de queries por segundo
  • p99 entre 10-19ms
  • 99,999% de disponibilidade
  • Apenas um SEV-0 em um ano (e foi no lançamento viral do ImageGen, que trouxe 100 milhões de novos usuários em uma semana)

Leia isso de novo. Um. Único. Writer. Para 800 milhões de usuários.

“Mas vocês deveriam usar sharding”

Não. E a razão é brutalmente pragmática.

Shardear PostgreSQL teria exigido modificar centenas de endpoints na aplicação. Cada query que presume que todos os dados estão no mesmo banco de dados — que basicamente são todas — precisaria ser reescrita para identificar em qual shard cada dado está localizado.

O custo dessa migração? Meses de trabalho de engenharia, novos bugs espalhados por todos os lados, e um período de transição onde você precisa manter ambos os sistemas.

O que eles fizeram em vez disso? Identificaram os writes mais pesados e os transferiram para o Cosmos DB. Não porque o Cosmos fosse melhor que o PostgreSQL, mas porque esses workloads específicos se ajustavam melhor a um modelo de documentos. O restante — a imensa maioria da lógica de negócios — permaneceu no PostgreSQL.

Em bom português: em vez de complicar todo o sistema, isolaram o problema e o resolveram onde realmente precisava. Cirurgia com bisturi, não com motosserra.

PgBouncer: de 50ms para 5ms por conexão

Um dos primeiros gargalos identificados foi a latência ao estabelecer conexões. O PostgreSQL cria um processo para cada nova conexão. Com milhares de conexões simultâneas vindas de centenas de pods de aplicação, o overhead da conexão tomava 50ms antes mesmo de executar uma única query.

A solução: PgBouncer como connection pooler. Ele mantém um pool de conexões já estabelecidas e as reutiliza. O resultado: a latência de conexão caiu para 5ms. Uma redução de 90%, simplesmente trocando uma peça de encanamento.

Não é uma tecnologia nova. O PgBouncer está em produção há mais de 15 anos, em empresas de todos os tamanhos. Mas lá está ele: uma ferramenta calejada e “sem glamour” resolvendo um problema em uma das aplicações mais utilizadas do mundo.

O ORM que fazia joins de 12 tabelas

Essa é a minha parte favorita. Porque já vi acontecer em projetos dos meus alunos, em startups, e até em bancos. Está em todo lugar.

O ORM gerava queries com joins de 12 tabelas. Não porque alguém tenha planejado isso, mas porque os modelos estavam conectados entre si e o ORM, obedientemente, seguia essas relações até o final.

A solução não foi trocar de ORM, nem usar apenas queries manuais para tudo. Foi mover parte da lógica para a aplicação. Em vez de pedir ao PostgreSQL para lidar com um join monstruoso, eles dividiram em várias queries mais simples e reuniram os dados no código.

Isso é menos elegante? Sim. É mais rápido? Extremamente. Porque o PostgreSQL pode otimizar queries simples muito melhor do que um join de 12 tabelas com condições cruzadas. E porque você pode armazenar em cache os resultados parciais para reutilizá-los.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- ANTES: o ORM gera isso
SELECT u.*, p.*, s.*, t.*, ...
FROM users u
JOIN profiles p ON ...
JOIN settings s ON ...
JOIN teams t ON ...
JOIN ... -- 12 tabelas
WHERE u.id = $1;

-- DEPOIS: queries separadas, lógica na aplicação
SELECT * FROM users WHERE id = $1;
SELECT * FROM profiles WHERE user_id = $1;
-- cacheável, paralelizável, depurável

Cada query individual é trivial. O query planner as executa em microsegundos. E se uma falhar ou estiver lenta, você sabe exatamente qual é.

As defesas invisíveis

O que considero mais brilhante no artigo de Bohan Zhang não são os números impressionantes, mas as pequenas defesas que evitam que tudo desmorone:

idle_in_transaction_session_timeout

Se uma transação permanecer aberta sem realizar nenhuma operação, o PostgreSQL a mata após um tempo configurável. Por que isso importa? Porque uma transação aberta bloqueia o autovacuum. Sem o autovacuum, as tabelas aumentam, os índices se degradam, e eventualmente o banco de dados fica mais lento a cada dia.

É como deixar a porta da geladeira aberta. Nos primeiros 5 minutos, não há problemas. Mas se você esquecer durante toda a noite, no dia seguinte tudo estará na temperatura ambiente.

Alterações no schema com timeout de 5 segundos

Quando você faz um ALTER TABLE no PostgreSQL, é necessário obter um lock na tabela. Se houver transações longas em andamento, esse lock espera. E enquanto espera, bloqueia todas as novas queries. Uma migração de schema que leva 200ms pode derrubar seu banco de dados se houver uma transação antiga que não terminou.

A solução da OpenAI: SET lock_timeout = '5s'. Se a migração não conseguir o lock em 5 segundos, ela aborta. É melhor falhar rapidamente e tentar novamente do que bloquear todo o sistema esperando.

Rate limiting em 4 camadas

Não uma. Não duas. Quatro camadas de rate limiting:

  1. Edge/CDN — bloqueia tráfego abusivo antes que alcance a aplicação
  2. API gateway — limites por usuário ou chave de API
  3. Aplicação — limites por tipo de operação
  4. Banco de dados — limites de conexões e statement timeouts

Cada camada captura aquilo que a anterior deixou passar. Defesa em profundidade. A mesma filosofia de camadas que uso para as defesas contra alucinações, aplicada à infraestrutura.

Isolamento de workloads por prioridade

Nem todas as queries têm a mesma importância. Uma query para “mostrar o chat do usuário” é crítica — se falhar, o usuário vê um erro. Uma query para “gerar um relatório analítico” é importante, mas pode esperar 30 segundos.

A OpenAI direciona as queries por prioridade para diferentes read replicas. As réplicas de alta prioridade têm menos carga e respondem mais rápido. As de baixa prioridade podem carregar mais dados sem afetar a experiência do usuário.

É senso comum, mas exige disciplina. Você precisa classificar cada query, configurar o roteamento, e resistir à tentação de mandar tudo para a réplica rápida “porque é só mais uma query.”

Os backfills que demoram semanas

Quando você precisa preencher uma nova coluna para 800 milhões de usuários, não pode simplesmente fazer um UPDATE users SET new_column = computed_value. Isso bloquearia a tabela, saturaria o disco e provavelmente derrubaria o primary.

Na OpenAI, os backfills são executados com um rate limit rigoroso. Semanas. Um backfill que leva semanas para ser concluído.

Parece horrível? É o oposto. Essa é uma decisão de uma equipe que entende que a velocidade do backfill é irrelevante quando comparada à estabilidade do sistema. Melhor levar 3 semanas e ninguém perceber do que levar 3 horas e causar um SEV-0 às 2 da manhã.

A replicação em cascata que vem aí

Atualmente, eles têm cerca de ~50 réplicas conectadas diretamente ao primary. Cada réplica consome uma conexão de replicação e banda do primary. Com 50, isso é gerenciável. Com 100+, viraria um problema.

A solução que estão desenvolvendo: replicação em cascata. Réplicas que replicam de outras réplicas, e não diretamente do primary. Um modelo em árvore em vez de uma estrela. O primary envia dados para 5-10 réplicas de primeiro nível, e essas réplicas distribuem para o restante.

É a mesma ideia do BitTorrent. Em vez de todo mundo baixar do mesmo servidor, os nós compartilham entre si. Funciona para filmes pirateados, funciona para segmentos de WAL.

A lição que ninguém quer ouvir

A indústria tem uma obsessão por over-engineering. Toda semana surge um banco de dados novo que promete resolver problemas que a maioria das empresas não tem. E toda semana, equipes de engenharia adotam essas tecnologias porque “escalam melhor” ou “são mais modernas”, sem se perguntar se o PostgreSQL com um pouco de disciplina faria o trabalho.

A OpenAI — a empresa que está moldando o futuro da IA, com um dos produtos de maior crescimento da história — usa PostgreSQL. Com um único primary. Sem sharding. Sem um banco de dados distribuído exótico.

Eles usam PgBouncer (2007). Réplicas de leitura (conceito dos anos 90). Pool de conexões (tão antigo quanto bancos relacionais). Rate limiting (inventado antes de muitos de nós nascermos).

A mágica não está na tecnologia. Está na disciplina:

  • Queries simples em vez de joins monstruosos
  • Timeouts agressivos em vez de esperas infinitas
  • Isolamento de workloads em vez de “tudo no mesmo servidor”
  • Migrar apenas o que precisa ser migrado, em vez de reescrever tudo

Para sua próxima standup

Da próxima vez que alguém no seu time sugerir migrar para um banco distribuído, colocar sharding no PostgreSQL, ou adicionar uma fila entre a API e o banco “porque o sistema não escala”, mostre esses números.

800 milhões de usuários. Um primary. p99 entre 10-19ms. 99,999% de uptime.

E pergunte: “Nosso problema é realmente que o PostgreSQL não escala? Ou é que nossas queries são uma gambiarra?”

Porque quase sempre é a segunda opção.


Fonte: Inside the Postgres Setup Powering 800M ChatGPT Users — Bohan Zhang, OpenAI. Se você ler apenas um artigo sobre infraestrutura este ano, que seja este.