PRODPlataforma BaaS soberana europeaAbrir panel →

Rendimiento · 11 lectura mínima

PostgREST: benchmark y límites reales en producción

Affane Daylami · Fondateur · 18 de mayo de 2026

volver al blog

PostgREST en sí casi nunca es el cuello de botella. En instancias dedicadas de Aurabase, una réplica se ejecuta con entre 50 y 250 milicores de CPU y entre 64 y 128 MB de RAM. Es un binario Haskell liviano que traduce solicitudes HTTP a SQL, nada más. Los verdaderos límites que aparecen en la producción están en otra parte. Cuatro de ellos surgen con mayor frecuencia: el presupuesto para las conexiones de Postgres que consumen sus réplicas y el costo de un COUNT exacto bajo MVCC. Un truncamiento de respuesta también puede permanecer invisible en los encabezados, al igual que una ventana de latencia después de cada migración de esquema.

Este texto en inglés se generó automáticamente a partir del original en francés y aún no ha sido revisado.
Esta página fue traducida automáticamente. La versión en inglés es autorizada.

Nuestro artículo sobre compatibilidad con PostgREST detalla lo que cubre funcionalmente el servidor (filtros, incrustación, RPC, RLS) y lo que deja a usted. Éste viene de otro lugar. Documenta, con el código de Aurabase y la documentación oficial de PostgREST como fuentes, dónde y por qué PostgREST realmente se estanca a escala. No estamos reproduciendo aquí un banco de carga que no hayamos gestionado nosotros mismos. Nuestra metodología de benchmark explica por qué una cifra aislada, sin un protocolo publicado, no nos parece fiable.

Lo esencial

  • PostgREST en sí es liviano: de 50 a 250 milicores de CPU, de 64 a 128 MB de RAM por réplica en instancias dedicadas de Aurabase. El rendimiento HTTP sin procesar casi nunca es el factor limitante en la producción.
  • El límite real es el presupuesto de conexión de Postgres: PGRST_DB_POOL × réplicas. Verificado en el código de Aurabase: 20 conexiones por proyecto en el nivel dedicado (10×2), 4 en el nivel compartido (2×2). Esta es una elección deliberada para incluir más inquilinos en el mismo max_connections.
  • Prefer: count=exact fuerza un costoso escaneo MVCC en tablas grandes. PostgREST documenta dos alternativas más económicas: count=planned y count=estimated, con un costo total aproximado.
  • Un límite db-max-rows (1000 líneas por defecto en Aurabase) trunca una respuesta SIN reportarla en Content-Range (medido en condiciones reales, detallado a continuación).
  • Después de una migración DDL, la caché del esquema PostgREST se recarga de forma asincrónica. La puerta de enlace de Aurabase lo reintenta hasta 8 veces (alrededor de 3,5 segundos acumulativos en el peor de los casos) antes de darse por vencido, un comportamiento documentado directamente en el código.
#
Metodología

Qué mide un benchmark PostgREST y qué no mide

Una prueba de rendimiento HTTP en PostgREST mide principalmente Postgres, rara vez PostgREST. El servidor es una fina capa de traducción delante de la base. En la gran mayoría de las cargas del mundo real, el tiempo de respuesta lo domina la consulta SQL ejecutada, no el proceso que la generó.

El proyecto PostgREST mantiene un repositorio dedicado a este tema, PostgREST/postgrest-benchmark en GitHub, que rastrea las variaciones de rendimiento de una versión a otra en lugar de publicar una cifra de marketing aislada. No lo hemos realizado ni republicado aquí. Sus resultados dependen del hardware, el tamaño del esquema y el escenario probado, exactamente las variables que nuestro propio protocolo de referencia requiere documentar antes de citar una cifra.

Debajo de PostgREST, está pgbench que mide la capa que realmente importa: el tiempo de transacción SQL bajo carga concurrente. Esta es la herramienta de referencia oficial de PostgreSQL (postgresql.org/docs/current/pgbench.html, consultada el 24 de agosto de 2026). En lugar de reproducir este protocolo aquí, este artículo documenta cuatro limitaciones arquitectónicas concretas de PostgREST en producción, cada una verificada en el código fuente de Aurabase o en la documentación oficial del proyecto.

#
Comprobado en código

La huella real de una instancia de PostgREST en Aurabase

Cada proyecto del motor Aurabase Postgres recibe dos réplicas PostgREST dedicadas, ubicadas junto con su clúster. El manifiesto de Kubernetes que los implementa establece recursos modestos.

