Supabase Supavisor: quando il pooler ti salva e quando no
Supavisor incanala migliaia di connessioni serverless in poche connessioni Postgres, ma aggiunge latenza e rompe i prepared statement. Quando conviene davvero.
Supavisor è il connection pooler di Supabase scritto in Elixir, pensato per smistare migliaia di connessioni client di breve durata su un pool ridotto di connessioni Postgres reali. Risolve un problema preciso: le funzioni serverless aprono e chiudono connessioni al database più velocemente di quanto Postgres riesca ad accettarle, e un tetto di 60 connessioni dirette ferma un deploy su Vercel sotto carico.
Non è un upgrade gratuito. Ogni query attraverso Supavisor costa circa 2ms in più rispetto a una connessione diretta o a PgBouncer co-locato con il database, e la transaction mode disattiva feature su cui il tuo ORM fa silenziosamente affidamento. La risposta giusta dipende da dove gira il codice e da quanto vivono le sessioni.
Cosa fa Supavisor
Postgres tratta ogni connessione client come un vero processo del sistema operativo. Un singolo progetto Supabase regge tra le 60 e qualche centinaio di connessioni dirette, a seconda del piano di compute. Apri di più e i nuovi client ricevono too many connections.
Un connection pooler si mette tra applicazione e Postgres. Mantiene un piccolo pool di connessioni reali al backend e le presta a molte connessioni client su richiesta. Quando un client chiude la transazione, il pooler restituisce la connessione al pool. L'applicazione crede di avere una connessione dedicata. Postgres crede di parlare con un solo client ben educato.
Supavisor è l'implementazione Supabase di questo schema. Usa Elixir/OTP, progettato per milioni di processi leggeri, e Supabase ha pubblicato benchmark che mostrano un milione di connessioni client gestite da un singolo cluster. PgBouncer, in confronto, è C single-thread, leggerissimo per client (circa 2MB di RAM ogni 1.000 client) ma limitato a circa 10.000 client per processo prima che la CPU diventi il collo di bottiglia.
Quando Supavisor ti salva
1. Funzioni serverless su Vercel, Netlify, AWS Lambda
È il caso da manuale. Ogni invocazione può aprire una connessione fresca. Sotto carico, un'app Next.js su Vercel produce regolarmente centinaia di funzioni concorrenti, ciascuna che vuole una connessione. Vercel stessa descrive il problema: funziona in sviluppo, crolla in produzione con errori di troppe connessioni. Transaction mode via Supavisor sulla porta 6543 è il default sensato per ogni funzione che esegue query brevi e finisce.
2. Container che scalano orizzontalmente
Stessa forma del serverless, ma il problema si manifesta più lentamente. Ogni replica moltiplica il conteggio delle connessioni. Un deployment a 20 repliche con pool da 10 per replica fa 200 connessioni, ben oltre il limite di ogni piano Supabase sotto il tier medium. Supavisor permette a ogni replica di tenersi il pool lato applicazione mentre il database vede un fan-in molto più stretto.
3. Ambienti IPv4-only
La connessione diretta di Supabase è solo IPv6 da quando, nel 2024, PgBouncer e IPv4 sono stati deprecati. Molti provider CI, hosting legacy e alcune reti aziendali non raggiungono ancora indirizzi IPv6. Supavisor su entrambe le porte parla sia IPv4 sia IPv6, ed è il fix più economico al di fuori dell'add-on IPv4. La doc Supabase sulla compatibilità lo dice in chiaro.
4. SaaS multi-tenant con molti database
Se isoli i tenant per database o per schema, l'isolamento di pool per tenant di Supavisor evita che un tenant rumoroso prosciughi un pool condiviso. PgBouncer ci riesce, ma serve configurazione manuale. Supavisor è nato per questo.
Quando Supavisor ti fa male
1. Read path sensibili alla latenza
I benchmark di Tembo misurano la latenza di Supavisor dall'80% al 160% in più rispetto a PgBouncer co-locato. Il throughput di picco è stato di 21.700 transazioni al secondo per Supavisor contro le 44.096 di PgBouncer. Il report di Tembo merita una lettura completa prima di dare per scontato che il pooler sia gratis. Per un SaaS write-heavy che fa 10 query per richiesta HTTP, 2ms in più per query diventano 20ms di response time che non recuperi.
2. Prepared statement in transaction mode
La transaction mode mette in pool connessioni backend per query, non per sessione. I prepared statement vivono nella sessione, quindi si rompono. Supavisor 1.0 ha aggiunto un supporto parziale ai named prepared statement trasmettendo PREPARE a tutte le connessioni backend, ma chi mantiene gli ORM ha segnalato casi limite ancora rotti sotto forte concorrenza. Se usi Prisma con la transaction mode ti serve pgbouncer=true nella connection string, altrimenti otterrai errori criptici sotto carico in produzione.
3. Funzioni di sessione che falliscono in silenzio
La transaction mode rompe anche i SET che dovrebbero attraversare più query, gli advisory lock, LISTEN/NOTIFY e qualunque tabella temporanea che debba sopravvivere alla singola transazione. Se l'applicazione si appoggia a una di queste cose ti serve la session mode sulla porta 5432, che restituisce connessioni esclusive al costo di perdere il vantaggio del pooling.
4. Connessioni lunghe che vuoi davvero
Un worker in background che tiene aperta una connessione per ricevere NOTIFY o per mantenere pg_advisory_lock in una leader election non è un problema di pooling. È una connessione che deve esistere. Farla passare per Supavisor in transaction mode significa che non restituirà mai la connessione al pool, vanificando il senso e occupando uno slot a vuoto.
Session mode contro transaction mode dopo febbraio 2025
Il 28 febbraio 2025 Supavisor ha deprecato la session mode sulla porta 6543. Da allora la 6543 serve solo transaction mode e la 5432 solo session mode. Connection string che fino a un anno fa funzionavano sulla 6543 con comportamento da session mode si sono rotte in silenzio. Verifica le tue stringhe esistenti: la porta che usavi l'anno scorso può non fare più quello che ricordavi.
Il modello mentale è semplice. Se il client apre, esegue una o poche query in una transazione e si disconnette, usa la 6543 in transaction mode. Se il client deve tenere viva una connessione su molte query con stato di sessione, usa la 5432 in session mode. Una terza opzione che fa metà e metà non esiste.
La questione della dimensione del pool
Supabase condivide il budget di connessioni tra i pool di Supavisor e le connessioni dirette. Se il piano di compute permette 60 connessioni backend e Supavisor è configurato con un pool da 30 in transaction mode più 30 in session mode, i due pool in contesa possono brevemente generare 60 connessioni backend al picco. Aggiungi i servizi interni del database (Auth, Realtime, Storage, PostgREST) e puoi esaurire il margine prima ancora che giri il codice applicativo.
La linea guida di Supabase è tenere il pool di Supavisor sotto il 40% del totale di connessioni backend quando ti appoggi anche all'API REST del database, spingendolo all'80% solo se controlli tutti i consumatori. Anche il pool lato applicazione conta: in Vercel parti con connection_limit=1 nell'URL di Prisma e alza con prudenza. Ogni replica si somma al totale.
Quando saltare del tutto Supavisor
Tre casi. Primo: un singolo server Node.js a vita lunga con un proprio pool applicativo di, diciamo, 10 connessioni. Non gli serve Supavisor; la connessione diretta è più veloce e più semplice. Secondo: il client JavaScript di Supabase (@supabase/supabase-js) parla con PostgREST via HTTP, non col database direttamente, e quindi è fuori da questo discorso. Terzo: un edge runtime che ha bisogno di accesso via fetch, dove si usa l'API dati di Supabase invece di un driver Postgres.
Un default pragmatico per il 2026
Per un SaaS Next.js nuovo, deployato su Vercel con Supabase come database, la configurazione che spediamo come default è questa. Transaction mode sulla porta 6543 per la connessione principale dell'applicazione, con connection_limit=1 e pgbouncer=true se Prisma è nello stack. Session mode sulla 5432 per le migrazioni e per ogni worker che ha bisogno di prepared statement o advisory lock. Una connessione IPv6 diretta solo per task amministrativi una tantum lanciati da una macchina di sviluppo. Il codice applicativo non apre mai una connessione diretta da una funzione serverless. La row-level security resta dentro al database, quindi il costo di latenza di Supavisor si somma a ogni controllo di policy.
Questa configurazione non è la più veloce. PgBouncer co-locato sul database sarebbe l'80% più veloce in throughput puro e taglierebbe i 2ms a query. Accettiamo lo scambio perché Supavisor è ciò che Supabase fornisce e supporta, perché l'isolamento di pool per tenant pesa nel lavoro multi-tenant, e perché l'alternativa, far girare un deploy serverless senza pooler davanti, è molto peggio di 2ms.
La risposta giusta raramente è "usa sempre Supavisor" o "evitalo". È sapere cosa fa ciascuna porta, cosa rompe ciascuna modalità e dove girano davvero le tue query. Il connection pooling è impiantistica. Se sbagli scelta lo scopri lunedì mattina con un errore di troppe connessioni, non alle tre di notte di sabato.
Domande frequenti
- 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
Inizia un progetto.
Un partner unico per il prodotto digitale che devi costruire. Produzione più veloce, tecnologie moderne, costi ridotti. Un team, una fattura.