Nodo Oracle nativo abriendo demasiadas sesiones de BD concurrentes en modo de cola con múltiples workers

Describa el problema/error/pregunta

Las ejecuciones en paralelo usando el nodo nativo de Oracle Database abren un gran número de sesiones simultáneas de Oracle, y la base de datos comienza a sufrir contención de latches.

El problema principal es que el alto número de conexiones de Oracle abiertas simultáneamente por n8n está abrumando la base de datos. Cada conexión de n8n se asigna a una sesión de servidor dedicado en Oracle, por lo que cuando muchas ejecuciones se ejecutan en paralelo, se abren docenas de sesiones al mismo tiempo y la instancia comienza a sufrir contención de latches. Mi objetivo es limitar cuántas sesiones de Oracle pueden estar abiertas al mismo tiempo para que la base de datos deje de estar abrumada.

Estoy ejecutando en modo cola con 3 contenedores de worker y N8N_WORKER_CONCURRENCY=10 (hasta ~30 ejecuciones concurrentes).

Mi comprensión actual (por favor corrígeme): en modo cola cada worker es un proceso separado, por lo que asumo que el pool de conexiones de Oracle se crea por worker, lo que significa que Pool Max se aplica por worker y no globalmente.

Preguntas:

  1. ¿Se crea el pool de conexiones de Oracle por proceso de worker (por lo que Pool Max es por worker), o se comparte de alguna manera?

  2. Durante ejecuciones en paralelo, ¿el nodo retira una conexión por ejecución (1:1), o por query / por elemento?

  3. Para reducir las sesiones concurrentes de Oracle, ¿cuál es el enfoque recomendado: reducir N8N_WORKER_CONCURRENCY, reducir el número de workers, reducir Pool Max, o una combinación? ¿Cuál es el principal?

  4. Si reduzco Pool Max, ¿las solicitudes de conexión adicionales se encolan y esperan una conexión libre en lugar de abrumar la BD?

  5. ¿Este nodo ejecuta node-oracledb en modo Thin o Thick por defecto? Si es Thick, ¿es necesario ajustar UV_THREADPOOL_SIZE junto con Pool Max?

¿Cuál es el mensaje de error (si hay alguno)?

No hay error a nivel de aplicación en n8n. El síntoma está del lado de Oracle: docenas de sesiones activas ejecutando el mismo SQL_ID simultáneamente, atrapadas en latch: cache buffers chains y cpu runqueue. Muestra resumida:

SID  SERIAL   USER     PROG  TYPE  STATE  WAIT_CLASS  EVENT
...  3332220  APPUSER  node  DED   CPU    Other       cpu runqueue
...  3334370  APPUSER  node  DED   CPU    Concurrency latch: cache buffers chains
...  3331979  APPUSER  node  DED   CPU    Other       latch free
...  3332223  APPUSER  node  DED   CPU    Other       PGA memory operation
(docenas más, mismo SQL_ID, mismos eventos de espera)

Por favor comparta su workflow

(No es relevante para esta pregunta. El problema es la concurrencia de conexiones/sesiones, no un workflow específico.)

Comparta la salida devuelta por el último nodo

(No aplica.)

Configuración actual del pool de Oracle (por credencial)

  • Pool Min: 0

  • Pool Max: 600

  • Pool Increment: 5

  • Pool Maximum Session Life Time: 3600

  • Pool Connection Idle Timeout: 180

  • Connection Class Name: no configurado

  • Connection Timeout: 0

  • Transport Connection Timeout: 20

  • Keepalive Probe Interval: 60

Información sobre su instalación de n8n

  • Versión de n8n: Version 2.20.7-exp.0

  • Versión del nodo de Oracle Database: Oracle Database node version 1 (Latest)

  • Base de datos (la propia de n8n): PostgreSQL 16

  • Versión de Oracle Database (la BD objetivo):

  • Modo n8n EXECUTIONS_PROCESS / mode: modo cola (Redis/Bull), 3 workers, N8N_WORKER_CONCURRENCY=10

  • Ejecutando n8n mediante: Docker (autoalojado)

  • Sistema operativo: RedHat

Hola @giovanni.tavares

Tu suposición es correcta. En modo cola, cada worker es un proceso separado, por lo que el pool es por worker, no compartido, lo que significa que tu Pool Max 600 es realmente hasta 1800 en 3 workers.

El nodo obtiene una conexión por ejecución, no por consulta o elemento, por lo que cada ejecución mantiene una conexión. Con concurrencia 10, un worker nunca necesita más de ~10.

Entonces Pool Max es tu principal palanca de control: establécela por worker alrededor de tu concurrencia (10-15), y las sesiones totales rondan los workers multiplicados por eso, aproximadamente 30-45. Cuando el pool está al máximo, node-oracledb pone en cola las solicitudes de conexión hasta que una se libera, en lugar de sobrecargar la BD.

Ejecutar en modo delgado por defecto, por lo que UV_THREADPOOL_SIZE solo importa si habilitas modo grueso.

Hola @achamm

Actualización después de investigar más. Bajé Pool Max y empecé a recibir NJS-076: connection request rejected. Pool queue length "queueMax" 500 reached. Entonces las solicitudes no se quedan esperando en la cola indefinidamente. node-oracledb solo pone en cola las llamadas pendientes a getConnection() hasta queueMax (por defecto 500), y rechaza todo lo que vaya más allá de eso.

Resulta que la razón por la que la cola se llena es mi arquitectura, no el checkout por elemento. Mi flujo de trabajo principal obtiene 1000 registros y los envía a un subflujo con “Wait For Sub-Workflow Completion” deshabilitado. Asumí que N8N_WORKER_CONCURRENCY (3 workers x 10) limitaría esto a ~30 concurrentes y el resto esperaría su turno.