50-250m
CPU POR RÉPLICA
solicitudes → límites
64-128
MB RAM POR RÉPLICA
solicitudes → límites
2
RÉPLICAS POR PROYECTO
alta disponibilidad (P22)

Lo que realmente consumen estas réplicas no es CPU: son conexiones al primario de Postgres. Cada instancia de PostgREST se conecta directamente a la instancia principal (-rw), sin pasar por el agrupador PgBouncer implementado para el inquilino. Esta elección ya se detalla en nuestro artículo sobre Compatibilidad con PostgREST: el mecanismo de recarga del esquema LISTEN/NOTIFY requiere una conexión persistente, incompatible con un pooler en modo transacción. Qué agrega este artículo: cuánto cuesta realmente, en términos de conexiones, y dónde alcanza su punto máximo.

El tamaño de este grupo por réplica (PGRST_DB_POOL) difiere deliberadamente según el nivel del proyecto, verificado en k8s_tenant.rs, la función que crea el manifiesto PostgREST para cada proyecto:

RodamientoPGRST_DB_POOL/réplicaRéplicasConexiones / proyecto despertado
Dedicado (premium, A1)10 (predeterminado PostgREST)220
Compartido (flota, gratis/pro/equipo)2 (predeterminado de Aurabase, reducido)24
deploy/cnpg/tenant-postgrest.yaml (Extracto real, valor sustituido por el aprovisionador.)yaml
# Huella digital de conexiones por réplica en el primario.
env:
  - { name: PGRST_DB_POOL, value: "PGRST_DB_POOL_VALUE" }  # 10 (dedicados) o 2 (compartidos)

En el nivel dedicado, la restricción se relaja: un proyecto tiene su propio clúster CNPG y, por lo tanto, su propio max_connections, sin vecinos de sobra. A nivel compartido, varios proyectos de una misma organización comparten un único cluster: es este contexto el que hace decisivo el presupuesto de conexiones, que se desarrolla en el siguiente apartado.

#
El verdadero techo

El presupuesto de conexiones decide cuántos inquilinos se están ejecutando al mismo tiempo.

En un clúster de Postgres compartido, no es el rendimiento HTTP lo que limita la cantidad de proyectos activos simultáneamente. Esta es la cantidad de conexiones que sus instancias PostgREST mantienen abiertas en la principal, en comparación con las max_connectionsdisponibles.

Aurabase deriva este presupuesto directamente de los límites reales del clúster, marcados en fleet.rs: (max_connections − réserve superuser/CNPG − connexions réservées au pooler) ÷ connexions par projet éveillé, piso en 1. La reserva fija es 10 conexiones (superusuario, administrador de instancias CNPG, exportador de métricas, margen de administrador del aprovisionador). En cuanto a los defectos entregados (grupo de 2 por réplica, 2 réplicas o 4 conexiones por proyecto activado), el cálculo arroja tres presupuestos diferentes según el nivel de tamaño del clúster.

Presupuesto para proyectos activos simultáneamente por nivel, cluster de Postgres compartidoNivel libre: 5 proyectos activos simultáneos (max_connections 50, pooler 20). Nivel profesional: 7 (max_connections 100, pooler 60). Nivel de equipo: 10 (max_connections 200, pooler 150). Fórmula derivada del código de Aurabase (fleet.rs::derive_wake_budget), reserva fija de 10 conexiones, 4 conexiones por proyecto despierto.024681012gratis (max_connections 50)5 proyectospro (max_conexiones 100)7 proyectosequipo (max_connections 200)10 proyectos

Fuente: derivado de fleet.rs::derive_wake_budget y wake_budget_for_org_plan, código de Aurabase, releído el 24 de agosto de 2026.

Este presupuesto no es una cuota de proyectos propios: una organización team puede tener 50 proyectos, la mayoría de los cuales están inactivos. Este es un límite de concurrencia : la cantidad de proyectos que pueden mantener conexiones abiertas al mismo tiempo en el primario. Un despertar por encima del presupuesto no falla, se aplaza hasta que un proyecto hermano vuelva a dormir, comprobado en el mismo archivo. El tema del tamaño de max_connections en sí se amplía en nuestro artículo sobre el ajuste de de max_connectionsy la compensación dedicada/mutualizada en su conjunto en base dedicada vs. compartida.

#
Costo oculto

Por qué preferir: count=exact ralentiza una consulta en una tabla grande

Solicitar un total exacto obliga a Postgres a contar las filas visibles del resultado filtrado con cada consulta, un costo que crece con la tabla, no es una operación gratuita.

