Módulo 5: Dependencias entre workflows
5. Orden y contrapresión con queue mode
Descripción
Al terminar esta lección vas a poder reconocer y razonar sobre lo que pasa cuando los eventos llegan más rápido de lo que tu sistema los procesa: la acumulación —una cola que crece—, la pérdida de orden —eventos que se aplican al revés— y, en el peor caso, la pérdida de eventos. Vas a entender qué es el queue mode de n8n y cómo da dos cosas distintas —capacidad (más workers) y control de ritmo (límites de concurrencia)—, y vas a aprender la consecuencia incómoda que casi nadie anticipa: que correr cosas de verdad en paralelo no conserva el orden por sí solo, y que el orden, cuando importa, hay que garantizarlo en la capa de datos con una versión y el ledger, no confiándolo al motor.
Esto importa porque la contrapresión —ese nombre raro para "me está entrando más de lo que puedo sacar"— es la causa de una familia entera de bugs que solo aparecen bajo carga y por eso nunca se ven en la demo. Un pedido y su corrección que se aplican al revés dejando el inventario mal; una ráfaga de eventos que satura la instancia y algunos se quedan sin procesar; una cola que crece sin que nadie la mire hasta que la latencia se dispara. Son problemas de ritmo, no de lógica, y se diagnostican distinto. Esta lección te da el vocabulario y las defensas.
Conexión con el módulo: la lección 4 te dejó una puerta abierta —el fan-out da paralelismo, y el paralelismo de verdad viene del queue mode— y esta lección la cruza. Es también donde se resuelve el desastre 2 de la lección 1, la pérdida de orden. Y prepara la lección 6: parte de la defensa contra la contrapresión es desacoplar "recibir" de "procesar" con una cola intermedia, que es medio paso hacia el patrón outbox. Una aclaración de alcance, la misma que se anunció en la introducción: aquí tratamos el queue mode como el mecanismo que da capacidad y control de ritmo; montar y dimensionar un clúster de workers en producción es trabajo de la guía de producción y mantenimiento. Vas a entender qué es y por qué importa para el orden; afinarlo para escala es otra guía.
Cuando entra más de lo que sale
Piensa en el mostrador de una cafetería en la hora pico. Hay un solo barista. Los clientes hacen pedidos más rápido de lo que el barista prepara cafés. ¿Qué pasa? Se forma una fila. Mientras la fila avanza a un ritmo razonable, todo bien. Pero si los pedidos siguen entrando más rápido de lo que salen los cafés, la fila crece, y crece, y empiezan a pasar tres cosas malas.
La primera: la espera se dispara. El último cliente que llegó tiene que esperar todos los cafés de adelante. La cola no solo es larga, es lenta de atravesar.
La segunda, más sutil: el orden se puede perder. Supón que para ir más rápido, la cafetería pone un segundo barista. Ahora hay dos preparando de la misma fila. El cliente A pidió antes que el B, pero el barista 1 tomó el pedido de A (un café elaborado, lento) y el barista 2 tomó el de B (un café simple, rápido). B recibe su café antes que A, aunque pidió después. Si el orden importaba —imagina que A pidió "un café" y B, un segundo después, pidió "cancela lo anterior, mejor un té"— procesarlos en paralelo y a destiempo produce un resultado incorrecto: se prepara el café que se iba a cancelar.
La tercera, la peor: la pérdida. Si la fila crece sin límite y el local cierra, los clientes que quedaron sin atender se van sin su café. En un sistema, si la cola de eventos crece más allá de lo que la memoria o la configuración aguantan, o si los eventos tienen un tiempo de vida y expiran, algunos simplemente no se procesan nunca.
Esas tres cosas —espera que se dispara, orden que se pierde, eventos que se pierden— son lo que pasa cuando el que produce va más rápido que el que consume. El nombre técnico de esa situación es contrapresión (en inglés, backpressure): la presión que se acumula hacia atrás cuando la salida no da abasto con la entrada. Toda la lección es sobre cómo manejarla sin que cause daño.
Contrapresión en Cumbre
Traigamos esto a los pedidos de Cumbre. El proveedor —la tienda en línea— dispara un webhook por cada pedido. En un día normal, entran pocos pedidos por minuto y order-triage los procesa de sobra. Pero llega el Buen Fin, o una promoción, y de golpe entran cientos de pedidos por minuto. order-triage, que además llama a check-credit (que espera al buró) y descuenta inventario, no procesa tan rápido como entran. Se forma la fila.
Y aquí Cumbre sufre las tres formas del problema:
- Acumulación: las ejecuciones de
order-triagese encolan. Un pedido que entró a las 12:00 quizás no se termina de procesar hasta las 12:08. El cliente ya recibió el correo de "pedido recibido" de la tienda, pero en el sistema de Cumbre todavía no existe. - Pérdida de orden: un cliente hace un pedido y a los treinta segundos lo modifica. Entran dos eventos para el mismo
order_id: "pedido de 12 kg" y "corrección: 8 kg". Si esos dos eventos se procesan en paralelo, o si el segundo adelanta al primero,inventory-syncpuede terminar descontando 12 kg cuando debía descontar 8, o aplicar la corrección y luego el pedido original encima. El desastre 2 de la lección 1, en vivo. - Pérdida: si la ráfaga es tan grande que la instancia se satura y algunas ejecuciones fallan por falta de recursos sin reintentarse, hay pedidos que no se procesaron y nadie se enteró.
Las tres tienen defensas distintas, y el queue mode es la herramienta central para las dos primeras. Veámoslo.
Qué es el queue mode
Por defecto, una instancia de n8n corre en modo regular: un solo proceso principal recibe los disparos y ejecuta los workflows él mismo. Es simple y alcanza para mucho. Pero ese proceso único es un límite: si le entran más ejecuciones de las que puede correr a la vez, se forma la fila, y no hay a quién más pedirle ayuda.
El queue mode (modo cola) cambia esa arquitectura. En vez de un proceso que hace todo, hay tres piezas:
- Un proceso principal que recibe los disparos —los webhooks, los schedules— pero no ejecuta los workflows. En lugar de ejecutarlos, los encola.
- Una cola, que en n8n es Redis (una base de datos en memoria, muy rápida, pensada justo para hacer de cola). El principal mete ahí cada ejecución pendiente.
- Uno o varios workers: procesos separados cuyo único trabajo es sacar ejecuciones de la cola de Redis, correrlas, y guardar el resultado en Postgres.
La analogía es directa: el proceso principal es el que toma los pedidos en el mostrador y los anota en un riel; Redis es el riel de comandas; los workers son los baristas que toman comandas del riel y preparan. La ventaja salta a la vista: si la fila crece, pones más baristas —más workers— y la capacidad sube. Ya no dependes de un solo proceso.
# Modo regular
disparos ──▶ [ proceso principal: recibe Y ejecuta ] ──▶ Postgres
# Queue mode
disparos ──▶ [ principal: recibe y encola ] ──▶ Redis ──┬──▶ worker 1 ──▶ Postgres
├──▶ worker 2 ──▶ Postgres
└──▶ worker 3 ──▶ Postgres
Activar queue mode es, a grandes rasgos, poner la variable EXECUTIONS_MODE=queue en el principal y en los workers, y tener un Redis disponible. Está incluido en la edición Community self-hosted; no es una función de pago. Los detalles de montaje —cuántos workers, cómo se conectan, cómo se dimensiona Redis— son de la guía de producción; aquí nos quedamos en qué hace y por qué importa.
Las dos cosas que da el queue mode: capacidad y control de ritmo
El queue mode resuelve dos problemas distintos, y conviene no confundirlos.
Capacidad: más workers, más throughput. Si la contrapresión viene de que no procesas rápido, agregar workers procesa más en paralelo. Tres workers hacen aproximadamente el triple de trabajo por minuto que uno. Esto ataca la acumulación: la fila se drena más rápido porque hay más manos.
Control de ritmo: límites de concurrencia. A veces el problema es el contrario: no quieres procesar demasiado rápido, porque río abajo hay algo frágil. Si inventory-sync golpea un sistema de bodega que solo aguanta veinte peticiones simultáneas, soltarle cien en paralelo lo tumba. Aquí quieres un límite de concurrencia: "procesa como máximo N a la vez". n8n tiene un control de concurrencia —la variable N8N_CONCURRENCY_PRODUCTION_LIMIT fija cuántas ejecuciones de producción corren a la vez, y en queue mode cada worker tiene además su propio límite (la opción --concurrency al arrancar el worker)—. Esto convierte tu sistema en un regulador: pase lo que pase en la entrada, río abajo no le llegan más de N a la vez. Es una defensa contra la cascada de la lección 1: pones un techo a cuánto puedes saturar a un servicio frágil.
Fíjate en la tensión entre las dos. Capacidad quiere ir más rápido; control de ritmo quiere no ir demasiado rápido. El punto correcto depende de tu cuello de botella: si el cuello es n8n, subes capacidad; si el cuello es un servicio río abajo, bajas el ritmo para no tumbarlo. Diagnosticar cuál de los dos es tu cuello —mirando dónde se acumula la fila— es la mitad del trabajo de operar bajo carga.
La sorpresa: el paralelismo no conserva el orden
Aquí está la parte que casi nadie anticipa, y que es la razón por la que esta lección junta "orden" y "contrapresión" en el mismo título.
Cuando activas queue mode y pones varios workers, ganas capacidad —pero pierdes la garantía de orden—. Redis reparte las ejecuciones de la cola entre los workers por orden de llegada, pero los workers corren en paralelo y a ritmos distintos. El worker 1 toma el evento A, el worker 2 toma el evento B que llegó justo después, y si el trabajo de A tarda más que el de B, B termina antes que A. Es exactamente el caso de los dos baristas: el que pidió después recibe primero.
Para la mayoría de los eventos, esto da igual: dos pedidos de dos clientes distintos se pueden procesar en cualquier orden sin problema. El peligro aparece cuando dos eventos son del mismo order_id y tienen un orden correcto entre sí. El pedido y su corrección. La creación y la cancelación. Si esos dos caen en workers distintos y se procesan a destiempo, el resultado queda mal, y no hay error: el sistema hizo su trabajo, solo que en el orden equivocado.
La conclusión es dura y vale la pena grabarla: el motor te da capacidad, no orden. Si tu sistema tiene eventos que deben aplicarse en cierto orden, no puedes confiarle ese orden al motor de ejecución —regular o queue mode—. Tienes que garantizarlo tú, en la capa de datos. Y la buena noticia es que la herramienta para hacerlo ya la tienes: el ledger.
Garantizar el orden en la capa de datos
Hay dos formas de asegurar que los eventos de un mismo order_id no se apliquen mal, y las dos viven en los datos, no en el motor.
Forma 1 — Versión + verificación (control de concurrencia optimista)
La idea: cada evento de un pedido trae un número de versión que dice cuál es su lugar en la secuencia. El pedido original es la versión 1; la corrección es la versión 2. Y en el ledger guardas, para cada order_id, cuál es la última versión que aplicaste. Antes de aplicar un evento, comparas:
- Si la versión del evento que llega es mayor que la última aplicada, es más nuevo: lo aplicas y actualizas la versión guardada.
- Si es menor o igual, es viejo o repetido: no lo aplicas, porque ya procesaste algo más nuevo.
Evento llega con order_id = ORD-2041, version = 2 (la corrección: 8 kg)
1. Postgres: SELECT last_version FROM ledger WHERE order_id = 'ORD-2041';
2. IF version_del_evento > last_version:
aplicar el efecto (descontar 8 kg)
UPDATE ledger SET last_version = 2 WHERE order_id = 'ORD-2041';
ELSE:
ignorar (llegó un evento más viejo o repetido)
Con esto, el orden deja de importar en el motor. Si la corrección (v2) llega y se procesa antes que el original (v1) —porque cayó en un worker más rápido—, cuando después llegue el original (v1), la verificación ve que last_version ya es 2, y descarta el v1 por viejo. El resultado final es correcto —queda la versión 2— sin importar en qué orden se procesaron. A esta técnica se le llama control de concurrencia optimista: no bloqueas nada, dejas que las cosas corran en paralelo, y usas la versión para que el desorden no cause daño. Es la misma familia de ideas que la escritura condicional que viste en el Módulo 2.
Forma 2 — Serializar por clave
La otra forma es más directa y más costosa: procesar los eventos de un mismo order_id de uno en uno, nunca en paralelo. Distintos pedidos siguen corriendo en paralelo —eso te da capacidad— pero dos eventos del mismo pedido se serializan.
En n8n, garantizar esto de forma estricta requiere un mecanismo de bloqueo: antes de procesar un evento de ORD-2041, tomas un "candado" para ese order_id en el ledger (una fila que marca "ORD-2041 en proceso"); si otro worker ya lo tiene, esperas o reencolas. Es más complejo de armar y reduce el paralelismo para los eventos que comparten clave. Se justifica cuando el orden es crítico y no puedes representar la secuencia con una versión —por ejemplo, cuando cada evento es un incremento que depende del estado anterior y no un reemplazo—.
Cuál usar. Para la mayoría de los casos de Cumbre —donde un evento reemplaza el estado (la corrección de 8 kg reemplaza los 12 kg)— la Forma 1, versión + verificación, es más simple y no sacrifica paralelismo. La Forma 2, serializar, se reserva para cuando los eventos son incrementales y de verdad no pueden aplicarse fuera de orden. Empieza siempre por la versión; sube a serializar solo si la versión no puede capturar tu secuencia.
Ejemplo trabajado: el pedido y su corrección, resueltos con versión
Vamos a ver la Forma 1 completa con el caso que da miedo: el pedido de 12 kg y su corrección a 8 kg, procesados al revés.
El montaje. Cada evento de pedido que Cumbre recibe trae ahora un campo version que el proveedor incrementa con cada cambio del mismo pedido:
{ "order_id": "ORD-2041", "version": 1, "quantity_kg": 12 } // el pedido original
{ "order_id": "ORD-2041", "version": 2, "quantity_kg": 8 } // la corrección
En el ledger, una columna last_version por order_id. En inventory-sync, antes de descontar, la verificación de arriba.
El escenario del desorden. Es Buen Fin, hay tres workers, y los dos eventos caen en workers distintos. Por mala suerte, la corrección (v2) se procesa primero:
Qué esperar, paso a paso.
- Llega v2 (8 kg) a un worker. Consulta el ledger:
last_versionparaORD-2041está vacío (o en 0). Como2 > 0, aplica: descuenta 8 kg y ponelast_version = 2. - Un instante después, llega v1 (12 kg) a otro worker. Consulta el ledger:
last_versionya es2. Como1 > 2es falso, no aplica nada. El evento v1, que es más viejo, se descarta. - Estado final: se descontaron 8 kg,
last_version = 2. Correcto, aunque los eventos se procesaron en el orden equivocado.
Compáralo con lo que habría pasado sin la versión: v2 descuenta 8 kg, luego v1 descuenta 12 kg encima, y terminas con 20 kg descontados o con el pedido en el estado viejo —en cualquier caso, mal—. La versión convirtió un desorden peligroso en un desorden inofensivo. Ese es el objetivo: no evitar que las cosas lleguen desordenadas —eso es carísimo y a veces imposible— sino hacer que el desorden no cause daño.
Un detalle honesto. Esto asume que el proveedor te da un version confiable y creciente. Si no lo da, a veces puedes usar una marca de tiempo del evento (created_at) como sustituto —"aplica solo si este evento es más nuevo que el último aplicado"— con el cuidado de que dos eventos con la misma marca, o relojes desincronizados, complican la comparación. Si no tienes ni versión ni marca de tiempo confiable, estás en el terreno de la Forma 2 (serializar) o de negociar con el proveedor que incluya una. Es un buen ejemplo de por qué el contrato de entrada del Módulo 3 importa: un campo version en el contrato es lo que hace posible toda esta defensa.
Defensas contra la acumulación y la pérdida
La versión resuelve el orden. Para las otras dos formas de la contrapresión —acumulación y pérdida— las defensas son de diseño:
- Contra la acumulación: más capacidad (más workers en queue mode) si el cuello es n8n; o control de ritmo (límite de concurrencia) si el cuello es un servicio río abajo y prefieres una fila ordenada a una cascada. La cola de Redis, además, es en sí una defensa: absorbe las ráfagas. Una ráfaga que en modo regular tumbaría el proceso, en queue mode se queda esperando en Redis hasta que los workers la drenan. La cola es un amortiguador.
- Contra la pérdida: desacoplar "recibir" de "procesar". Si
order-triagerecibe el webhook y lo único que hace de inmediato es anotar el evento en una tabla (o encolarlo) y responder al proveedor, entonces el evento está a salvo aunque el procesamiento se atrase o falle: quedó guardado, y se puede procesar después. Lo que se pierde es lo que se recibe y se intenta procesar de corrido sin guardarlo primero. Recibir-y-guardar-rápido, procesar-después, es justo la forma del patrón outbox de la lección 6. La contrapresión es una de las razones por las que ese patrón existe.
Fíjate cómo las tres lecciones se enlazan: el ledger (M4) resuelve el orden con versiones; la cola de Redis absorbe las ráfagas; y el desacople recibir/procesar —que la lección 6 formaliza como outbox— evita la pérdida. Ninguna defensa es nueva; son las piezas del módulo aplicadas al problema del ritmo.
Errores comunes
Asumir que si los eventos se disparan en orden, se procesan en orden (conceptual). Qué pasa: alguien razona "el proveedor manda el pedido antes que la corrección, así que se procesan en ese orden" y no protege contra el desorden. Bajo carga, con varios workers, la corrección adelanta al pedido y el inventario queda mal, sin ningún error visible. Por qué pasa: en modo regular y con poca carga, el orden de disparo casi siempre coincide con el de procesamiento, así que la suposición parece cierta durante mucho tiempo. Cómo detectarlo: pregúntate "¿qué pasa si el evento 2 de este order_id se procesa antes que el evento 1?". Si la respuesta es "queda mal", tu orden no está protegido. Cómo corregirlo: no confíes el orden al motor; ponlo en los datos con un version y la verificación "aplica solo si es más nuevo". El orden de disparo no garantiza el orden de aplicación en cuanto hay paralelismo.
Agregar workers para ir más rápido y romper el orden sin darse cuenta (práctico). Qué pasa: bajo carga, alguien activa queue mode y sube a cuatro workers para drenar la fila, la latencia mejora, todos contentos —y a la semana aparecen inventarios inconsistentes que antes no había—. Por qué pasa: el paralelismo que ganó capacidad es el mismo que rompió el orden de los eventos que compartían clave, y la relación causa-efecto no es obvia porque el síntoma aparece después. Cómo detectarlo: si los problemas de orden empezaron justo cuando subiste la concurrencia o los workers, esa es la pista. Cómo corregirlo: antes de subir el paralelismo, protege el orden en la capa de datos (versión + verificación) para los eventos que comparten clave; la capacidad y el orden se resuelven en capas distintas, y subir una sin cuidar la otra es cambiar un problema por otro.
Confundir "más capacidad" con "menos ritmo" y aplicar la palanca equivocada (práctico). Qué pasa: el sistema se atrasa porque inventory-sync está tumbando al sistema de bodega con demasiadas peticiones simultáneas, y alguien, para "ir más rápido", agrega workers —lo que manda todavía más peticiones simultáneas a la bodega y la tumba más—. Por qué pasa: "está lento" se traduce por reflejo a "necesito más capacidad", sin diagnosticar dónde está el cuello. Cómo detectarlo: mira dónde se acumula la fila y qué está fallando; si el que sufre es un servicio río abajo (la bodega devuelve errores de saturación), el problema no es falta de capacidad tuya, es exceso de ritmo hacia él. Cómo corregirlo: cuando el cuello es un servicio frágil río abajo, la palanca es bajar la concurrencia (un límite), no subir workers; más workers solo ayuda cuando el cuello eres tú.
Procesar el webhook de corrido sin guardarlo primero, y perder eventos en una ráfaga (conceptual). Qué pasa: order-triage recibe el webhook y lo procesa entero —crédito, inventario, CRM— antes de responder al proveedor; en una ráfaga, algunas ejecuciones fallan por saturación y esos pedidos se pierden, porque nunca se guardaron en ningún lado. Por qué pasa: es lo más natural —recibir y procesar en un solo flujo— y en volumen bajo no falla nunca. Cómo detectarlo: pregúntate "si el procesamiento de este webhook falla, ¿queda registro de que el evento llegó?". Si la respuesta es no, un fallo bajo carga es un evento perdido. Cómo corregirlo: separa recibir de procesar —al recibir, anota el evento en una tabla y responde rápido; procesa después desde esa tabla—; así un fallo de procesamiento es recuperable porque el evento quedó guardado. Es la puerta de entrada al patrón outbox de la lección 6.
Ejercicios
Ejercicio 1 — Identifica la forma de la contrapresión. Para cada síntoma, di cuál de las tres formas es —acumulación, pérdida de orden o pérdida de eventos— y una defensa apropiada:
(a) En Buen Fin, un pedido que entró a las 12:00 no aparece en el CRM hasta las 12:09, pero al final aparece correcto. (b) Un cliente corrigió su pedido y el inventario quedó descontado según la versión vieja, aunque los dos eventos llegaron. (c) Después de una ráfaga enorme, el equipo nota que faltan tres pedidos que el proveedor confirma haber enviado y que no dejaron ningún rastro en el sistema.
Ver solución
(a) Acumulación. El pedido se procesó bien, solo tarde: la fila creció y drenó despacio. La defensa es capacidad —más workers en queue mode— si el cuello es n8n, o aceptar la latencia si la carga es un pico puntual y la cola de Redis la absorbe sin perder nada.
(b) Pérdida de orden. Los dos eventos llegaron pero se aplicaron mal: quedó la versión vieja. La defensa es la capa de datos: un version por evento y la verificación "aplica solo si es más nuevo", que descarta el evento viejo aunque llegue después.
(c) Pérdida de eventos. Tres pedidos que llegaron y no dejaron rastro: se intentaron procesar de corrido, fallaron bajo carga, y como nunca se guardaron, desaparecieron. La defensa es desacoplar recibir de procesar —anotar el evento al recibirlo, antes de procesarlo— para que un fallo sea recuperable. Es el outbox de la lección 6.
Por qué funciona: las tres formas tienen defensas distintas —capacidad, versión, desacople— y confundirlas lleva a aplicar la palanca equivocada. Ponerle nombre a la forma es lo que te dice qué defensa toca.
Ejercicio 2 — Traza el desorden con y sin versión. Dos eventos del pedido ORD-3300 llegan bajo carga y caen en workers distintos: v1 (crear pedido, 20 unidades) y v2 (cancelar pedido). Por el paralelismo, v2 (cancelar) se procesa antes que v1 (crear). Traza qué pasa sin versión y con versión + verificación, y di cuál es el estado final correcto.
Ver solución
El estado final correcto es: el pedido cancelado (v2 es lo más nuevo, gana).
Sin versión:
- Se procesa v2 (cancelar) primero. Pero el pedido todavía no existe —v1 no ha corrido—, así que cancelar quizás falle, o marque como cancelado algo que no está, o no haga nada.
- Después se procesa v1 (crear): crea el pedido con 20 unidades, activo.
- Estado final: pedido activo con 20 unidades. Incorrecto —debía quedar cancelado—. El cliente que canceló recibe su pedido igual.
Con versión + verificación:
- Se procesa v2 (cancelar). El ledger consulta
last_versionparaORD-3300: vacío. Como2 > 0, aplica la cancelación y ponelast_version = 2. - Se procesa v1 (crear). El ledger consulta
last_version: ya es2. Como1 > 2es falso, descarta v1 por viejo. No crea nada. - Estado final: pedido cancelado,
last_version = 2. Correcto, a pesar del desorden.
(Un matiz: para que "cancelar antes de crear" quede bien representado, el ledger guarda el estado más nuevo por versión, no ejecuta a ciegas; la verificación por versión es la que garantiza que el estado final refleje el evento más nuevo, sin importar el orden de llegada.)
Por qué funciona: mostraste que sin versión el desorden produce un estado incorrecto y con versión produce el correcto, que es toda la tesis de la lección: el orden se garantiza en los datos, no en el motor. El caso de "cancelar antes de crear" es más agudo que el de cantidades porque el daño —entregar un pedido cancelado— es más visible.
Ejercicio 3 — Diagnostica el cuello de botella. El sistema de Cumbre se atrasa mucho bajo carga. Tienes dos observaciones: (1) la cola de Redis crece y no se drena; (2) el sistema de bodega que usa inventory-sync devuelve muchos errores de "demasiadas peticiones". Con esas dos pistas, ¿cuál es el cuello de botella, y por qué agregar más workers empeoraría las cosas? ¿Qué palanca es la correcta?
Ver solución
El cuello de botella es el sistema de bodega río abajo, no la capacidad de n8n. La pista está en la observación (2): la bodega devuelve errores de saturación, lo que significa que ya le está llegando más de lo que aguanta. La cola de Redis crece (observación 1) no porque n8n no tenga manos, sino porque el trabajo se atasca esperando a una bodega que rechaza peticiones y obliga a reintentar, lo que a su vez mete más presión.
Por qué agregar workers empeoraría: más workers significa más ejecuciones de inventory-sync en paralelo, y por lo tanto más peticiones simultáneas a la bodega, que ya está saturada. Es echarle más presión al que ya no da abasto: la bodega devolvería aún más errores, habría aún más reintentos, y la cola crecería más rápido. La intuición "está lento, agrego capacidad" es exactamente la trampa aquí.
La palanca correcta es bajar el ritmo hacia la bodega: un límite de concurrencia que garantice que a inventory-sync —y por lo tanto a la bodega— no le lleguen más de N peticiones a la vez, dentro de lo que la bodega aguanta. Eso convierte la ráfaga en una fila ordenada que la bodega puede procesar sin caerse. Sacrificas velocidad instantánea a cambio de que el trabajo de verdad avance en vez de rebotar. Es la defensa contra la cascada de la lección 1, aplicada con la palanca de control de ritmo del queue mode.
Por qué funciona: diagnosticaste el cuello mirando dónde fallan las cosas (la bodega, no n8n) en vez de asumir que "lento = falta capacidad", y elegiste la palanca opuesta a la intuición —bajar el ritmo, no subir la capacidad— porque el cuello está río abajo. Ese diagnóstico es la habilidad central de operar bajo contrapresión.
Resumen y siguiente paso
En esta lección enfrentaste la contrapresión: lo que pasa cuando entra más de lo que sale. Se manifiesta de tres formas —acumulación (la fila crece), pérdida de orden (eventos que se aplican al revés) y pérdida (eventos que desaparecen)— y cada una tiene su defensa. El queue mode de n8n —proceso principal que encola, Redis como cola, workers que procesan— da dos cosas distintas: capacidad (más workers, contra la acumulación) y control de ritmo (límites de concurrencia como N8N_CONCURRENCY_PRODUCTION_LIMIT y --concurrency, contra la cascada hacia un servicio frágil). Y viste la sorpresa clave: correr en paralelo no conserva el orden, porque los workers procesan a ritmos distintos; el motor te da capacidad, no orden. El orden, cuando importa, se garantiza en la capa de datos —con un version por evento y la verificación "aplica solo si es más nuevo" (control de concurrencia optimista), o serializando por clave en los casos incrementales—. Y contra la pérdida, desacoplar recibir de procesar, que es la puerta del outbox.
Antes de avanzar a la lección 6 deberías poder: definir contrapresión y sus tres formas; explicar qué son la capacidad y el control de ritmo del queue mode y cuándo aplicar cada palanca; y explicar por qué el paralelismo rompe el orden y cómo un campo version en el ledger lo arregla sin sacrificar paralelismo.
La lección 6 es la bisagra del módulo. Vas a ver el patrón outbox, la técnica que separa "decidir el efecto" de "ejecutar el efecto": el workflow anota la intención del efecto en una tabla outbox —en la misma operación que registra su decisión— y otro flujo la lee y la ejecuta de forma idempotente. Es lo que hace que un fallo entre pasos no duplique ni pierda un efecto en cascada, es la forma robusta de desacoplar recibir de procesar que asomó en esta lección, y es la pieza que faltaba para coordinar varios workflows con seguridad de verdad. Todo lo que viste hasta ahora —idempotencia, ledger, grafo, fan-out, orden— converge ahí.
Recursos
- Scaling n8n / Queue mode — n8n Docs — qué es el queue mode, sus piezas (principal, Redis, workers) y cómo se activa con
EXECUTIONS_MODE=queue. El montaje a fondo es de la guía de producción; aquí basta el concepto. - Concurrency control — n8n Docs — el control de ritmo:
N8N_CONCURRENCY_PRODUCTION_LIMITpara el límite de ejecuciones concurrentes y la opción--concurrencypor worker en queue mode. Verifica los nombres exactos en tu versión. - Queue mode environment variables — n8n Docs — las variables que configuran la cola y los workers, para cuando pases al montaje real (guía de producción).
- Postgres node — n8n Docs — el nodo con el que implementas la verificación por versión contra el ledger: leer
last_versiony actualizarla condicionalmente. - Webhook node — n8n Docs — el punto de entrada donde nace la contrapresión y donde conviene recibir-y-guardar-rápido en vez de procesar de corrido.