En corto

1
2
3
; /etc/pgbouncer/pgbouncer.ini
[pgbouncer]
ignore_startup_parameters = extra_float_digits, options
1
systemctl reload pgbouncer          # o RELOAD; en la consola de admin: psql -p 6432 -U pgbouncer pgbouncer

extra_float_digits se puede ignorar sin efectos. options (lo que JDBC y DataGrip usan para -c search_path=…) también se ignora, pero entonces el search_path del cliente no llega: fíjalo en el rol con alter role app set search_path = app, public.

El error

1
FATAL: unsupported startup parameter: extra_float_digits

o, según el cliente, pooler error: unsupported startup parameter: extra_float_digits. Directo al puerto 5432 conecta; por el 6432 de pgbouncer, no. Lo mandan JDBC (todo lo que use el driver de PostgreSQL para Java: DBeaver, DataGrip, Spring, Jenkins), Npgsql y algunos ORMs; otros clientes mandan options.

Por qué pasa

Cuando un cliente abre conexión, mete en el paquete de inicio los parámetros de sesión que quiere (extra_float_digits=3, options=-c search_path=app). pgbouncer, en modo transaction o statement, comparte una misma conexión de servidor entre muchos clientes, así que solo deja pasar los parámetros que sabe guardar por cliente y restaurar en el servidor cuando le toca a ese cliente: client_encoding, datestyle, timezone, standard_conforming_strings, application_name. Cualquier otro sería un ajuste que se filtra de un cliente a otro; antes que eso, pgbouncer rechaza la conexión.

Dos formas de resolverlo:

OpciónQué hacePara qué parámetros
ignore_startup_parametersAcepta el parámetro y lo tiraLos que no cambian nada (extra_float_digits) o los que puedes fijar en otro sitio (options)
track_extra_parametersLo guarda por cliente y lo restaura en el servidor, como hace con timezoneParámetros que el servidor reporta al cliente (ParameterStatus) y que de verdad necesitas por sesión

extra_float_digits es inofensivo desde PostgreSQL 12: la salida de float ya es la representación más corta exacta y el parámetro no cambia nada. options con search_path sí importa: ignorado, el cliente verá el search_path por defecto de la base y las consultas sin esquema fallan con relation does not exist. La solución limpia es el rol:

1
alter role app set search_path = app, public;

’no such database'

1
pooler error: no such database: app_db

pgbouncer solo enruta bases que estén en [databases]. O añades la entrada, o pones un comodín:

1
2
3
[databases]
app_db = host=db.example port=5432 dbname=app_db
* = host=db.example port=5432

Con *, un cliente que pida otra_db obtiene host=db.example dbname=otra_db; esas entradas automáticas se limpian solas tras autodb_idle_timeout. Y comprueba el dbname= de la entrada: es habitual apuntar app_db a otra base por un copy-paste.

Trampas

  • RELOAD basta para ignore_startup_parameters y [databases]; listen_* y auth_type piden reinicio.
  • Modo transaction y SET: un SET search_path a mano en la sesión se pierde en cuanto la conexión de servidor pasa a otro cliente; usa SET LOCAL dentro de la transacción o el alter role.
  • Prepared statements con nombre (JDBC prepareThreshold, PREPARE) no funcionan en modo transaction en pgbouncer antiguo; desde 1.21, max_prepared_statements = 200 los soporta.
  • SCRAM: con auth_type = scram-sha-256 la userlist.txt necesita el hash SCRAM tal cual sale de pg_authid, o auth_query para que pgbouncer lo consulte.
  • SHOW CONFIG; y SHOW DATABASES; en la consola de admin te dicen qué está cargado de verdad, no lo que crees haber escrito.

Ver también: PostgreSQL: cheat sheet de administración. Referencia: pgbouncer.ini.