Error de PostgreSQL: out of shared memory
ERROR: out of shared memory
HINT: You might need to increase max_locks_per_transaction.
A pesar de la formulación alarmante, esto casi nunca tiene que ver con que el servidor se quede sin memoria en general. Cuando viene con el hint sobre max_locks_per_transaction, significa una cosa concreta: la tabla de bloqueos compartida — la estructura de tamaño fijo donde PostgreSQL rastrea los bloqueos sobre tablas, índices y otros objetos — está llena.
Qué significa este error
La tabla de bloqueos se dimensiona al arrancar el servidor, con espacio para aproximadamente max_locks_per_transaction × (max_connections + max_prepared_transactions) bloqueos de objeto. Dos propiedades explican todo lo que observarás:
- A pesar del nombre,
max_locks_per_transactionno es un tope por transacción — solo dimensiona el pool compartido. Una sola transacción puede sostener felizmente miles de bloqueos mientras el pool tenga espacio. - Lo que también significa que una transacción glotona puede vaciar el pool para todos: el error puede golpear a un espectador inocente, no a la transacción que consumió los bloqueos.
Cada tabla, partición e índice tocado por una consulta necesita su propia entrada de bloqueo, sostenida hasta el final de la transacción. Esa es la aritmética que te atrapa: una consulta sobre una tabla con 2.000 particiones y unos cuantos índices cada una puede necesitar muchos miles de entradas — mientras que el dimensionamiento por defecto (64 × ~100 conexiones) te da unas 6.400 para todo el servidor.
Causas comunes
- Tablas particionadas con muchas particiones: una consulta que no puede podar toca cada partición y cada uno de sus índices. Con diferencia el disparador más común.
pg_dump/pg_restoresobre bases de datos con un número muy grande de objetos (bloquea cada tabla que vuelca en una sola transacción).- Migraciones que alteran o eliminan muchas tablas en una sola transacción.
- Muchas transacciones concurrentes tocando cada una un número moderado de relaciones — el pool es compartido, así que lo que importa es la suma.
Cómo diagnosticarlo
Mientras se ejecuta la carga de trabajo que falla (o justo antes de que falle):
-- Cuán llena está la tabla de bloqueos:
SELECT count(*) FROM pg_locks;
-- Quién está consumiendo las entradas:
SELECT pid, count(*)
FROM pg_locks
GROUP BY pid
ORDER BY count(*) DESC
LIMIT 10;
Compara el total con la fórmula de arriba usando tus ajustes. Si un pid sostiene miles de bloqueos, mira qué está ejecutando — y si hay particiones implicadas, cuéntalas: SELECT count(*) FROM pg_inherits WHERE inhparent = 'events'::regclass;
Cómo solucionarlo
- Sube
max_locks_per_transaction— p. ej. de 64 a 256 o 512 en servidores con schemas fuertemente particionados. Requiere un reinicio; la memoria compartida extra es modesta.ALTER SYSTEM SET max_locks_per_transaction = 256; -- luego reinicio del servidor - Ayuda a la poda de particiones: asegúrate de que las consultas filtran por la clave de partición con valores que el planificador pueda ver, de modo que solo se bloqueen las particiones relevantes.
- Divide las operaciones amplias: parte las migraciones y los scripts de mantenimiento en varias transacciones en vez de una gigante.
- Reconsidera el número de particiones: los schemas con muchos miles de particiones diminutas pagan este impuesto en todas partes, no solo aquí.