Pero según la documentación de n8n, el límite de concurrencia solo se aplica a las ejecuciones de producción iniciadas desde un nodo de disparador o webhook, y explícitamente no se aplica a las ejecuciones de subflujos. El flujo principal (una ejecución de disparador) está limitado, pero las 1000 ejecuciones de subflujo que genera no lo están. Se envían en volumen, cada una solicita una conexión Oracle aproximadamente al mismo tiempo, la cola del pool se llena hasta 500, y el resto se rechaza con NJS-076. El número que coincide con los 500 por defecto confirma esto.

Entonces hay realmente dos colas separadas aquí: la cola de ejecución de n8n (Redis/Bull) y la cola del pool de node-oracledb. El límite de concurrencia solo controla la primera, y no para subflujos.

Preguntas:

  1. ¿Es este el comportamiento esperado, que las ejecuciones de subflujo activadas con “Wait For Sub-Workflow Completion” deshabilitado omitan completamente el límite de concurrencia? ¿Proporciona N8N_WORKER_CONCURRENCY algún tipo de limitación para ellas en modo cola, o ninguna en absoluto?

  2. Dado eso, ¿cuál es el patrón recomendado para limitar el envío para no saturar el pool de conexiones: mantener “Wait For Sub-Workflow Completion” activado, usar Loop Over Items / Split in Batches en el flujo principal para enviar lotes más pequeños, o agrupar registros para que se ejecuten menos subflujos?

  3. ¿Es queueMax configurable a través de la credencial de Oracle? No está en los campos de credencial actuales, así que asumo que está fijo en 500.

Por ahora mi plan es controlar el fan-out en el lado del flujo principal en lugar de confiar en Pool Max, ya que bajar Pool Max solo traslada el fallo de “BD abrumada” a “pool queue rejected”. Me encantaría escuchar si hay un enfoque más limpio.

bienvenido a la comunidad n8n @giovanni.tavares
n8n recomienda una concurrencia de 5 o más por worker; valores muy bajos con muchos workers pueden agotar el pool de conexiones de la base de datos. La fórmula aproximada de sesiones máximas sería: número_de_workers × Pool Max Configuring queue mode | n8n Docs

siguen otros documentos que pueden apoyarte

Gracias @tamy.santos. La fórmula workers × Pool Max confirma que el pool es por worker, lo que coincide con lo que dijo @achamm.

Lo que sigo sin poder resolver es que esta fórmula describe el límite máximo de sesiones establecidas en estado estable, pero mi fallo es diferente. NJS-076 es la cola de solicitudes de conexión (queueMax 500) que se llena porque las solicitudes llegan más rápido de lo que se liberan las conexiones, no porque se exceda el límite de sesiones. La parte complicada es que bajar Pool Max en realidad hace que NJS-076 sea más probable (menos conexiones para absorber el pico), mientras que subirlo vuelve a causar el problema original de saturar la BD. Así que no hay un valor de Pool Max estable a menos que el pico mismo de solicitudes de conexión se regule.

Ese pico viene de que el workflow padre dispara ~1000 ejecuciones de sub-workflows con “Wait For Sub-Workflow Completion” desactivado, y según la documentación el límite de concurrencia de producción no se aplica a las ejecuciones de sub-workflows, así que no están limitados por la concurrencia del worker.

Así que mi plan es regular en el lado del padre en lugar de ajustar Pool Max: o mantener “Wait For Sub-Workflow Completion” activado, o usar Loop Over Items / Split in Batches para disparar en lotes más pequeños, o agrupar registros para que se ejecuten menos sub-workflows.

¿Alguien tiene un patrón recomendado para regular el envío de sub-workflows en modo cola, dado que el límite de concurrencia no los cubre? Ese parece ser el verdadero punto de control aquí.

@giovanni.tavares
¿cuál es la arquitectura de tu flujo?

Por supuesto, aquí está la arquitectura:

Flujo de trabajo principal

  • El disparador se ejecuta y obtiene ~1000 registros de la API.

  • Un nodo Execute Workflow configurado en “ejecutar una vez para cada elemento” dispara una ejecución de subflujo por registro, es decir, ~1000 ejecuciones de subflujo.

  • “Wait For Sub-Workflow Completion” está deshabilitado, por lo que el flujo principal los dispara todos sin esperar.

Subflujo (una ejecución por registro)

  • Ejecuta varias consultas Oracle contra la misma base de datos. Se ejecutan secuencialmente en un flujo lineal, por lo que una sola ejecución mantiene como máximo una conexión a la vez. El número de consultas por ejecución no multiplica las conexiones concurrentes; lo que impulsa la concurrencia es cuántas ejecuciones de subflujo se ejecutan al mismo tiempo.

Infraestructura

  • Modo de cola, Redis/Bull, 3 contenedores de trabajador, N8N_WORKER_CONCURRENCY=10.

  • Nodo Oracle Database nativo, versión 2.20.7-exp.0.

El problema es que las ~1000 ejecuciones de subflujo no están limitadas por el límite de concurrencia (ya que no se aplica a las ejecuciones de subflujo), por lo que golpean el grupo de Oracle en ráfagas y desbordan la cola de solicitudes de conexión (queueMax 500), generando NJS-076.

Esto parece ser un problema de límite de concurrencia más que un problema de consulta de Oracle. Con modo de cola y múltiples workers, asumiría que cada worker puede crear su propio conjunto de sesiones hasta que se demuestre lo contrario, luego reduce la concurrencia de workers o canaliza el trabajo pesado de Oracle a través de un subflujo controlado. Tendría cuidado con los reintentos aquí porque pueden empeorar el pico de sesiones. ¿Has probado el mismo flujo de trabajo con un worker y baja concurrencia para confirmar que el conteo de sesiones disminuye?