PRODPlataforma BaaS europeia soberanaAbra o painel →

Desempenho · 11 minutos de leitura

PostgREST: benchmark e limites reais na produção

Affane Daylami · Fondateur · 18 de maio de 2026

Voltar ao blog

O próprio PostgREST quase nunca é o gargalo. Em instâncias dedicadas do Aurabase, uma réplica é executada com 50 a 250 milicores de CPU e 64 a 128 MB de RAM. É um binário Haskell leve que traduz solicitações HTTP para SQL, nada mais. Os verdadeiros limites que aparecem na produção estão em outro lugar. Quatro deles aparecem com mais frequência: o orçamento para conexões Postgres que suas réplicas consomem e o custo de COUNT exato no MVCC. Um truncamento de resposta também pode permanecer invisível nos cabeçalhos, assim como uma janela de latência após cada migração de esquema.

Este texto em inglês foi gerado automaticamente a partir do original em francês e ainda não foi revisado.
Esta página foi traduzida automaticamente. A versão em inglês é oficial.

Nosso artigo sobre Compatibilidade com PostgREST detalha o que o servidor cobre funcionalmente (filtros, incorporação, RPC, RLS) e o que ele deixa para você. Este vem de outro lugar. Ele documenta, com o código Aurabase e a documentação oficial do PostgREST como fontes, onde e por que o PostgREST realmente estagna em escala. Não estamos reproduzindo aqui um banco de carga que não administramos. Nossa metodologia de benchmark explica por que um número isolado, sem um protocolo publicado, não nos parece confiável.

O essencial

  • O próprio PostgREST é leve: 50 a 250 milicores de CPU, 64 a 128 MB de RAM por réplica em instâncias dedicadas do Aurabase. A taxa de transferência HTTP bruta quase nunca é o fator limitante na produção.
  • O teto real é o orçamento de conexão do Postgres: PGRST_DB_POOL × réplicas. Verificado no código Aurabase: 20 conexões por projeto no nível dedicado (10×2), 4 no nível compartilhado (2×2). Esta é uma escolha deliberada para acomodar mais inquilinos no mesmo max_connections.
  • Prefer: count=exact força uma varredura MVCC cara em tabelas grandes. PostgREST documenta duas alternativas mais baratas: count=planned e count=estimated, custando um total aproximado.
  • Um teto db-max-rows (1000 linhas por padrão no Aurabase) trunca uma resposta SEM relatá-la em Content-Range (medido em condições reais, detalhado abaixo).
  • Após uma migração DDL, o cache do esquema PostgREST é recarregado de forma assíncrona. O gateway Aurabase tenta até 8 vezes (cerca de 3,5 segundos cumulativos no pior caso) antes de desistir, um comportamento documentado diretamente no código.
#
Metodologia

O que um benchmark PostgREST mede e o que não mede

Um teste de taxa de transferência HTTP no PostgREST mede principalmente o Postgres, raramente o PostgREST. O servidor é uma fina camada de tradução na frente da base. Na grande maioria das cargas do mundo real, o tempo de resposta é dominado pela consulta SQL executada, não pelo processo que a gerou.

O projeto PostgREST mantém um repositório dedicado para este tópico, PostgREST/postgrest-benchmark no GitHub, que rastreia variações de rendimento de lançamento para lançamento, em vez de publicar uma figura de marketing isolada. Não o executamos nem o republicamos aqui. Seus resultados dependem do hardware, do tamanho do esquemático e do cenário testado, exatamente as variáveis ​​que nosso próprio protocolo de benchmark exige que sejam documentadas antes de citar uma figura.

Abaixo do PostgREST, está pgbench que mede a camada que realmente importa: tempo de transação SQL sob carga simultânea. Esta é a ferramenta oficial de benchmark do PostgreSQL (postgresql.org/docs/current/pgbench.html, acessado em 24 de agosto de 2026). Em vez de reproduzir este protocolo aqui, este artigo documenta quatro limitações arquitetônicas concretas do PostgREST em produção, cada uma verificada no código-fonte do Aurabase ou na documentação oficial do projeto.

#
Verificado no código

A pegada real de uma instância PostgREST no Aurabase

Cada projeto do mecanismo Aurabase Postgres recebe duas réplicas PostgREST dedicadas, co-localizadas com seu cluster. O manifesto do Kubernetes que os implanta define recursos modestos.

