'Too many connections': el pool de conexiones explicado

'Too many connections' significa que todos los espacios de conexión permitidos por max_connections están ocupados, así que la base rechaza clientes nuevos. Se arregla reutilizando conexiones con un pool y cerrando fugas, no subiendo el límite primero.

Actualizado el 6 min de lectura

Objetivos de aprendizaje

Después de leer este artículo podrás:

  • Leer los mensajes de error de MySQL y PostgreSQL y entender a qué límite apuntan
  • Descubrir quién está ocupando tus conexiones
  • Configurar un pool de conexiones con un tamaño seguro para tu app
En esta página

Tu app funcionaba ayer. Hoy se cae al iniciar, o falla al azar bajo carga, con un mensaje que dice que la base de datos no tiene lugar para ti. La base no está caída. Simplemente tiene una cantidad fija de espacios de conexión, y algo los ocupó todos.

Es uno de los errores de producción más comunes en bots, APIs y sitios web, y casi siempre es un problema del lado del cliente: demasiados procesos, falta de pool o conexiones que nunca se cierran.

Proceso 1Proceso 2Proceso 3nuevaconexiónmax_connections3 × pool de 4 = 12 en usomargenBase de datosToo many connections
Procesos por tamaño del pool da el total de conexiones; tiene que quedar por debajo de max_connections, con margen.

¿Cómo se ve el error?#

MySQL (y MariaDB) reportan el error 1040:

text
ERROR 1040 (HY000): Too many connections

En Node.js con mysql2, ves el mismo texto con el código ER_CON_COUNT_ERROR.

PostgreSQL tiene dos variantes, ambas con SQLSTATE 53300:

text
FATAL:  sorry, too many clients already
FATAL:  remaining connection slots are reserved for roles with the SUPERUSER attribute

La primera indica que el servidor llegó a max_connections. La segunda indica que solo quedan los espacios reservados para superusuarios: los roles normales quedan fuera, pero un administrador todavía puede conectarse. Antes de PostgreSQL 16, el segundo mensaje decía "remaining connection slots are reserved for non-replication superuser connections".

¿Qué es una conexión a la base de datos y por qué está limitada?#

Una conexión es una sesión activa entre un cliente y el servidor. En PostgreSQL cada una es un proceso separado del servidor; en MySQL cada una recibe un hilo. En ambos casos consume memoria y estructuras internas del servidor, aunque esté inactiva.

Por eso el servidor limita la cantidad con max_connections:

  • PostgreSQL: el valor por defecto suele ser 100, y superuser_reserved_connections reserva 3 de ellos. Cambiar max_connections exige reiniciar el servidor.
  • MySQL: el valor por defecto es 151. El servidor acepta max_connections + 1 clientes, y el espacio extra es para cuentas con el privilegio CONNECTION_ADMIN, de modo que un administrador todavía pueda conectarse e investigar el problema.

Subir ese número no sale gratis, porque cada espacio extra consume recursos del servidor.

¿Por qué te quedas sin conexiones?#

De la causa más común a la menos común:

  1. Sin pool, una conexión por petición. El código que abre un cliente nuevo en cada petición o evento multiplica las conexiones por el tráfico.
  2. Fugas de conexiones. Tu código toma un cliente del pool y nunca lo libera, muchas veces porque una ruta de error se salta release() o close().
  3. Demasiados procesos. Cada proceso tiene su propio pool. Diez instancias con un pool de 10 son 100 conexiones, todo el valor por defecto de PostgreSQL.
  4. Apps serverless o que se reinician solas. Cada arranque en frío o bucle de caídas abre conexiones nuevas antes de que las anteriores expiren en el servidor.
  5. Transacciones largas y consultas lentas. Las conexiones siguen ocupadas, así que el pool necesita más.
  6. Otros clientes. Herramientas de administración, tareas de migración, scripts de cron y paneles también ocupan espacios del mismo límite.

¿Cómo ves quién está usando las conexiones?#

En PostgreSQL, con un rol que pueda ver otras sesiones:

sql
SHOW max_connections;

SELECT count(*) AS total FROM pg_stat_activity;

SELECT usename, application_name, state, count(*)
FROM pg_stat_activity
GROUP BY usename, application_name, state
ORDER BY count(*) DESC;

Muchas filas en estado idle con el mismo application_name son la señal clásica de una fuga o de un pool demasiado grande.

En MySQL:

sql
SHOW VARIABLES LIKE 'max_connections';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
SHOW FULL PROCESSLIST;

Max_used_connections es el pico desde el último reinicio. Si es igual a max_connections, llegaste al techo al menos una vez. Las entradas Sleep en la lista de procesos son conexiones inactivas.

¿Cómo se arregla con un pool de conexiones?#

Un pool abre una cantidad pequeña de conexiones una sola vez y se las presta a tu código. Las consultas esperan un momento a que haya una libre en vez de abrir una nueva. Crea el pool una vez al iniciar, compártelo en todo el proceso y nunca lo crees dentro de un manejador de peticiones.

Con pg (node-postgres), cuyo pool usa un max de 10 por defecto:

js
// db.js
import pg from "pg";

export const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  max: 5, // límite superior para ESTE proceso
  idleTimeoutMillis: 10_000, // cierra clientes inactivos tras 10 s
  connectionTimeoutMillis: 5_000, // falla rápido en vez de esperar para siempre
});

// Para consultas sueltas, deja que el pool tome y libere la conexión:
const { rows } = await pool.query("SELECT now()");
console.log(rows[0]);