PostgreSQL no mantiene ningún contador de filas indexadas listo para usar. En MVCC, la visibilidad de una fila depende de la transacción que la lee. Por lo tanto, un COUNT(*) exacto debe visitar las filas candidatas en lugar de leer un valor precalculado. Esta es una limitación estructural bien documentada en el ecosistema de Postgres, incluidos los proveedores de análisis como ClickHouse, que comparan sus propios contadores aproximados con el comportamiento transaccional de Postgres.

terminalbash
# Caro en una mesa grande: fuerza un escaneo MVCC del resultado filtrado
curl "https://<gateway>/v1/db/<project_id>/orders?status=eq.paid" \
  -H "apikey: <clé>" -H "Prefer: count=exact"

# Alternativas menos costosas, documentadas por PostgREST
  -H "Prefer: count=planned"   # estimación a través del planificador
  -H "Prefer: count=estimated" # planeado más allá de un umbral, exactamente debajo

PostgREST documenta estas tres estrategias de conteo de forma nativa (postgrest.org, consultado el 24 de agosto de 2026). La estrategia exact garantiza un total al precio del escaneo. planned devuelve una estimación casi gratuita del planificador de consultas. estimated cambia automáticamente entre los dos según un umbral. La elección no es cosmética: una paginación que requiere count=exact en una tabla de varios millones de filas paga por este escaneo en cada página, incluso cuando el usuario nunca consulta la última.

#
Medido en reales

El truncamiento de db-max-rows es invisible sin count=exact

Un límite de fila puede truncar una respuesta PostgREST sin ninguna indicación en el cuerpo o los encabezados, a menos que se solicite explícitamente un total exacto. Lo medimos en condiciones reales en una instancia dedicada de Aurabase, no asumida.

En una tabla de prueba de 10 filas con PGRST_DB_MAX_ROWS=5, PostgREST v12.2.3 representa exactamente el mismo encabezado Content-Range para dos situaciones muy diferentes:

