Por que o ZippyDB precisava de uma camada de proxy
O ZippyDB é o key-value store mais usado do Meta, sustentando metadados de produtos, contadores e configurações a bilhões de operações por segundo. Mas o modelo de acesso direto criava uma malha densa de conexões TLS muitos-para-muitos: um cliente típico mantinha dezenas de milhares de conexões de saída, e um host de banco de dados típico aceitava dezenas de milhares de conexões de entrada. Cada novo grupo de clientes piorava cada host de banco de dados.
Proxies aparecem onde uma população grande e diversa de clientes fala com um backend compartilhado—connection poolers, sidecars de service mesh, edges de CDN, API gateways. Colocar uma camada entre muitos chamadores e um recurso compartilhado traz três benefícios: limita o problema, cria um lar para trabalho compartilhado e cria um ponto de controle. O tradeoff é um hop a mais e uma camada adicional para operar, mas compensa quando a população de clientes é grande, diversa e não é sua para mudar.
A vitória real não é a redução pontual de conexões—é a mudança no comportamento de escala. O fan-in se torna independente da população de clientes.
Esse é um padrão clássico de platform engineering: quando você não pode mudar um milhão de clientes, insira uma camada que você pode mudar. Para um estudo de caso relacionado sobre platform engineering em escala, veja como o Santander reduziu o provisionamento de infraestrutura de 90 dias para horas.
Fonte: Meta Engineering Blog

Colapsando o fluxo de requisições: batching e coalescing
O ponto de vantagem compartilhado do proxy permite algo que nenhuma biblioteca de cliente consegue: combinar trabalho entre chamadores não relacionados. Um batcher compartilhado em cada host agrupa requisições destinadas ao mesmo destino, com chave por caso de uso e shard físico, e as funde em um único RPC de backend.
# Ilustração conceitual de coalescing de requisições na camada de proxy
# (A implementação do Meta é em C++, mas o padrão é agnóstico de linguagem)
import asyncio
from collections import defaultdict
class BatchCoalescer:
def __init__(self, linger_ms=2, max_size=100):
self.linger_ms = linger_ms
self.max_size = max_size
# Mapa: (caso_de_uso, shard) -> lista de futures pendentes
self.batches = defaultdict(list)
async def request(self, use_case: str, shard: int, key: bytes):
loop = asyncio.get_event_loop()
fut = loop.create_future()
bucket = self.batches[(use_case, shard)]
bucket.append((key, fut))
if len(bucket) >= self.max_size:
await self._flush(use_case, shard)
else:
# Agenda um flush após a janela de linger
loop.call_later(self.linger_ms / 1000, self._schedule_flush, use_case, shard)
return await fut
async def _flush(self, use_case, shard):
batch = self.batches.pop((use_case, shard), [])
if not batch:
return
# Coalesce chaves duplicadas: busca uma vez, distribui o resultado
unique_keys = {k for k, _ in batch}
results = await self._backend_fetch(use_case, shard, unique_keys)
for key, fut in batch:
if not fut.done():
fut.set_result(results[key])
Dois benefícios não são sobre eficiência. Primeiro, coalescing sob uma hot key: milhares de chamadores simultâneos colapsam em uma única leitura de backend, então uma hot key nunca vira uma estampida contra uma réplica. Segundo, aposenta bibliotecas de batching client-side frágeis, que consumiam CPU e eram fonte constante de incidentes.
O batching vem com duas proteções: idle eviction (entradas do mapa de batch ociosas além de um TTL são apagadas no próximo flush) e um cap de in-flight (rejeita novas execuções quando corrotinas se acumulam mais rápido do que drenam).

