Questo articolo mette a confronto l'architettura dei tre pooler: lingua, modalità di pooling, modello singolo o multi-tenant, funzioni oltre il puro pooling. Tembo e PkgPulse hanno pubblicato confronti numerici di questi tre strumenti, ma noi stessi non abbiamo riprodotto nessuna delle loro misurazioni. La nostra posizione editoriale sui benchmark, dettagliata nella nostra metodologia di benchmark , è di non ripubblicare mai una cifra che non abbiamo verificato noi stessi. Quello che troverai invece qui: l'architettura effettiva di ogni strumento e il modo in cui Aurabase instrada effettivamente il traffico Postgres, verificato sezione per sezione nel codice sorgente.
- PgBouncer (C) rimane il pooler più collaudato e meglio integrato con Kubernetes: CloudNativePG si affida direttamente ad esso per la sua risorsa
Pooler. - Supavisor (progetto Elixir, Supabase) si rivolge a un problema diverso: servire migliaia di database dallo stesso servizio, anziché un pooler per database.
- PgCat (Rust) aggiunge lo sharding delle applicazioni, il bilanciamento del carico tra le repliche e il failover automatico al raw pooling.
- Il repository Aurabase mostra PgBouncer utilizzato a due livelli: una distribuzione condivisa per la flotta condivisa e una risorsa
Poolergestita da CloudNativePG per tenant dedicato. Entrambi operano in modalità transazione. - PostgREST e il pool di amministrazione
aura-dbrimangono volontariamente in connessione diretta a Postgres, senza passare attraverso il pooler: il pooling delle transazioni interromperebbe il ricaricamento dello schema e i blocchi della sessione.
Tre pooler, tre filosofie
PgBouncer minimizza, Supavisor pool su scala multi-tenant, PgCat aggiunge funzioni di rete al raw pooling. Nessuno dei tre sostituisce direttamente gli altri due, anche se spesso vengono confrontati termine per termine nelle stesse pagine.
| Lingua | C | Elisir (RAGGIO) | Ruggine |
|---|---|---|---|
| Modalità di pooling | Sessione, transazione, dichiarazione | Sessione, transazione | Sessione, transazione, dichiarazione |
| Modello di locazione | Un cluster di destinazione per istanza, progettato per essere a tenant singolo | Multi-tenant nativo: un servizio per più database | Un cluster di destinazione, partizionamento in base alla chiave di partizione |
| Oltre il pooling | Nessuna funzione aggiuntiva, volutamente minimale | API HTTP di amministrazione, registrazione dinamica del tenant | Sharding, bilanciamento del carico e failover tra repliche |
| Integrazione nativa di Kubernetes | Sì: pool di risorse CloudNativePG | Non documentato in modo nativo fino ad oggi | Non documentato in modo nativo fino ad oggi |
| Origine | Lo standard storico del pooling di Postgres | Costruito da Supabase per il proprio cloud multi-tenant | Nato su Instacart, gestito oggi da PostgresML |
Colonne, in ordine: PgBouncer, Supavisor, PgCat. Caratteristiche dell'architettura secondo i documenti ufficiali di ciascun progetto, da confermare sulla versione che si sta implementando, l'ecosistema si evolve rapidamente su questo punto.
Lo standard storico, leggero e integrato in Kubernetes
PgBouncer fa solo una cosa: raggruppare le connessioni Postgres, senza alcuna funzione aggiuntiva. Questo ambito deliberatamente ristretto spiega in gran parte la sua longevità e la sua adozione come elemento base nella maggior parte degli stack Postgres in produzione.
Sono disponibili tre modalità di pooling. La modalità Sessione apre una connessione server per connessione client, la più permissiva. La modalità transazione riutilizza una connessione server tra diversi client, rilasciata a ciascuna estremità di una transazione. La modalità istruzione va ancora oltre e viene utilizzata raramente in produzione. È la modalità di transazione che apporta il vero guadagno di pooling, ma impone regole rigide. Qualsiasi stato della sessione ( variabiliSET, blocchi consultivi, LISTEN/NOTIFY) non sopravvive oltre una transazione. Descriviamo in dettaglio queste regole e le loro insidie nel nostro articolo dedicato sulla modalità di pooling delle transazioni di PgBouncer.
Storicamente a processo singolo, un'istanza PgBouncer utilizza un singolo core della CPU per impostazione predefinita. L'esecuzione di più istanze dietro la stessa porta (tramite SO_REUSEPORT) è un'evoluzione più recente del progetto, non una caratteristica di progettazione iniziale. Dal punto di vista dell'autenticazione, PgBouncer supporta un auth_queryconfigurabile, una funzione SQL eseguita ad ogni connessione per risolvere dinamicamente la password di un ruolo. Questo meccanismo evita di dipendere da un file statico che elenca in anticipo ciascun utente. È esattamente questo meccanismo che Aurabase utilizza per i suoi ruoli per progetto (sezione 05).
PgBouncer è il pooler che CloudNativePG distribuisce nativamente dietro la sua risorsa Pooler. Su un cluster Postgres gestito dall'operatore CloudNativePG, attivare un pooler gestito equivale, in pratica, ad attivare PgBouncer senza configurarlo manualmente.
Pooler multi-tenant nativo del cloud di Supabase
Supavisor affronta un problema che PgBouncer non è mai stato progettato per risolvere su questa scala. Ciò implica servire un numero molto elevato di database tenant distinti da un singolo servizio, anziché da un'istanza del pooler per database. Scritto in Elixir ed eseguito sulla macchina virtuale Erlang (BEAM), il progetto è sviluppato e mantenuto da Supabase, in open source, sul proprio repository GitHub.
Il modello multi-tenant nativo è la vera differenza strutturale. Laddove una classica flotta di PgBouncer richiede un processo (o una serie di connessioni dedicate) per base di destinazione, Supavisor funziona in modo diverso. Registra dinamicamente i tenant tramite un'interfaccia di amministrazione HTTP e instrada ciascuna connessione in entrata al database corretto senza riavviare il servizio. Supabase ha migrato i propri progetti Cloud da PgBouncer a Supavisor proprio per questo motivo. Un classico cluster di pooling, uno per database, non è scalabile fino a un cloud multi-tenant che ospita centinaia di migliaia di progetti.
Questa scelta architettonica ha uno svantaggio documentato. La parità di funzionalità con PgBouncer sui casi avanzati ha richiesto tempo per stabilizzarsi dopo il lancio del progetto. Due esempi: alcuni comportamenti di LISTEN/NOTIFYe la gestione fine degli estratti conto preparati in modalità transazione. Controlla la tua versione prima della migrazione se la tua applicazione dipende da questi comportamenti specifici.
L'outsider di Rust: sharding nativo e bilanciamento del carico
PgCat è esplicitamente posizionato come alternativa a PgBouncer, scritto in Rust. Aggiunge funzioni di rete al pooling classico che né PgBouncer né Supavisor incorporano nativamente. Tre in particolare: partizionamento automatico delle applicazioni in base alla chiave di partizione, bilanciamento del carico tra repliche di lettura e failover automatico da una replica non riuscita. Il progetto è nato presso Instacart prima di essere rilevato e mantenuto oggi da PostgresML.
Concretamente, PgCat può svolgere il ruolo che normalmente occuperebbero due livelli distinti: un pooler di connessione e un proxy applicativo per l'instradamento tra diverse istanze di Postgres. Un team che stava già condividendo manualmente i propri dati può semplificare il proprio codice con PgCat. Stessa cosa per una logica di distribuzione delle letture tra repliche sviluppata internamente: uno strato di rete dedicato la sostituisce direttamente.
Esiste anche il compromesso opposto: PgCat è un progetto più giovane, con un ecosistema di documentazione e feedback sulla produzione molto più piccolo rispetto a PgBouncer. Adottare le sue funzioni di sharding e failover significa anche accettare di dipendere dalla maturità di questo componente specifico, non solo dalla sua capacità di pooling.
Cosa mostra il codice Aurabase: PgBouncer ovunque, tranne dove il pooling delle transazioni interrompe tutto
Il repository Aurabase distribuisce PgBouncer su due livelli separati, entrambi in modalità transazione. Per la flotta condivisa, il grafico Helm definisce una distribuzione PgBouncer dedicata davanti al piano dati condiviso (deploy/helm/aurabase/templates/infra/pgbouncer.yaml, immagine edoburu/pgbouncer). Per un tenant su un'istanza dedicata, il provisioner genera una risorsa Pooler gestita in modo nativo da CloudNativePG (deploy/cnpg/tenant-pooler.yaml, reso da k8s_tenant.rs). Nessuno dei due utilizza Supavisor o PgCat. Il codice non documenta un confronto esplicito che abbia preceduto questa scelta. D’altro canto, mostra un’integrazione profonda e già operativa con l’ecosistema CloudNativePG, coerente con il fatto che PgBouncer è il mattone di pooling nativo.
Tuttavia, non tutto passa attraverso il pooler e questa è una scelta deliberata e documentata nel codice stesso. PostgREST rimane connesso in tempo reale a Postgres, mai tramite PgBouncer. Il commento del grafico Helm è esplicito riguardo al motivo: il pooling delle transazioni interromperebbe il ricaricamento dello schema, che dipende da un LISTEN sul canale pgrst. Questo meccanismo non è compatibile con le connessioni server riciclate tra client. Anche il pool di amministrazioneaura-db (schema, DDL, blocchi consultivi di sessione) rimane in connessione diretta, per lo stesso motivo di base. SET search_path senza ambito di transazione e i blocchi di sessione non sopravvivono a un pooler in modalità transazione.
L'autenticazione segue il modello auth_query descritto nella sezione 02: AUTH_QUERY: SELECT * FROM pgbouncer.user_lookup($1), senza un file userlist.txt statico. Questo è ciò che consente ai ruoli creati dinamicamente per progetto (project_<uuid>_authenticator) di autenticarsi tramite PgBouncer senza ridistribuire il pooler per ogni nuovo progetto.
Il commento sullo stato di salute di PgBouncer nel manifesto locale di Kubernetes documenta un vero e proprio bug, già risolto. Un'esecuzione pg_isready contro PgBouncer convalida solo l'handshake del proxy, mai la connessione effettiva al backend Postgres che inoltra. PgBouncer risponde “accettando connessioni” anche quando il backend viene fermato, mettendo in coda le richieste. Risultato osservato durante un test distruttivo: il servizio è rimasto healthy per 5 cicli consecutivi mentre Postgres era irraggiungibile. La correzione sostituisce il controllo con una vera richiesta psql end-to-end attraverso il pooler, fino al back-end. Risultato dopo la correzione, nello stesso test ripetuto: unhealthy rilevato in 7 cicli, circa 35 secondi.
Un ultimo dettaglio, minore ma rivelatore: il grafico Helm fissa edoburu/pgbouncer:v1.24.1-p1 per impostazione predefinita, mentre il banco k3d locale utilizza v1.25.2-p0. Non è una scelta architetturale, solo una leggera mancanza di sincronizzazione delle versioni tra due ambienti, il tipo di dettaglio che una revisione del codice cattura più velocemente di un post sul blog. Lo documentiamo così com'è invece di mascherarlo. Per i dettagli sul partizionamento dello schema servito da questo pooler, vedere il nostro articolo sull'isolamento della sicurezza a livello di riga multi-tenant.
Come scegliere tra i tre
Scegli PgBouncer se...
- Cluster Postgres gestito da CloudNativePG o Kubernetes in generale
- Vuoi il pooler più collaudato e ben documentato
- Una base di destinazione per istanza del pooler è adatta alle tue esigenze
Scegli Supavisor se...
- Centinaia o migliaia di basi dietro lo stesso servizio
- È necessario registrare i tenant in modo dinamico tramite un'API, senza ridistribuzione
- Già nell'ecosistema Supabase o disposto a dipendere da esso
Scegli PgCat se...
- Condivisione delle applicazioni già in atto o pianificata a livello di pooler
- Bilanciamento del carico e replica di failover senza livello applicativo separato
- A mio agio con un progetto più giovane, meno documentato di PgBouncer
Qualunque sia il pooler scelto, non sostituisce il dimensionamento di Postgres stesso. La dimensione del pool e il server max_connections dovrebbero essere pensati insieme, non uno dopo l'altro. Una piscina generosa davanti a un max_connections troppo basso sposta semplicemente la saturazione da un livello all'altro. La nostra guida su tuning max_connections descrive in dettaglio la formula di dimensionamento da applicare prima di impostare la dimensione della piscina.
Quello che ci viene chiesto più spesso
Non esiste un pooler universale, ma solo una soluzione adatta alla tua locazione
PgBouncer, Supavisor e PgCat risolvono tre varianti dello stesso problema, non tre versioni dello stesso strumento. PgBouncer rimane la scelta più sicura quando la tua piattaforma si basa già su Kubernetes e CloudNativePG o quando desideri semplicemente il pooler più documentato. Supavisor diventa rilevante oltre un certo numero di basi servite dallo stesso servizio. PgCat vale la deviazione se perdi lo sharding e il failover della replica a livello di rete, a condizione che accetti la maturità di un progetto più giovane.
Il codice Aurabase mostra una scelta coerente e non neutrale: PgBouncer in modalità transazionale, a due livelli, flotta condivisa e pooler CNPG per tenant dedicato. Rimangono due eccezioni documentate, per PostgREST e per l'amministrazione dello schema. Se desideri vedere questo progetto in silo in azione anziché sulla carta, la nostra pagina Performance documenta la metodologia di misurazione associata.