Parte de Lab: RUN_002. Experimento realizado el 18 de septiembre de 2026 con PostgreSQL 17.11.
¿Qué significa too many notifications in the NOTIFY queue? La cola de notificaciones está llena. En esta ejecución, bastó dejar abierta una transacción en una conexión que había ejecutado LISTEN: la ocupación llegó al 100%, los nuevos envíos fallaron y finalizar la transacción permitió recuperar la entrega.
Cómo funciona LISTEN/NOTIFY#
LISTEN registra una conexión para recibir notificaciones de un canal. NOTIFY, o pg_notify(), publica un mensaje. El envío se hace efectivo al confirmar la transacción emisora; un listener dentro de una transacción espera a que esta termine para recibir las notificaciones. La documentación de NOTIFY explica esta relación.
Entorno: Docker y una cola de 512 KiB#
Necesitas Docker con Compose y dos terminales. Guarda este archivo como compose.yaml:
services:
postgres:
image: postgres:17
environment:
POSTGRES_PASSWORD: laboratorio
command: [postgres, -c, max_notify_queue_pages=64]
healthcheck:
test: [CMD-SHELL, "pg_isready -U postgres"]
interval: 1s
timeout: 3s
retries: 30
El contenedor no publica puertos. Usaremos el cliente psql incluido en la imagen mediante docker compose exec.
| Parámetro | Valor observado | | --- | --- | | Versión | PostgreSQL 17.11, Debian, 64 bits | | Tamaño de página (block_size) | 8,192 bytes | | Límite (max_notify_queue_pages) | 64 páginas | | Capacidad configurada | 524,288 bytes: 512 KiB | | Ocupación inicial | 0 |
La etiqueta postgres:17 sigue las actualizaciones de la versión 17; comprueba server_version al repetir la prueba. PostgreSQL 17 permite configurar el límite al arrancar. El valor predeterminado es 1,048,576 páginas: 8 GiB con páginas de 8 KiB. Aquí usamos 64 páginas para alcanzar el límite con pocos mensajes. Consulta el parámetro oficial.
docker compose -p notify-sql-report up -d --wait --wait-timeout 120
Espera a que el contenedor esté saludable. Si la inicialización supera la espera, revisa docker compose -p notify-sql-report logs postgres. En la ejecución documentada, la inicialización terminó después de la primera espera y repetir el comando permitió continuar.
Paso 1: dejar una transacción abierta en el listener#
En la terminal A abre una conexión:
docker compose -p notify-sql-report exec postgres psql -X -U postgres -P pager=off
Ejecuta estas sentencias por separado:
SET application_name = 'listener_informe';
LISTEN demo;
BEGIN;
Deja la conexión abierta. El orden importa: LISTEN quedó confirmado antes de abrir la transacción que mantuvimos pendiente. No había cálculos costosos; pg_stat_activity mostró este estado:
listener_informe|idle in transaction
La sesión esperaba otro comando, pero su transacción seguía abierta.
Paso 2: llenar la cola con pg_notify()#
En la terminal B abre otra conexión con el mismo comando:
docker compose -p notify-sql-report exec postgres psql -X -U postgres -P pager=off
Comprueba configuración y ocupación:
SHOW server_version;
SHOW block_size;
SHOW max_notify_queue_pages;
SELECT pg_notification_queue_usage();
Genera 100 envíos al canal demo, con 7,000 caracteres x y el número de envío en cada payload:
\set ON_ERROR_STOP off
SELECT format(
'SELECT %s AS send_number, pg_notify(%L, %L);',
g, 'demo', repeat('x', 7000) || g
)
FROM generate_series(1, 100) g
\gexec
\gexec ejecuta cada sentencia por separado. Mantuvimos psql en autocommit para confirmar cada envío exitoso y permitimos continuar después de los errores hasta completar los 100 intentos.
Durante los envíos apareció:
WARNING: NOTIFY queue is 50% full
El detalle identificó al proceso 516, el listener, entre los que retenían las transacciones más antiguas. Ese PID pertenece a esta ejecución y puede cambiar. La indicación era terminar su transacción para permitir la limpieza.
Los envíos 1 a 64 tuvieron éxito. Los 36 restantes fallaron con:
ERROR: too many notifications in the NOTIFY queue
Medir la ocupación#
SELECT pg_notification_queue_usage();
El resultado fue 1: 100%. La función devuelve una fracción; 0 significa vacía y 0.5, la mitad. Para mostrar un porcentaje:
SELECT round(
(100 * pg_notification_queue_usage())::numeric, 2
) AS queue_usage_percent;
Los 64 mensajes reflejan esta ejecución y este tamaño de payload; no son una capacidad universal. Las páginas también contienen información interna.
Qué había en pg_notify/#
Con la cola saturada inspeccionamos el directorio:
docker compose -p notify-sql-report exec postgres \
sh -c 'ls -l "$PGDATA/pg_notify"'
| Archivo | Tamaño observado | | --- | ---: | | 000000000000000 | 262,144 bytes | | 000000000000001 | 139,264 bytes |
Los tamaños visibles no sumaban los 512 KiB configurados aunque la función indicaba 100%. Sumar archivos no sustituye esa medición. El código de PostgreSQL 17 describe una cola central compartida entre las bases de datos de la instancia, respaldada por pg_notify/ y gestionada mediante SLRU. Combina páginas en memoria y almacenamiento en disco: no estamos midiendo un límite de RAM.
Paso 3: recuperar la entrega#
En la terminal A ejecutamos:
COMMIT;
El listener mostró las notificaciones pendientes. Al terminar, volvimos a consultar la ocupación desde la terminal B: había bajado a 0. Enviamos otro mensaje:
SELECT pg_notify('demo', 'vuelve a funcionar');
El envío terminó sin error. Tras ejecutar SELECT 1; en el listener, psql mostró el payload vuelve a funcionar. Así verificamos tanto el envío como la recepción después de la recuperación.
| Etapa | Ocupación medida | Resultado | | --- | ---: | --- | | Antes de enviar | 0% | Cola vacía | | Después de 100 intentos | 100% | 64 envíos exitosos y 36 fallidos | | Después del COMMIT del listener | 0% | Notificaciones pendientes entregadas | | Envío de verificación | No se volvió a medir | Mensaje enviado y recibido |
Qué demuestra y qué queda fuera#
La secuencia coincide con el comportamiento documentado: una transacción abierta en un listener puede impedir la limpieza y una cola llena hace fallar al confirmar las transacciones que ejecutan NOTIFY.
Si una aplicación combina escrituras y notificaciones en una transacción, ese fallo puede impedir confirmar las escrituras. Aquí solo enviamos mensajes: no usamos una tabla de negocio para medir ese efecto.
Usamos una sola base de datos. No verificamos interferencia entre bases, el cálculo histórico de wraparound de PostgreSQL 16, una cola de 8 GiB, latencia ni rendimiento de disco. Reducir el límite permitió observar retención, saturación y recuperación sin reiniciar PostgreSQL ni ampliar la capacidad.
Preguntas frecuentes#
¿El límite predeterminado sigue siendo 8 GB?#
Con páginas de 8,192 bytes, el valor predeterminado equivale a 8 GiB. Los 512 KiB son la configuración reducida de este experimento.
¿Aumentar max_notify_queue_pages resuelve la causa?#
Da más espacio, pero no termina la transacción que retiene las notificaciones. En esta ejecución recuperamos el servicio mediante COMMIT, sin cambiar la capacidad.
¿Hace falta reiniciar PostgreSQL?#
No fue necesario en esta prueba. Finalizamos la transacción y comprobamos otro envío. En una aplicación real, confirmar o revertir debe respetar el trabajo de esa transacción.
Limpiar el laboratorio#
Sal de ambas conexiones con \q y elimina el contenedor y sus datos de prueba:
docker compose -p notify-sql-report down -v
Investigaciones relacionadas#
La presión por conexiones y la duración de las transacciones son preguntas distintas del diagnóstico. Consulta cómo dimensionar un pool PostgreSQL y las métricas para incidentes para elegir señales de saturación. Cuando el problema sea SQL costoso, continúa con la optimización de consultas.

