Este artigo responde a uma pergunta específica – como avaliar uma API de backend de forma reproduzível – documentando o protocolo que aplicaremos na Aurabase antes de publicar quaisquer números de desempenho. Não resultados: um método. Qualquer medição já publicada em outro lugar neste site (principalmente em nossa página Desempenho) que ainda não dependa deste protocolo deve ser tratada como não verificada até novo aviso.
O essencial
Até o momento, não existem resultados de desempenho do Aurabase seguindo um protocolo publicado e reproduzível - este artigo documenta a metodologia que aplicaremos para produzi-los, e não os resultados já obtidos. O repositório já contém um conjunto de testes de 3 níveis: micro-benchmarks Criterion.rs em 3 caixas, testes de carga k6 em 8 cenários HTTP/WebSocket e um script Python para comparação direta entre Postgres e API com cálculo de percentil. O protocolo completo — duração da medição, percentis em vez de médias, isolamento do ambiente, divulgação de versão e data — é baseado em fontes externas verificadas: PostgreSQL, Criterion.rs, k6 (Grafana), HdrHistogram, PlanetScale e Convex. Quaisquer declarações de desempenho já publicadas em outro lugar deste site sem serem rastreadas até este protocolo devem ser consideradas não verificadas.
Por que não publicamos números simples
A Convex, editora de um back-end responsivo concorrente, distanciou-se publicamente do que sua equipe técnica chama de “guerra de gráficos de barras” entre provedores de banco de dados. Sua fórmula é direta: “É escalar teatro, não escalar” — escalar teatro, não escalar real (stack.convex.dev/on-competitive-benchmarks, Stack Technical Blog, acessado em 23 de agosto de 2026).
Seu argumento central: um benchmark que compara dois sistemas com diferentes garantias de consistência, topologia ou modelo de precificação muitas vezes não testa a mesma coisa, mesmo quando afirma fazê-lo — “o benchmark não está realmente testando a mesma coisa”. Este reflexo tem um nome na indústria: benchmarketing, publicando uma figura escolhida pelo seu efeito mercadológico e não pelo seu rigor metodológico.
A nossa resposta não é recusar medir – recusar publicar um número indefinidamente seria tão desonesto como publicar um número sem fundamento. É primeiro documentar como mediríamos, com que ferramentas e sob que condições, antes de afirmarmos ter medido alguma coisa. Isso também é o que distingue uma comparação útil (como nossa comparação Aurabase vs Appwrite, que documenta diferenças arquiteturais verificáveis) de uma comparação de números de desempenho sem um protocolo comum.
Isto é particularmente importante para um líder técnico ou um CTO que deve defender uma escolha de back-end num comité técnico: um número que não pode ser atribuído a um método não sobrevive à primeira pergunta, um tanto insistente. Um protocolo documentado é autodefensivo – você pode mostrar o script, a versão testada, e executar o teste novamente na frente de alguém, se necessário.
O que torna a maioria dos benchmarks de back-end enganosos
Duas armadilhas surgem sistematicamente: comparar diferentes topologias sem relatá-las e medir a latência de uma forma que oculte exatamente as pausas que mais importam para o usuário.
No primeiro ponto, PlanetScale documenta explicitamente sua restrição de paridade de hardware: cada ambiente comparado deve ser executado em recursos computacionais (vCPU, RAM) iguais ou superiores à instância de referência, na mesma região de nuvem (planetscale.com/benchmarks, metodologia “Telescope”, acessado em 23 de agosto de 2026). Sem essa disciplina, uma lacuna de latência pode simplesmente refletir uma máquina maior — e não uma arquitetura mais rápida.
O mesmo princípio se aplica ao estado do cache e à topologia da rede. Uma instância que acabou de ser iniciada (cache frio do Postgres, pool de conexões vazio, plano de consulta ainda não armazenado em cache) responde estruturalmente mais lentamente do que uma instância que está em execução há uma hora sob carga estável. Uma consulta da mesma região do banco de dados responde estruturalmente mais rápido do que uma consulta entre regiões. Dois benchmarks que não especificam nenhum deles simplesmente não são comparáveis, mesmo que exibam unidades idênticas.
No segundo ponto, a armadilha é chamada de omissão coordenada. HdrHistogram, o projeto de referência em medição de latência criado por Gil Tene, explica desta forma: quando um gerador de carga espera pela resposta de uma solicitação antes de enviar a próxima (loop fechado), uma pausa de serviço reduz automaticamente o número de solicitações enviadas durante a pausa - e, portanto, o número de medições de alta latência registradas (github.com/HdrHistogram/HdrHistogram, acessado em agosto 23, 2026). O projeto dá um exemplo concreto e quantificado: em um sistema hipotético que amostra sua latência a cada 10 ms durante 200 segundos, uma única pausa de 100 segundos no meio do teste é suficiente para produzir, sem correção, um histograma onde aproximadamente 99,99% das respostas parecem caber abaixo de 1 ms - mesmo que metade do tempo real tenha decorrido nesta única pausa.
Um teste de carga de circuito fechado que só envia uma solicitação após receber a resposta anterior subrepresenta sistematicamente pausas longas. O p99 exibido pode ser melhor do que a realidade vivenciada por um usuário real — não porque o sistema seja rápido, mas porque o protocolo de medição “esqueceu” de enviar as consultas durante a pausa.
Por que a média está: p50, p95, p99
Uma latência média pode parecer excelente quando uma em cada vinte solicitações leva cinco vezes mais tempo. Isto é precisamente o que os percentis revelam e o que a média esconde estruturalmente.
Mecanicamente, não há nada de misterioso em um percentil: classifique todas as latências medidas em ordem crescente e, em seguida, tome o valor na posição correspondente. De 1.000 consultas classificadas, p50 é o 500º valor, p95 o 950º, p99 o 990º. Uma única solicitação anormalmente lenta entre 1000 é suficiente para fazer o p99 se mover – é justamente sua sensibilidade a casos raros que o torna útil, onde essa mesma solicitação isolada quase não tem efeito na média.
Sinal revelador: o relatório de texto que pgbench - a ferramenta oficial de benchmark do PostgreSQL - exibe por padrão fornece uma média e um desvio padrão, não percentis (postgresql.org/docs/current/pgbench.html, acessado em 23 de agosto de 2026). Sua documentação oficial também alerta: “Nunca acredite em nenhum teste que rode apenas alguns segundos” – nunca acredite em um teste que rode apenas alguns segundos, o que se aplica tanto à duração quanto à métrica escolhida.
k6, a ferramenta de carregamento que usamos para o nível 2 de nossa suíte, resolve isso com limites expressos em percentil: a sintaxe p(95)<500 define um critério de aprovação/reprovação — 95% das solicitações devem responder dentro de 500 ms — diretamente na configuração de teste (grafana.com/docs/k6, consultado em 23 de agosto de 2026).
| p50 (mediana) | Metade das consultas são mais rápidas que esse valor | Esconde completamente a cauda de distribuição |
|---|---|---|
| p95 | 1 em cada 20 consultas é mais lenta | Área onde aparecem os primeiros usuários insatisfeitos |
| p99 | 1 em cada 100 consultas é mais lenta | Mais sensível à omissão coordenada se o protocolo for mal projetado |
Os 3 níveis de benchmark já presentes em nosso repositório
Publicar uma metodologia sem ferramentas reais seria apenas mais uma forma de teatro. A pasta benchmarks/ no repositório Aurabase já contém um conjunto de 3 níveis, inspirado em sua estrutura na metodologia pública Supabase - as ferramentas existem, os resultados medidos e datados ainda não existem.
Nível 1 - Critério de micro-benchmarks.rs
Três caixas do espaço de trabalho Cargo têm benchmarks dedicados vinculados à CPU: aura-crypto (hash Argon2, JWT HS256 — geração, validação e assinatura para PostgREST, criptografia AES-GCM), aura-db-adapters (filtros de análise e select no formato PostgREST — eq., gte., in.(), incorporações de relacionamento) e aura-core (serialização JSON, resolução schema_name, validação UUID).
aura-db-adapters mede especificamente o custo de análise de consultas no formato PostgREST - quatro casos para filtros (simple_4, complex_10, or_group, in_large_50 com 50 valores) e quatro para select (colunas únicas, *, uma relação incorporada, cinco incorporações). Este é o tipo de custo invisível em um teste de carga global: uma regressão na análise de um filtro or.(...) complexo não mudaria quase nada no p95 de um endpoint pouco utilizado, mas se tornaria mensurável em um endpoint com alto tráfego - daí o interesse em isolá-lo em um micro-benchmark em vez de depender apenas do nível 2.
aura-core adota uma abordagem diferente: em vez de medir o tempo bruto, ele mede a taxa de transferência (Throughput::Bytes) na serialização JSON e desserialização de mensagens NatsRequest/NatsResponse internas trocadas entre o gateway e os serviços - com três tamanhos de carga úteis realistas (uma solicitação mínima, uma solicitação com um corpo JSON aninhado, uma resposta de lista de 50 linhas).
Criterion.rs não apenas cronometra um loop. Ele primeiro executa uma fase de aquecimento para preencher os caches da CPU/SO, detecta outliers com uma versão modificada do método de Tukey (sem excluí-los do conjunto de dados), calcula intervalos de confiança inicializando um grande número de amostras reamostradas e detecta regressões de desempenho entre duas execuções pelo teste estatístico de Student, com um limite de ruído configurável - normalmente ± 1% - para ignorar variações que não são estatisticamente significativas (bheisler.github.io/criterion.rs/book/análise.html, acessado em 23 de agosto de 2026).
Cada execução do Criterion gera um relatório HTML detalhado em target/criterion/ — distribuições, gráficos de regressão, comparação com a execução anterior. É esta relação, e não apenas uma linha terminal, que uma metodologia séria deve permitir regenerar.
Nível 2 – teste de carga k6
Oito scripts k6 cobrem o gateway no lado do plano de dados: health (linha de base de latência), auth-flow (registro → login → atualização → logout), crud-read e crud-write, storage (upload/download), realtime-ws, breakpoint (aumento de carga até falha) e supabase-compare. Sete estão conectados a um alvo Makefile dedicado — supabase-compare.js existe no repositório, mas ainda não tem um alvo, um estado de coisas que este artigo documenta como está, em vez de disfarçá-lo.
A configuração compartilhada define limites por tipo de operação. Estes são critérios de aprovação/reprovação que o teste verifica cada vez que é executado – e não resultados já medidos:
| Leitura (GET) | p95 < 500 ms · p99 < 1000 ms | Configuração k6 (benchmarks/k6/lib/config.js) |
|---|---|---|
| Escrita (POST/PATCH) | p95 < 300 ms · p99 < 1000 ms | Configuração k6 |
| Autenticação (login/atualização) | p95 < 300 ms · p99 < 1000 ms | Configuração k6 |
| Armazenamento (upload/download) | p95 < 500 ms · p99 < 2.000 ms | Configuração k6 |
| Taxa de erro, todos os cenários | < 1 % | Configuração k6 |
O README.md na pasta benchmarks/ documenta um limite de leitura de p95 em < 200ms ("Supabase SLO"), enquanto o limite realmente aplicado em benchmarks/k6/lib/config.js — aquele que o teste executa — é p(95)<500. Os dois arquivos são derivados um do outro. Este é um exemplo concreto, encontrado durante a leitura do código-fonte deste artigo, de por que um protocolo deve ter uma única fonte de verdade com versão em vez de ser documentado em dois lugares: sem ela, mesmo uma equipe que tenta ser rigorosa acaba publicando limites conflitantes.
Nível 3 – Comparação direta entre PostgreSQL e API
Um script Python (direct_vs_api.py) mede a sobrecarga real da camada gateway + serviço comparando solicitações psycopg2 diretas com chamadas HTTP na mesma operação - lista, leitura única por id, leitura filtrada e classificada. Cada medição segue um aquecimento de 10 iterações antes do loop cronometrado e, em seguida, calcula a média, p50, p95, p99 e um rendimento em operações por segundo.
Um segundo script (aurabase_vs_supabase.py) aplica a mesma lógica de aquecimento e cálculo de percentil a uma comparação direta com uma instância local do Supabase (Supabase CLI, localhost:54321 por padrão) - mesma máquina, mesma rede local para ambos, exatamente a disciplina de paridade de ambiente que o PlanetScale documenta para suas próprias comparações.
Um script de orquestração (collect_baseline.sh, alvo bench-baseline de Makefile) conecta os três níveis - Critério nas 3 caixas, um subconjunto dos cenários k6 (health e crud-read hoje, nem todos os 8 ainda), então a comparação Python - e grava logs, relatórios JSON e Criterion HTML em uma pasta com carimbo de data / hora exclusivo: benchmarks/results/AAAAMMJJ_HHMMSS/. Este é exatamente o reflexo da divulgação datada, em uma única execução reprodutível, que a seção seguinte formaliza em um protocolo completo.
O protocolo que aplicaremos antes de publicar uma figura
Oito compromissos, cada um ancorado numa prática já documentada por uma ferramenta ou projeto de terceiros reconhecido – não inventado para a ocasião.
- Pré-aquecimento separado da medição. Criterion.rs preenche caches de CPU/SO antes do tempo;
pgbenchrecomenda explicitamente nunca acreditar em uma corrida que dura apenas alguns segundos. - Duração fixa, não um número fixo de iterações. Uma carga precisa de tempo para convergir - esta é a função de
stagesk6 e o sinalizador-Tdepgbench. - Percentis, nunca apenas a média — e vigilância ativa na omissão coordenada caso o gerador de carga opere em malha fechada.
- Ambiente documentado em detalhes: git commit do serviço testado, versão do PostgreSQL, especificação de hardware, versão da ferramenta de carregamento. O PlanetScale documenta seus parâmetros TPCC exatos (
TABLES=20,SCALE=250, ~500 GB) exatamente por esse motivo – sem esses detalhes, ninguém pode reproduzir uma execução. - Resultados com carimbo de data e hora e versão, nunca um único número gravado em uma página de marketing sem data. O ferramental atual já está gravado em um arquivo datado; será necessário alargar este reflexo a qualquer medição publicada publicamente, com a região anfitriã documentada como qualquer outra variável ambiental (ver o nosso guia sobre Soberania anfitriã da UE, relevante assim que um valor depender de uma determinada região).
- Scripts e dados brutos publicados junto com o resultado agregado, não apenas uma média final. A PlanetScale convida ainda os leitores a reportarem um erro metodológico num endereço dedicado – uma postura que consideramos saudável e que queremos retomar.
- Taxa de transferência anunciada junto com a latência, não apenas uma ou outra. Um sistema pode ter excelente latência em baixa carga e colapso na taxa de transferência à medida que a simultaneidade aumenta - isso é exatamente o que o cenário
breakpoint(escala até travar) de nossa suíte k6 foi projetado para revelar e o que a medição de microbenchmarkThroughput::Bytesdo Criterion captura no nível da função. - Lacuna significativa antes de anunciar uma melhoria. Uma variação de alguns por cento entre duas execuções pode ser um ruído de medição em vez de um ganho real — Criterion.rs calcula a probabilidade de que a diferença observada seja devida ao acaso antes de qualificá-la como regressão ou melhoria. Um número isolado, sem esta verificação, é apenas uma anedota estatística.
O que não faremos
Esta lista conta tanto quanto o protocolo positivo acima.
- Comparar diferentes topologias (instâncias auto-hospedadas versus gerenciadas, frias versus pré-aquecidas) sem reportá-las explicitamente.
- Retenha a melhor sequência de dez sem mencionar os outros nove.
- Publicar uma figura sem data, sem versão de serviço, sem roteiro de reprodução.
- Republicar uma figura de marketing existente, desde que não seja atribuída a este protocolo.
- Comparar-nos a um concorrente com base num valor bruto de desempenho se esse concorrente não publicar a sua própria metodologia de forma equivalente – um número versus silêncio não é uma comparação, é um slogan.
Um número como “partida a frio inferior a 1 ms” circulou sem ser apoiado por um benchmark reproduzível. Agora é tratado internamente como sem suporte e não deve ser lido como uma característica medida do produto até que qualquer medição datada, com metodologia publicada, o confirme. Este é precisamente o tipo de afirmação que este protocolo existe para evitar que se repita.
O protocolo mínimo para benchmarking de qualquer back-end
Este protocolo não depende de nenhuma ferramenta específica do Aurabase — você pode aplicá-lo à sua própria API hoje mesmo.
- Defina a carga antes de a ferramenta: somente leitura, gravação, mix realista para seu aplicativo - não uma proporção genérica copiada de outro projeto.
- Separe explicitamente a fase de pré-aquecimento da fase de medição.
- Execute o teste por tempo suficiente – minutos, não segundos.
- Medir em percentis (p50/p95/p99), nunca apenas em média.
- Verifique se o seu gerador de carga não está em malha fechada ou corrija a omissão de coordenação na análise.
- Isole o ambiente em teste – sem vizinhos barulhentos, sem tarefas concorrentes em segundo plano.
- Publique a versão testada, a data, as especificações de hardware e o script – não apenas o resultado final.
Em uma base simples do Postgres, este protocolo recebe um comando pgbench — 20 clientes simultâneos distribuídos em 4 threads, por 5 minutos, com um relatório de progresso a cada 10 segundos:
Ferramentas de referência, por nível
Cinco ferramentas, cada uma adequada a um nível diferente da pilha – nenhuma substitui as outras.
| Microfone (função) | Critério.rs | CPU pura, estatísticas de bootstrap |
|---|---|---|
| Consulta SQL | banco de dados | Transação semelhante a TPC-B, tps e latência |
| Carga HTTP/WS | k6 (Grafana) | Percentis, limites de aprovação/reprovação |
| OLTP em escala | sysbench + TPCC (metodologia do telescópio) | QPS, custo por desempenho |
| Correção de medição | Histograma HDR | Compensa a omissão coordenada |