50-250m
CPU POR RÉPLICA
solicitações → limites
64-128
MB RAM POR RÉPLICA
solicitações → limites
2
RÉPLICAS POR PROJETO
alta disponibilidade (P22)

O que essas réplicas realmente consomem não é CPU: são conexões com o primário do Postgres. Cada instância PostgREST se conecta diretamente ao primário (-rw), sem passar pelo pooler PgBouncer implantado para o locatário. Esta escolha já está detalhada em nosso artigo sobre Compatibilidade com PostgREST: o mecanismo de recarregamento de esquema LISTEN/NOTIFY requer uma conexão persistente, incompatível com um pooler em modo de transação. O que este artigo acrescenta: quanto custa realmente, em termos de conexões, e onde atinge o pico.

O tamanho deste pool por réplica (PGRST_DB_POOL) difere deliberadamente dependendo do nível do projeto, verificado em k8s_tenant.rs, a função que constrói o manifesto PostgREST para cada projeto:

RolamentoPGRST_DB_POOL/réplicaRéplicasConexões/projeto despertado
Dedicado (premium, A1)10 (padrão PostgREST)220
Compartilhado (frota, gratuito/profissional/equipe)2 (padrão Aurabase, reduzido)24
deploy/cnpg/tenant-postgrest.yaml (extrato real, valor substituído pelo provisionador)yaml
# Impressão digital de conexões por réplica no primário.
env:
  - { name: PGRST_DB_POOL, value: "PGRST_DB_POOL_VALUE" }  # 10 (dedicado) ou 2 (compartilhado)

No nível dedicado, a restrição é relaxada: um projeto possui seu próprio cluster CNPG, portanto seu próprio max_connections, sem vizinhos de sobra. Ao nível partilhado, vários projetos de uma mesma organização partilham um único cluster: é este contexto que torna decisivo o orçamento das ligações, desenvolvido na secção seguinte.

#
O verdadeiro teto

O orçamento de conexões decide quantos locatários estão funcionando ao mesmo tempo

Em um cluster Postgres compartilhado, não é a taxa de transferência HTTP que limita o número de projetos ativos simultaneamente. Este é o número de conexões que suas instâncias PostgREST mantêm abertas no primário, em comparação com o max_connectionsdisponível.

Aurabase deriva esse orçamento diretamente dos limites reais do cluster, verificados em fleet.rs: (max_connections − réserve superuser/CNPG − connexions réservées au pooler) ÷ connexions par projet éveillé, piso em 1. A reserva fixa é de 10 conexões (superusuário, gerenciador de instância CNPG, exportador de métricas, margem de administração do provisionador). Nos defeitos entregues (pool de 2 por réplica, 2 réplicas ou 4 conexões por projeto despertado), o cálculo fornece três orçamentos diferentes dependendo do nível de dimensionamento do cluster.

Orçamento para projetos ativos simultaneamente por nível, cluster Postgres compartilhadoNível gratuito: 5 projetos ativos simultâneos (max_connections 50, pooler 20). Nível Pro: 7 (max_connections 100, pooler 60). Nível da equipe: 10 (max_connections 200, pooler 150). Fórmula derivada do código Aurabase (fleet.rs::derive_wake_budget), reserva fixa de 10 conexões, 4 conexões por projeto acordado.024681012grátis (max_connections 50)5 projetosprofissional (max_conexões 100)7 projetosequipe (max_connections 200)10 projetos

Fonte: derivado de fleet.rs::derive_wake_budget e wake_budget_for_org_plan, código Aurabase, relido em 24 de agosto de 2026.

Este orçamento não é uma cota de projetos próprios: uma organização team pode manter 50 projetos, a maioria dos quais estão inativos. Este é um limite de simultaneidade : o número de projetos que podem manter conexões abertas ao mesmo tempo no primário. Um despertar acima do orçamento não falha, é adiado até que um projeto irmão volte a dormir, verificado no mesmo arquivo. O tópico de dimensionamento max_connections em si é expandido em nosso artigo sobre ajuste de max_connections, e a compensação dedicada/mutualizada como um todo em base dedicada vs. base compartilhada.

#
Custo oculto

Por que preferir: count=exact retarda uma consulta em uma tabela grande

Pedir um total exato força o Postgres a contar as linhas visíveis do resultado filtrado em cada consulta, um custo que cresce com a tabela, e não uma operação gratuita.