// Para transacciones, usa un solo cliente y libéralo SIEMPRE:
const client = await pool.connect();
try {
  await client.query("BEGIN");
  await client.query("UPDATE accounts SET balance = balance - 10 WHERE id = $1", [1]);
  await client.query("COMMIT");
} catch (err) {
  await client.query("ROLLBACK");
  throw err;
} finally {
  client.release();
}

No ejecutes una transacción con pool.query, porque cada llamada puede caer en un cliente distinto.

Con mysql2:

js
import mysql from "mysql2/promise";

export const pool = mysql.createPool({
  uri: process.env.DATABASE_URL,
  connectionLimit: 5,
  waitForConnections: true,
});

const [rows] = await pool.query("SELECT NOW() AS now");
console.log(rows[0]);

En Python funciona igual: crea un engine (SQLAlchemy) o un pool al importar el módulo y reutilízalo.

¿De qué tamaño debe ser el pool?#

Haz la multiplicación antes de elegir un número:

text
conexiones totales = procesos x tamaño del pool

Si corres 4 procesos con un pool de 5, usas 20 conexiones. Suma margen para migraciones, un cliente SQL que abras a mano y tareas de cron, y mantén el total bastante por debajo del límite. Un pool de 5 a 10 por proceso suele bastar, porque una conexión solo está ocupada mientras corre una consulta, así que puede atender muchas peticiones por segundo.

Si un solo proceso de verdad necesita cientos de consultas simultáneas, el cuello de botella probablemente es la base de datos, y más conexiones no la harán más rápida.

¿Cuándo subir el límite o añadir PgBouncer?#

Sube max_connections cuando tus pools ya tienen un tamaño razonable y simplemente tienes más procesos que espacios. En MySQL puedes cambiarlo en caliente:

sql
SET GLOBAL max_connections = 300;   -- se pierde al reiniciar
SET PERSIST max_connections = 300;  -- sobrevive al reinicio

En PostgreSQL, edita postgresql.conf y reinicia. Recuerda que más conexiones usan más memoria.

Cuando tienes muchos procesos o clientes de vida corta, coloca un pooler como PgBouncer delante de PostgreSQL. En el modo de pool por transacción, una conexión del servidor se asigna solo mientras dura una transacción, así que un conjunto pequeño de conexiones del servidor atiende a muchos más clientes. La contrapartida: las funciones que dependen del estado de la sesión, como SET, LISTEN, los advisory locks a nivel de sesión y el PREPARE a nivel de SQL, no funcionan de forma confiable en ese modo.

¿Cómo evitas que vuelva a pasar?#

  • Crea un pool por proceso, al iniciar.
  • Libera en bloques finally y cierra el pool al apagar con pool.end().
  • Configura timeouts de inactividad y de obtención de conexión para que los problemas fallen de forma visible.
  • Dale a cada app su propio usuario de base de datos o application_name para saber quién está conectado.
  • Crea una alerta cuando Threads_connected o la cantidad de filas de pg_stat_activity se acerque al 80 por ciento del límite.

¿No sabes qué motor encaja con tu proyecto? Cómo elegir una base de datos compara las opciones, qué es PostgreSQL explica el motor en sí y cómo alojar una base de datos muestra cómo poner una en marcha.

Cómo hacerlo en Square Cloud#

En Square Cloud creas la base de datos en el panel y haces el deploy de tus apps, así que la configuración del pool vive en tu código y en tus variables de entorno. El hosting de bases de datos para MongoDB, PostgreSQL, MySQL y Redis empieza en el plan Standard. Las bases corren en el mismo datacenter que tus aplicaciones, con TLS obligatorio.

Cada base acepta hasta 512 conexiones y no hay pooler delante, así que lo que controla la cantidad de conexiones es el pool de tu aplicación. Guarda la URL de conexión en una variable de entorno de cada aplicación y dimensiona el pool de cada proceso como se explicó arriba. Suma los pools de todas las aplicaciones que apuntan a la misma base y mantén el total con margen por debajo de ese tope.

Cada reinicio vuelve a abrir el pool. El reinicio automático es opcional (viene activado al hacer el deploy desde el panel) y solo actúa cuando el proceso termina con error. Si la app entra en un bucle de caídas, cada vuelta abre un pool nuevo, así que busca en los logs la causa del error y corrígela.

Consulta los motores y planes en la página de hosting de bases de datos.

Hosting de bases de datos

MongoDB, PostgreSQL, MySQL y Redis junto a tu app

Más información

Preguntas frecuentes

¿Cuál es el max_connections por defecto en PostgreSQL y MySQL?

PostgreSQL usa 100 por defecto (puede ser menos si el kernel no lo soporta) y reserva 3 espacios para superusuarios. MySQL usa 151 por defecto, más un espacio extra para cuentas de administrador.

¿Basta con subir max_connections?

Solo después de buscar fugas y añadir un pool. Cada conexión cuesta memoria en el servidor, y PostgreSQL necesita reiniciar para cambiar el valor. Un límite más alto muchas veces solo retrasa el mismo error.

¿Cuántas conexiones debe abrir un pool?

Empieza con poco, entre 5 y 10 por proceso de la app, y multiplica por la cantidad de procesos e instancias. El total debe quedar por debajo del límite de la base, con margen para herramientas de administración y migraciones.

¿Qué diferencia hay entre un pool dentro de mi app y PgBouncer?

Un pool dentro de la app comparte conexiones dentro de un proceso. PgBouncer se coloca delante de PostgreSQL y comparte un conjunto menor de conexiones del servidor entre muchos procesos o servidores.

Siguiente artículoMigraciones de base de datos en el deployEjecuta migraciones de base de datos en el deploy con Prisma, Drizzle o Alembic: un paso antes de arrancar el código nuevo, a salvo de fallos. Comandos y errores.

Más en Bases de datos

Ver los 24 artículos