Este artigo baseia-se exclusivamente em fontes datadas de terceiros – nunca em uma cifra Aurabase inventada. Nenhuma latência p99 específica para nosso back-end está incluída. Escrevemos o núcleo da nossa aplicação em Rust, sem coletor de lixo: fato que pode ser verificado diretamente no código, no workspace Cargo e nos serviços axum. No entanto, ainda não publicamos uma metodologia de benchmark p99 reproduzível para demonstrar isso em números. Este texto explica um mecanismo, não um resultado medido.
O essencial
- p99 mede a consulta mais lenta entre cem – o local onde a pausa do coletor de lixo (GC) prejudica mais, não em média (Dean & Barroso, “The Tail at Scale”, Google, 2013).
- Um GC interrompe todo o programa (“stop-the-world”) para liberar memória não utilizada. Rust não possui GC: a memória é liberada no exato momento em que um valor sai do escopo, verificado pelo compilador.
- O Discord documentou um serviço de cache em 2020, onde Go acionava um ciclo de coleta de lixo pelo menos a cada dois minutos, com cada ciclo causando um aumento na latência (Discord Engineering Blog).
- Reduzir uma pausa no GC leva anos de engenharia, mesmo no Google: o coletor GB passou de 300-400 ms para 500 µs entre 2015 e 2018, sem nunca chegar a zero (go.dev).
- O núcleo backend do Aurabase é escrito em Rust, sem coletor de lixo – verificado no código. Nenhum número de latência do p99 Aurabase foi publicado até o momento: isso continua sendo um mecanismo, não uma medida.
Por que p99 não é mediano
Uma média esconde o essencial. Se 99 solicitações em 100 responderem em 5 ms e apenas uma levar 500 ms, a média permanecerá baixa. Mas um em cada cem usuários enfrenta uma espera cem vezes maior. O p99 mede exatamente esta solicitação: o centésimo mais lento, aquele que viola seu SLA enquanto seu painel de latência média permanece verde.
No Google, Jeffrey Dean e Luiz André Barroso formalizaram esse problema em “The Tail at Scale” (Communications of the ACM, vol. 56, 2013). A sua observação, frequentemente citada desde então: “Episódios temporários de alta latência que não são importantes em sistemas de tamanho moderado podem vir a dominar o desempenho geral do serviço em grande escala”. Resumindo: episódios ocasionais de latência, insignificantes em pequena escala, acabam dominando o desempenho percebido de um sistema distribuído.
Um back-end que processa milhares de solicitações por segundo certamente enviará, em um ponto ou outro, uma solicitação que cai durante uma pausa do GC. Em grande escala, este não é um caso incomum. Esta é uma certeza estatística.
O que um coletor de lixo faz e por que pausa tudo
Um coletor de lixo (GC) rastreia continuamente os objetos vivos de um programa — aqueles ainda referenciados em algum lugar — e libera a memória de objetos que se tornaram inacessíveis. Esse rastreamento é chamado de tracing: o GC percorre o gráfico de referência, marca o que ainda é usado e depois varre o restante.
O problema: percorrer este gráfico enquanto o programa continua a criar novas referências produz resultados inconsistentes. A resposta histórica, ainda usada como último recurso por muitos GCs modernos, é stop-the-world — todo o programa faz uma pausa durante a marcação e a digitalização. Quanto maior o heap, mais longa tende a ser a pausa: sua duração depende do tamanho dos dados ativos, não da carga de trabalho atual.
A maioria dos GCs modernos utiliza uma estratégia geracional: eles assumem que a maioria dos objetos morre jovem. As alocações recentes são, portanto, verificadas frequentemente, mas rapidamente, em uma pequena área de memória. Objetos que sobrevivem a vários ciclos migram para uma área maior, escaneados com pouca frequência — mas quando essa área precisa ser limpa, a pausa associada aumenta com seu tamanho. É esta quebra “grande”, e não as pequenas quebras “menores”, que domina o p99 de um serviço de alto tráfego e alta alocação.
Os GCs simultâneos e geracionais modernos reduzem a frequência e a duração dessas pausas, trabalhando em paralelo com o programa. Mas quase todos eles mantêm um mecanismo alternativo de parar o mundo para casos limítrofes – e reduzi-lo requer anos de engenharia. A seção 04 fornece um exemplo quantificado e fundamentado.
Discord, 2020: uma quebra de GC se torna um incidente de produção
Em fevereiro de 2020, o engenheiro Jesse Howarth publicou um post que se tornou referência no setor: “Por que o Discord está mudando do Go to Rust” (Discord Engineering Blog). O serviço relevante, Read States, gerencia o status de leitura de mensagens para milhões de usuários – dezenas de milhões de entradas por cache – com centenas de milhares de atualizações por segundo.
O diagnóstico é direto, citado como está no artigo: “Go forçará uma coleta de lixo a cada 2 minutos no mínimo”. Em outras palavras, Go aciona um ciclo de coleta de lixo pelo menos a cada dois minutos neste serviço — e cada ciclo produz um aumento na latência visível nos gráficos da equipe.
A equipe primeiro reduziu o tamanho do cache para suavizar os picos. O compromisso permaneceu desfavorável: menos pausas de GC, mas mais solicitações de perda de cache caindo no banco de dados – portanto, um p99 geral degradado em outros lugares. A solução básica foi reescrever o serviço em Rust, sem coletor de lixo para monitorar.
A postagem gerou um acalorado debate técnico: mais de 1.580 pontos e 642 comentários no Hacker News no mesmo dia de sua publicação (4 de fevereiro de 2020) — sinal de que o problema vai muito além do caso Discord.
Três anos de engenharia no Google para reduzir a pausa de 400 ms para 500 µs
O coletor de lixo Go ilustra a escala do esforço necessário para controlar uma pausa do GC – mesmo com os recursos de uma equipe dedicada do Google. Rick Hudson, líder técnico do Go GC, documentou essa história em duas postagens oficiais do blog Go.
| Antes de agosto de 2015 | 300-400ms | Colecionador histórico de Go, antes do redesenho |
|---|---|---|
| Agosto de 2015 · Vá para 1.5 | 30-40ms | Primeiro coletor concorrente, meta < 10 ms definida |
| 2016 · Ir 1.6 | < 10 ms (SLO mantido) | Objetivo inicial alcançado na produção |
| Março de 2017 · Vá para 1.8 | abaixo do milissegundo | Removendo a verificação de pilha stop-the-world |
| Agosto de 2017 · Vá para 1.9 | 100-200 µs (marca) | Novo benchmark informal mencionado pela equipe |
| 2018 · SLO anunciado | 500 µs por ciclo | Objetivo de serviço formalizado por Rick Hudson |
Fonte: “Getting to Go: The Journey of Go’s Garbage Collector”, go.dev, 12 de julho de 2018; e “Go GC: Priorizando baixa latência e simplicidade”, go.dev, 31 de agosto de 2015.
Três anos de trabalho dedicado reduziram a quebra típica por um fator de mil. Mas a pausa nunca desapareceu: é um objetivo de serviço (SLO), e não uma garantia absoluta de zero. Um traçado de GC deve, por construção, percorrer um gráfico de objetos vivos de tempos em tempos. A única variável ajustável é a frequência e a duração desta viagem – não a sua existência.
Esta escolha de prioridade não é neutra. Go visa principalmente serviços de rede e back-ends da web, onde uma pausa de várias centenas de milissegundos interrompe diretamente a experiência do usuário – daí o enorme esforço investido em latência, em vez de taxa de transferência bruta de GC. Outros tempos de execução gerenciados herdaram diferentes compensações, moldadas por seus casos de uso históricos, antes de ganhar terreno com seus próprios coletores de baixa pausa. O ponto comum permanece o mesmo: todos partem do traçado do GC, portanto de um mecanismo de pausa a ser minimizado – nunca eliminado pela construção.
Por que Rust não tem esse problema por construção
A ferrugem não reduz as pausas do GC: elimina o mecanismo que as causa. O compilador rastreia, na compilação, quem possui cada valor de memória - este éownership. Quando o dono de um valor sai do escopo, o Rust insere automaticamente a chamada que libera aquela memória, no mesmo local do código binário. Esse mecanismo é chamado de RAII (Resource Acquisition Is Initialization): a liberação é determinística, não agendada por um coletor de lixo em execução em segundo plano.
Alexandru Nedelcu, autor de um blog técnico reconhecido no ecossistema Scala/Rust, resume a compensação em um artigo recente: “A compensação que Rust faz é a facilidade de uso, em preferência ao desempenho com latência previsível e segurança” (alexn.org, 21 de julho de 2026). Rust troca um pouco da simplicidade da escrita por uma latência previsível.
O mesmo artigo resume porque os GCs modernos nem sempre são suficientes: "Os GCs modernos tentam fazer o seu trabalho de forma incremental e simultânea, sem afetar o programa. Mas a sua capacidade é limitada, recorrendo a um ciclo de GC de parar o mundo que congela todo o programa, afetando assim a latência".
Aqui está o mecanismo em cerca de dez linhas — um exemplo genérico, não um extrato do código Aurabase:
Nuance importante: nem tudo é de graça. Os tipos de contagem de referências (Rc, Arc) adicionam um pequeno custo a cada clone e lançamento. Este custo permanece local e determinístico. Nunca há uma pausa que congele o programa inteiro enquanto ele passa por uma pilha de memória.
Esclarecimento útil para um back-end assíncrono: o tempo de execução assíncrono Rust (tokio, usado por todos os serviços Aurabase) não tem nada a ver com um coletor de lixo. Ele agenda tarefas cooperativas em um conjunto de threads, mas nunca itera através de um gráfico de objeto ativo para liberar memória. A confusão é comum em ecossistemas onde o tempo de execução assíncrono e o GC são gerenciados pela mesma máquina virtual.
O que isso muda para um back-end de alto tráfego
Em um backend que atende milhares de solicitações simultâneas, a ausência de GC remove uma variável da equação p99. Não há mais necessidade de dimensionar um heap de memória, ajustar as gerações de um coletor ou monitorar um ciclo que pode cair no pior momento. A latência de uma solicitação individual depende do seu próprio trabalho, e não de algum evento global imprevisível em outra parte do programa.
O núcleo backend do Aurabase aplica este princípio: todos os serviços (aura-gateway, aura-auth, aura-db, aura-realtime, aura-storage, aura-functions, aura-ai…) são escritos em Rust, organizados em um único Cargo workspace. Isso pode ser verificado diretamente no repositório:
Extrato real de Cargo.toml, edição do espaço de trabalho 2021, resolvedor v2 - verificado no repositório Aurabase.
O que este facto não prova, nesta fase: um valor de latência p99 medido para Aurabase. Ainda não publicamos uma metodologia de benchmark reproduzível para nosso próprio back-end — este é um trabalho em andamento, não um resultado disponível hoje. A ausência de coletor de lixo é um mecanismo verificado no código. Isso por si só não é evidência da latência medida do p99. Tenha essa distinção em mente diante de qualquer argumento de marketing sobre o assunto, incluindo o nosso - consulte nossa comparação técnica Aurabase vs Supabase para obter detalhes da arquitetura.
Medir corretamente um p99 requer disciplina própria: condições de carga representativas, percentis calculados em uma janela deslizante suficientemente ampla e um ambiente de teste próximo à produção. Publicar uma figura sem esta metodologia é como publicar uma figura de marketing. Isto é exatamente o que nos recusamos a fazer neste artigo.
O que a ausência de GC não resolve
A remoção do coletor de lixo elimina apenas uma fonte de latência final – não todas. Um back-end Rust ainda pode mostrar um p99 degradado devido à espera da rede, um pool de conexões Postgres saturado, um bloqueio de banco de dados contestado, uma consulta SQL mal indexada ou uma chamada lenta de API de terceiros. O mecanismo descrito neste artigo elimina uma causa estrutural. Não fornece imunidade contra outras pessoas.
No Aurabase, por exemplo, cada serviço se comunica com o Postgres através de um pool de conexões (sqlx) e com outros serviços via NATS JetStream. Um pool subdimensionado, uma assinatura NATS de consumo lento ou uma consulta SQL sem um índice adequado produzem seu próprio pico de latência, independentemente da ausência de um coletor de lixo.
A conclusão prática: a ausência de GC é uma boa razão arquitetônica para escolher um back-end Rust para um sistema compatível com p99. Isto não é, por si só, uma garantia de latência – nem na Aurabase, nem em qualquer outro lugar. O método que importa permanece o mesmo: medir, publicar a metodologia e depois corrigir o que as medições revelam. Se você estiver migrando de um back-end com GC, nosso guia de migração Supabase para Aurabase detalha o que está mudando e o que permanece igual.