O PostgreSQL não mantém nenhum contador de linha indexado pronto para uso. No MVCC, a visibilidade de uma linha depende da transação que a lê. Um COUNT(*) exato deve, portanto, visitar as linhas candidatas em vez de ler um valor pré-computado. Esta é uma limitação estrutural bem documentada no ecossistema Postgres, incluindo fornecedores de análises como ClickHouse, que comparam seus próprios contadores aproximados com o comportamento transacional do Postgres.

terminalbash
# Caro em uma tabela grande: força uma varredura MVCC do resultado filtrado
curl "https://<gateway>/v1/db/<project_id>/orders?status=eq.paid" \
  -H "apikey: <clé>" -H "Prefer: count=exact"

# Alternativas menos caras, documentadas por PostgREST
  -H "Prefer: count=planned"   # estimativa via planejador
  -H "Prefer: count=estimated" # planejado além de um limite, exatamente abaixo

PostgREST documenta essas três estratégias de contagem nativamente (postgrest.org, acessado em 24 de agosto de 2026). A estratégia exact garante um total ao preço da varredura. planned retorna uma estimativa quase gratuita do planejador de consultas. estimated alterna automaticamente entre os dois com base em um limite. A escolha não é cosmética: uma paginação que requer count=exact numa tabela de vários milhões de linhas paga por esta verificação em cada página, mesmo quando o utilizador nunca consulta a última.

#
Medido em real

O truncamento de db-max-rows é invisível sem count=exact

Um limite de linha pode truncar uma resposta PostgREST sem qualquer indicação no corpo ou nos cabeçalhos, a menos que solicite explicitamente um total exato. Medimos isso em condições reais em uma instância dedicada do Aurabase, não presumida.

Em uma tabela de teste de 10 linhas com PGRST_DB_MAX_ROWS=5, PostgREST v12.2.3 renderiza exatamente o mesmo cabeçalho Content-Range para duas situações muito diferentes:

ConsultaLinhas renderizadasFaixa de conteúdometa (Aurabase)
?limit=50 (sem contagem)5/10 reais0-4/*{}
?limit=50&count=exato5/10 reais0-4/10{total: 10}

Sem count=exact, a resposta de 5 linhas é indistinguível de uma tabela que realmente conteria apenas 5: Content-Range: 0-4/* descreve as linhas renderizadas, nunca o limite aplicado. O limite real em vigor não aparece em nenhum lugar neste caso, medido diretamente no caminho PostgREST do SDK Aurabase.

Consequência para qualquer paginação no PostgREST

Se sua implantação PostgREST definir um db-max-rows (o padrão do Aurabase é 1000), um cliente que compara data.length ao limite solicitado para detectar uma página inteira pode estar enganado. O erro aparece assim que o teto do servidor for inferior a esse limite. O único sinal confiável é comparar o número de linhas recebidas com o total retornado por count=exact, o que coloca em jogo diretamente a compensação de custos descrita na seção anterior.

#
Latência adiada

Recarregando o cache do esquema após uma migração

PostgREST mantém o esquema Postgres na memória na inicialização. Após um DDL (criar tabela, adicionar coluna), esse cache deve ser recarregado antes que a nova rota responda, e esse recarregamento é assíncrono.

Uma gravação que chega nesta janela pode receber um transitório 404 (cache ainda não atualizado), mesmo que a tabela realmente exista no lado do Postgres. O gateway Aurabase absorve isso com um loop de repetição limitado, verificado em postgrest_proxy.rs: até 8 tentativas, aumentando a espera (250 ms mais 100 ms por tentativa), 3,5 segundos cumulativos no pior caso. Este mecanismo afeta apenas gravações, nunca leituras.

Um detalhe que o próprio código documenta

O gateway não emite nenhum sinal de recarga: apenas espera. O único gatilho real é um pg_notify('pgrst', 'reload schema') emitido pelo serviço de banco de dados no caminho DDL. Se um caminho de migração se esquecer de emitir este sinal, as 8 tentativas se esgotam em um cache que nunca mudará, risco documentado como está no comentário do código, não disfarçado.

Para uma implantação PostgREST auto-hospedada, a lição é generalizada. Cada caminho DDL em seu aplicativo deve acionar o recarregamento, via NOTIFY ou um sinal SIGUSR1 para o processo. Caso contrário, uma migração produz um pico de latência p99 disfarçado como erros intermitentes logo após a implantação.

#
Resumo

O que a arquitetura corta, não o rendimento bruto

As quatro limitações documentadas aqui compartilham uma coisa em comum: nenhuma é vista em um teste de taxa de transferência HTTP isolado, mas todas as quatro determinam se uma implantação do PostgREST pode ser escalada para produção.

  • Orçamento de conexão: limita o número de locatários ativos simultaneamente em um cluster compartilhado, independentemente da taxa de transferência por locatário.
  • Custo de COUNT exato: cresce com a tabela, não com a carga; é ignorado com planned/estimated.
  • Truncamento silencioso: um limite de linha configurado corretamente ainda pode interromper a paginação mal instrumentada.
  • Recarga do esquema: uma janela de latência após cada migração, limitada se o sinal de recarga estiver bem conectado, ilimitada caso contrário.

Esteja você escolhendo entre PostgREST auto-hospedado, uma camada GraphQL estilo Hasura ou uma API personalizada, esses quatro eixos são um melhor ponto de comparação do que uma figura de req/s isolada. Veja nossa comparação PostgREST vs Hasura vs API personalizada. A escolha do pooler que fica na frente do seu banco de dados é igualmente importante: nossa comparação PgBouncer vs Supavisor vs PgCat detalha porque o PostgREST não pode passar por um pooler no modo de transação.

#
Perguntas frequentes

Perguntas frequentes

O PostgREST é rápido o suficiente para produção em larga escala?+
O próprio PostgREST é um processo leve. Em instâncias dedicadas do Aurabase, uma réplica é executada com 50 a 250 milicores de CPU e 64 a 128 MB de RAM, verificados no manifesto Kubernetes do projeto. A taxa de transferência HTTP bruta quase nunca é o fator limitante na produção. É o orçamento de conexão do Postgres, o custo de um COUNT exato e o cache do esquema que determinam se tudo é escalonável, e não apenas a velocidade do binário PostgREST.
Como posso saber se minha resposta PostgREST foi truncada por db-max-rows?+
O cabeçalho Content-Range retornado pelo PostgREST nunca diz isso. Uma resposta limitada a 5 linhas por db-max-rows é indistinguível de uma tabela que realmente contém apenas 5, medidas em condições reais em uma instância dedicada do Aurabase. A única maneira confiável de detectá-lo é comparar o número de linhas recebidas com o total retornado por Prefer: count=exact, sem este cabeçalho o truncamento permanece invisível.
A COUNT exata sempre retarda uma solicitação PostgREST?+
Preferir: count=exact força o Postgres a contar as linhas visíveis do resultado filtrado com cada consulta, um custo que aumenta com o tamanho da tabela devido ao MVCC. O Postgres não mantém um contador de linhas indexado pronto para uso. PostgREST oferece duas alternativas menos dispendiosas, count=planned (estimativa através do agendador) e count=estimated (comutação automática além de um limite), documentadas em sua documentação oficial.
Quantas conexões Postgres o PostgREST consome?+
Depende inteiramente de PGRST_DB_POOL multiplicado pelo número de réplicas. Verificado no código Aurabase: uma instância dedicada (nível premium) abre por padrão 10 conexões por réplica, ou 20 no total em 2 réplicas. O nível partilhado reduz voluntariamente este conjunto para 2 por réplica, ou 4 ligações por projeto despertado, para acomodar mais inquilinos no mesmo orçamento max_connections do cluster partilhado.
Existe um benchmark oficial do PostgREST?+
O projeto mantém um repositório dedicado, PostgREST/postgrest-benchmark no GitHub, que rastreia variações de rendimento de lançamento para lançamento, em vez de publicar uma figura de marketing isolada. Não o executamos nem o republicamos aqui. Este artigo documenta limitações arquitetônicas verificadas em nosso código e na documentação oficial do PostgREST, e não em um banco que nós mesmos reproduzimos.
#
Conclusão

O que lembrar

O PostgREST quase nunca quebra apenas sob carga HTTP: sua arquitetura é simples demais para isso. O que quebra a produção é o que a rodeia: quantas conexões suas réplicas mantêm abertas, quanto custa um total exato. Isto também inclui se um truncamento permanece visível e quanto tempo dura a janela após uma migração.

Essas quatro limitações não são específicas do Aurabase: elas se aplicam a qualquer implantação PostgREST, auto-hospedada ou gerenciada. O que esse código mostra é como uma implantação multilocatário os torna explícitos, em vez de deixá-los surpreendentes na produção.

PRONTO PARA IMPLEMENTAR?

Seu back-end em cinco minutos.

Não é necessário cartão de crédito · 500 MB grátis · 50.000 MAU