Supabase Supavisor: cuándo el pooler te salva y cuándo no
Supavisor canaliza miles de conexiones serverless hacia un pool pequeño de Postgres, pero añade latencia y rompe los prepared statements. Cuándo conviene.
Supavisor es el pooler de conexiones de Supabase escrito en Elixir, diseñado para canalizar miles de conexiones de cliente cortas hacia un pool reducido de conexiones reales de Postgres. Resuelve un problema concreto: las funciones serverless abren y descartan conexiones a la base de datos más rápido de lo que Postgres puede aceptarlas, y un techo de 60 conexiones directas tumba un despliegue en Vercel bajo carga.
No es una mejora gratis. Cada consulta a través de Supavisor cuesta unos 2ms extra frente a una conexión directa o frente a PgBouncer co-ubicado con la base de datos, y el modo transacción desactiva funciones en las que tu ORM se apoya en silencio. La respuesta correcta depende de dónde corre tu código y cuánto viven tus sesiones.
Qué hace Supavisor
Postgres trata cada conexión de cliente como un proceso real del sistema operativo. Un proyecto Supabase aguanta entre 60 y algunos cientos de conexiones directas, según el plan de cómputo. Si abres más, los clientes nuevos reciben too many connections.
Un pooler se sitúa entre tu aplicación y Postgres. Mantiene un pool pequeño de conexiones reales al backend y las presta a muchas conexiones de cliente bajo demanda. Cuando un cliente cierra la transacción, el pooler devuelve la conexión al pool. La aplicación cree tener su conexión propia. Postgres cree hablar con un solo cliente bien educado.
Supavisor es la implementación de Supabase de este patrón. Usa Elixir/OTP, pensado para millones de procesos ligeros, y Supabase ha publicado benchmarks que muestran un millón de conexiones de cliente gestionadas por un solo cluster. PgBouncer, en cambio, es C de un solo hilo, ligerísimo por cliente (unos 2MB de RAM por cada 1.000 clientes) pero tope cercano a 10.000 clientes por proceso antes de que la CPU sea el cuello de botella.
Cuándo Supavisor te salva
1. Funciones serverless en Vercel, Netlify, AWS Lambda
Es el caso de manual. Cada invocación puede abrir una conexión nueva. Bajo carga, una app Next.js en Vercel produce con normalidad cientos de funciones concurrentes y cada una quiere su conexión. El propio Vercel describe el problema: funciona en desarrollo, cae en producción con errores de demasiadas conexiones. Modo transacción a través de Supavisor en el puerto 6543 es el default sensato para cualquier función con consultas cortas que termina enseguida.
2. Contenedores con autoescalado horizontal
La misma forma que serverless pero falla más despacio. Cada réplica multiplica el contador de conexiones. Un despliegue de 20 réplicas con un pool de 10 por réplica son 200 conexiones, por encima del límite de cualquier plan Supabase por debajo del tier medium. Supavisor deja que cada réplica conserve su pool a nivel de aplicación mientras la base de datos ve un fan-in mucho menor.
3. Entornos solo IPv4
La conexión directa de Supabase es solo IPv6 desde que en 2024 se desaprobaron PgBouncer e IPv4. Muchos proveedores de CI, hosting heredado y algunas redes corporativas todavía no alcanzan direcciones IPv6. Supavisor en ambos puertos habla IPv4 y IPv6, y es el arreglo más barato al margen del add-on IPv4. La documentación de Supabase sobre compatibilidad lo deja claro.
4. SaaS multi-tenant con muchas bases de datos
Si aíslas inquilinos por base o por esquema, el aislamiento de pool por tenant de Supavisor evita que un inquilino ruidoso vacíe un pool compartido. PgBouncer también lo hace, pero pide configuración manual. Supavisor nació para esto.
Cuándo Supavisor te hace daño
1. Rutas de lectura sensibles a la latencia
Los benchmarks de Tembo miden la latencia de Supavisor entre un 80% y un 160% por encima de PgBouncer co-ubicado. El pico de throughput fue de 21.700 transacciones por segundo para Supavisor frente a 44.096 de PgBouncer. El artículo de Tembo merece leerse entero antes de asumir que el pooler es gratis. Para un SaaS de muchas escrituras que hace 10 consultas por petición HTTP, esos 2ms extra por consulta se convierten en 20ms de respuesta que no recuperas.
2. Prepared statements en modo transacción
El modo transacción reparte conexiones de backend por consulta, no por sesión. Los prepared statements viven en la sesión, así que se rompen. Supavisor 1.0 añadió soporte parcial de prepared statements con nombre difundiendo PREPARE entre todas las conexiones de backend, pero los autores de ORMs han reportado casos límite que siguen fallando con concurrencia alta. Si usas Prisma con modo transacción necesitas pgbouncer=true en la cadena de conexión, si no te encontrarás errores crípticos en carga de producción.
3. Funciones de sesión que fallan en silencio
El modo transacción también rompe los SET pensados para atravesar varias consultas, los advisory locks, LISTEN/NOTIFY y cualquier tabla temporal que tenga que durar más que una sola transacción. Si tu aplicación depende de algo de esto necesitas el modo sesión en el puerto 5432, que devuelve conexiones exclusivas a cambio de perder el beneficio del pooling.
4. Conexiones largas que sí quieres
Un worker en segundo plano que mantiene una conexión para recibir NOTIFY o sostener un pg_advisory_lock en una elección de líder no es un problema de pooling. Es una conexión que tiene que existir. Pasarla por Supavisor en modo transacción significa que nunca devuelve la conexión al pool, lo que tira por tierra la idea y consume una plaza en balde.
Modo sesión y modo transacción después de febrero de 2025
El 28 de febrero de 2025 Supavisor desaprobó el modo sesión en el puerto 6543. Desde esa fecha el 6543 sólo sirve modo transacción y el 5432 sólo modo sesión. Las cadenas de conexión que funcionaban en el 6543 con comportamiento de sesión se rompieron en silencio. Revisa tus cadenas existentes: el puerto que usabas el año pasado puede no hacer ya lo que recordabas.
El modelo mental es simple. Si tu cliente abre, ejecuta una o pocas consultas dentro de una transacción y se desconecta, usa 6543 en modo transacción. Si tu cliente espera mantener una conexión viva entre muchas consultas con estado de sesión, usa 5432 en modo sesión. No hay una tercera opción que divida la diferencia.
La cuestión del tamaño del pool
Supabase comparte el presupuesto de conexiones entre los pools de Supavisor y las conexiones directas. Si tu plan de cómputo permite 60 conexiones de backend y Supavisor está configurado con un pool de 30 en modo transacción más 30 en modo sesión, los dos pools compitiendo pueden generar 60 conexiones de backend en el pico. Suma los servicios internos de la base de datos (Auth, Realtime, Storage, PostgREST) y agotas el margen antes de que corra tu código.
La guía oficial de Supabase es mantener el pool de Supavisor por debajo del 40% de las conexiones de backend totales cuando además dependes de la API REST de la base de datos, y subir al 80% sólo si controlas a todos los consumidores. El pool a nivel de aplicación también pesa: en funciones de Vercel arranca con connection_limit=1 en la URL de Prisma y sube con cautela. Cada réplica suma al total.
Cuándo saltarse Supavisor por completo
Tres casos. Primero: un único servidor Node.js de larga duración con su propio pool aplicativo de, digamos, 10 conexiones. No necesita Supavisor; la conexión directa es más rápida y más simple. Segundo: el cliente JavaScript de Supabase (@supabase/supabase-js) habla con PostgREST por HTTP, no con la base de datos directamente, así que queda fuera de esta conversación. Tercero: un edge runtime que necesita acceso vía fetch, donde se usa la API de datos de Supabase en vez de un driver Postgres.
Un default pragmático para 2026
Para un SaaS Next.js nuevo, desplegado en Vercel con Supabase como base de datos, la configuración que enviamos por defecto es la siguiente. Modo transacción en el puerto 6543 para la conexión principal de la aplicación, con connection_limit=1 y pgbouncer=true si Prisma está en el stack. Modo sesión en el 5432 para migraciones y para cualquier worker que necesite prepared statements o advisory locks. Una conexión IPv6 directa sólo para tareas de administración puntuales lanzadas desde la máquina de un desarrollador. El código de aplicación nunca abre una conexión directa desde una función serverless. La row-level security sigue corriendo en la base de datos, así que el coste de latencia de Supavisor se suma a cada comprobación de política.
Esa configuración no es la más rápida. PgBouncer co-ubicado en la base de datos sería un 80% más rápido en throughput puro y recortaría 2ms por consulta. Aceptamos el intercambio porque Supavisor es lo que Supabase entrega y soporta, porque el aislamiento de pool por tenant pesa en trabajo multi-tenant y porque el modo de fallo de correr sin ningún pooler delante de un despliegue serverless es mucho peor que 2ms.
La respuesta correcta rara vez es "usa siempre Supavisor" o "evítalo". Es saber qué hace cada puerto, qué rompe cada modo y dónde corren tus consultas en realidad. El pooling de conexiones es fontanería. Si eliges mal te enteras un lunes por la mañana con un error de demasiadas conexiones, no a las tres de la madrugada del sábado.
Preguntas frecuentes
- Should I always use Supavisor with Supabase?
- No. Use it whenever your application code runs in a serverless or autoscaling environment, or when you need IPv4 connectivity. Skip it for a single long-running Node.js server with a sane in-process pool, for the Supabase JavaScript client (which talks to PostgREST over HTTP, not to Postgres), and for background workers that need persistent session state. The pooler costs about 2ms per query, so it is not free.
- Why does Prisma fail with Supavisor in transaction mode?
- Prisma creates prepared statements in the background, and transaction mode pools backend connections per query rather than per session. Prepared statements live on a single session, so they break when the next query lands on a different backend. Adding pgbouncer=true to the connection string tells Prisma to disable prepared statements. Supavisor 1.0 added partial named prepared statement support, but expect rough edges under high concurrency until your specific Supavisor version is confirmed stable.
- What changed with the February 2025 session mode deprecation?
- Before 28 February 2025, port 6543 served both transaction mode and session mode behaviour depending on configuration. After that date, port 6543 is transaction-only and port 5432 is session-only. Connection strings that used 6543 with implicit session behaviour silently broke. If you are still seeing odd behaviour, audit every connection string in your project and confirm each one targets the right port for the mode you actually want.
- How big should my Supavisor pool be?
- Stay under 40% of total backend connections when the database REST API and other Supabase services share that budget. Push toward 80% only when you control every consumer. On Vercel functions, start the application-side pool at connection_limit=1 per replica and raise it cautiously. Every replica multiplies the total. The combined ceiling of transaction-mode plus session-mode pools can briefly equal the entire backend budget at peak, so size both pools with the database compute tier in mind, not just the workload.
Studio
Empieza un proyecto.
Un partner único para el producto digital que necesitas construir. Producción más rápida, tecnología moderna, costes reducidos. Un equipo, una factura.