ConsultaLíneas renderizadasRango de contenidometa (Aurabase)
?límite=50 (sin contar)5/10 reales0-4/*{}
?límite=50&count=exacto5/10 reales0-4/10{total: 10}

Sin count=exact, la respuesta de 5 filas es indistinguible de una tabla que en realidad solo contendría 5: Content-Range: 0-4/* describe las filas representadas, nunca el límite aplicado. El límite real vigente no aparece en ninguna parte en este caso, medido directamente en la ruta PostgREST del SDK de Aurabase.

Consecuencias de cualquier paginación en PostgREST

Si su implementación de PostgREST establece un db-max-rows (Aurabase tiene como valor predeterminado 1000), un cliente que compare data.length con el límite solicitado para detectar una página completa puede estar equivocado. El error aparece tan pronto como el límite del servidor es inferior a este límite. La única señal confiable es comparar el número de líneas recibidas con el total devuelto por count=exact, lo que pone en juego directamente la compensación de costos descrita en la sección anterior.

#
Latencia diferida

Recargar la caché de esquema después de una migración

PostgREST mantiene el esquema de Postgres en la memoria al inicio. Después de un DDL (crear tabla, agregar columna), este caché debe recargarse antes de que responda la nueva ruta, y esta recarga es asíncrona.

Una escritura que llega a esta ventana puede recibir un 404 transitorio (caché aún no actualizado), aunque la tabla exista en el lado de Postgres. La puerta de enlace de Aurabase absorbe esto con un bucle de reintento limitado, verificado en postgrest_proxy.rs: hasta 8 intentos, retroceso creciente (250 ms más 100 ms por intento), 3,5 segundos acumulativos en el peor de los casos. Este mecanismo sólo afecta a las escrituras, nunca a las lecturas.

Un detalle que el propio código documenta

La pasarela no emite ninguna señal de recarga: sólo espera. El único desencadenante real es un pg_notify('pgrst', 'reload schema') emitido por el servicio de base de datos en la ruta DDL. Si una ruta de migración olvida emitir esta señal, los 8 intentos se agotan en un caché que nunca cambiará, un riesgo documentado tal como está en el comentario del código, no disfrazado.

Para una implementación de PostgREST autohospedada, la lección se generaliza. Cada ruta DDL en su aplicación debe activar la recarga, a través de NOTIFY o una señal SIGUSR1 al proceso. De lo contrario, una migración produce un pico de latencia p99 disfrazado de errores intermitentes inmediatamente después de la implementación.

#
Resumen

Lo que divide la arquitectura, no el rendimiento bruto

Las cuatro limitaciones documentadas aquí tienen una cosa en común: ninguna se observa en una prueba de rendimiento HTTP aislada, sin embargo, las cuatro determinan si una implementación de PostgREST escala a producción.

  • Presupuesto de conexión: limita la cantidad de inquilinos activos simultáneamente en un clúster compartido, independientemente del rendimiento por inquilino.
  • Costo exacto COUNT: crece con la mesa, no con la carga; se omite con planned/estimated.
  • Truncamiento silencioso: un límite de fila configurado correctamente aún puede interrumpir la paginación mal instrumentada.
  • Recarga del esquema: una ventana de latencia después de cada migración, limitada si la señal de recarga está bien conectada, ilimitada en caso contrario.

Ya sea que elija entre PostgREST autohospedado, una capa GraphQL estilo Hasura o una API personalizada, estos cuatro ejes son un mejor punto de comparación que una cifra de solicitudes aisladas. Vea nuestra comparación PostgREST vs Hasura vs API personalizada. La elección del pooler que se encuentra frente a su base de datos es igualmente importante: nuestra comparación PgBouncer vs Supavisor vs PgCat detalla por qué PostgREST no puede pasar por un pooler en modo de transacción.

#
Preguntas frecuentes

Preguntas frecuentes

¿PostgREST es lo suficientemente rápido para la producción a gran escala?+
PostgREST en sí es un proceso ligero. En instancias dedicadas de Aurabase, una réplica se ejecuta con entre 50 y 250 milicores de CPU y entre 64 y 128 MB de RAM, verificados en el manifiesto de Kubernetes del proyecto. El rendimiento HTTP sin procesar casi nunca es el factor limitante en la producción. Es el presupuesto de conexión de Postgres, el costo de un COUNT exacto y el caché del esquema lo que determina si todo escala, no solo la velocidad del binario PostgREST.
¿Cómo sé si mi respuesta PostgREST fue truncada por db-max-rows?+
El encabezado Content-Range devuelto por PostgREST nunca dice esto. Una respuesta limitada a 5 filas por db-max-rows es indistinguible de una tabla que en realidad solo contiene 5, medida en condiciones reales en una instancia dedicada de Aurabase. La única forma confiable de detectarlo es comparar el número de líneas recibidas con el total devuelto por Prefer: count=exact, sin este encabezado el truncamiento permanece invisible.
¿El COUNT exacto siempre ralentiza una solicitud PostgREST?+
Prefer: count=exact obliga a Postgres a contar las filas visibles del resultado filtrado con cada consulta, un costo que aumenta con el tamaño de la tabla debido a MVCC. Postgres no mantiene un contador de filas indexadas listo para usar. PostgREST ofrece dos alternativas menos costosas, count=planned (estimación a través del programador) y count=estimated (cambio automático más allá de un umbral), documentadas en su documentación oficial.
¿Cuántas conexiones de Postgres consume PostgREST?+
Depende completamente de PGRST_DB_POOL multiplicado por el número de réplicas. Verificado en el código de Aurabase: una instancia dedicada (nivel premium) abre por defecto 10 conexiones por réplica, o 20 en total en 2 réplicas. El nivel compartido reduce voluntariamente este grupo a 2 por réplica, o 4 conexiones por proyecto activado, para dar cabida a más inquilinos con el mismo presupuesto max_connections del clúster compartido.
¿Existe un punto de referencia oficial de PostgREST?+
El proyecto mantiene un repositorio dedicado, PostgREST/postgrest-benchmark en GitHub, que rastrea las variaciones de rendimiento de una versión a otra en lugar de publicar una cifra de marketing aislada. No lo hemos realizado ni republicado aquí. Este artículo documenta las limitaciones arquitectónicas verificadas en nuestro código y en la documentación oficial de PostgREST, no en un banco que reprodujimos nosotros mismos.
#
Conclusión

que recordar

PostgREST casi nunca falla solo con la carga HTTP: su arquitectura es demasiado simple para eso. Lo que rompe en la producción es lo que la rodea: cuántas conexiones mantienen abiertas sus réplicas, cuánto cuesta un total exacto. Esto también incluye si un truncamiento permanece visible y cuánto dura la ventana después de una migración.

Estas cuatro limitaciones no son específicas de Aurabase: se aplican a cualquier implementación de PostgREST, autohospedada o administrada. Lo que este código muestra es cómo una implementación multiinquilino los hace explícitos en lugar de dejarlos sorprendentes en producción.

¿LISTO PARA IMPLEMENTAR?

Tu backend en cinco minutos.

No se requiere tarjeta de crédito · 500 MB gratis · 50,000 MAU