A aritmética da redução de fan-in
Modele a frota como bolas em caixas. Jogue B bolas (shards que um host toca) em H caixas (hosts) e conte caixas distintas atingidas. O número esperado é:
E[caixas distintas] = H * (1 - (1 - 1/H)^B)
E uma caixa específica é atingida com probabilidade:
p = 1 - (1 - 1/H)^B
Fan-out é caixas distintas atingidas; fan-in é essa probabilidade vezes a população de chamadores. Com números fictícios—20 regiões, 500.000 hosts de banco, 30.000 hosts de proxy, 1.000.000 de clientes, 50.000 shards por cliente—o modelo resulta em ~19x de redução no total de conexões persistentes, porque cada conexão de backend multiplexa muitos clientes.
Mas a redução pontual não é o ponto principal. No modelo direto, o fan-in do host de banco é linear na população de clientes: cada novo grupo piora cada host. Com o ZGateway, o fan-in cai para aproximadamente R · S_host—regiões vezes densidade de shards por host—independente de ambas as frotas. Um número ilimitado dirigido por todos os outros se torna um número limitado que você controla.
Fique de olho nisso
- Hop extra e camada operacional: O proxy adiciona latência e uma frota para operar. Compensa apenas quando a população de clientes é grande, diversa e não é sua para mudar.
- Risco de OOM no batching: Manter requisições em memória é perigoso. O TTL de idle eviction e o cap de in-flight não são opcionais—são o que torna o batching seguro por padrão.
- Custo de reshuffling na camada de cache: Mover um peso em uma camada de cache significa mover chaves. O load balancer precisa limitar mudanças para evitar reshuffling caro.
- Hosts heterogêneos: Tratamento igual de hosts desiguais produz outliers quentes. Uma política fixa não serve tanto para uma camada calma quanto para uma em choque.
Próximos passos
Se você está projetando uma camada similar, comece medindo a distribuição de fan-in. Depois estude weighted consistent hashing estilo ServiceRouter e controle de concorrência AIMD. Para o lado do cliente, veja tendências de adoção de typing em Python e os desafios que permanecem.

Operando o ZGateway em produção
O ZGateway roda em dezenas de regiões em algumas camadas: uma grande camada de propósito geral para a cauda longa, camadas dedicadas para os maiores clientes e uma camada separada de proxy de alto throughput. Elas diferem em footprint por mais de uma ordem de magnitude, e camadas individuais não são uniformes devido ao stacking. Observabilidade rica por caso de uso é o que torna controle de admissão e load balancing seguros em infraestrutura compartilhada.
Isolamento de tenants e controle de admissão
Discriminant Load Shedding (DLS) mapeia cada requisição para um bucket por tenant, com chave por caso de uso e dividido por prioridade. Quando um tenant inunda a camada, seu próprio bucket enche e o excesso é descartado enquanto todos os outros buckets continuam drenando. Em uma sobrecarga controlada a >90% de CPU em ~1.350 buckets ativos, apenas 6—os reais vizinhos barulhentos—estavam shedding; os outros ~1.344 executaram 99,9% das requisições sem rejeições.
Resiliência cross-region
Três mecanismos estendem o ZGateway além da regionalidade estrita: global routing (tabela de roteamento entre regiões), mega-regions (agrupar regiões geograficamente próximas em uma localidade) e rings (declarar exatamente quais regiões se backupam, em que proporção). O sinal de failover importa tanto quanto o roteamento—uma média simples de CPU regional suaviza exatamente as condições quentes que você precisa capturar.
O caminho à frente
Três direções se destacam: heurísticas operadas por agentes (expor control loops como superfície estruturada para agentes de IA), co-location (empurrar parte do gateway ao lado do host ZServer para workloads críticos de latência) e um gateway multi-processo (dividir responsabilidades para isolamento duro de falhas). Juntas, transformam o ZGateway de uma camada inteligente em uma camada programável.
A lição: Quando você não pode mudar um milhão de clientes, construa a camada que você pode mudar. Batching, coalescing, controle de admissão e failover cross-region se tornam possíveis de uma vez—resolvidos uma vez pela equipe que conhece o backend melhor.