Módulo 4: El modelo de datos del sistema

4. Estrategias de deduplicación

Descripción

Al terminar esta lección vas a poder elegir, con criterio, entre las tres estrategias de deduplicación que existen en la práctica: la ventana de tiempo, la clave-ya-vista y el nodo Remove Duplicates que n8n trae de fábrica. Vas a saber qué hace cada una, cuándo es la correcta y —lo más importante— dónde se queda corta cada una. Vas a entender por qué el nodo Remove Duplicates, que parece resolver todo el problema con un solo clic, tiene dos modos distintos con capacidades muy diferentes, y por qué ninguno de los dos es la base adecuada para la verdad de un sistema idempotente, aunque los dos sean útiles para otras cosas.

Esto importa porque la deduplicación no es una sola técnica, es una familia, y usar la del caso equivocado es una fuente clásica de bugs sutiles. Una ventana de tiempo que deja pasar el duplicado que llegó tarde. Un nodo que deduplica dentro de una ejecución pero no entre ejecuciones. Un almacén interno que "olvida" claves viejas justo cuando las necesitabas. Cada una de esas fallas nace de aplicar una estrategia fuera de su terreno. Esta lección te da el mapa de terrenos para que la tienda de dedup que construyes en la lección 5 sea una elección informada, no la primera que encontraste.

Conexión con el módulo: la lección 3 te dio el ledger, que registra. Esta lección es el puente hacia la tienda de dedup de la lección 5, que decide. Antes de construir esa tienda con una tabla y ON CONFLICT, conviene entender qué otras opciones había y por qué esta es la que sostiene la idempotencia entre ejecuciones. Vas a ver que el nodo Remove Duplicates resuelve casos parecidos —y para muchos flujos es perfecto— pero que, como fundamento de la correctitud de un sistema, arrastra los mismos problemas que la lección 2 le señaló a Static Data: es un almacén administrado por n8n, acoplado al workflow, acotado en tamaño y que no puedes consultar ni auditar. La lección 5 elige una tabla propia justamente para no heredar esos problemas.

Tres formas de llevar la lista en la puerta

Piensa en el portero de un evento con una lista. Su trabajo es dejar pasar a cada persona una sola vez. Tiene tres maneras de organizarse, y cada una tiene sentido en un tipo de evento distinto.

La primera: el portero recuerda las caras de los últimos minutos. Si alguien intenta colarse dos veces seguidas, lo reconoce y lo detiene. Pero si esa persona vuelve tres horas después, ya no la recuerda —solo retiene lo reciente—. Esta es la ventana de tiempo: deduplicas lo que aparece dentro de un lapso corto, y olvidas lo viejo.

La segunda: el portero marca con tinta invisible la mano de cada persona que entra, y en la puerta hay una lámpara que revela la marca. No importa si vuelves en cinco minutos o al día siguiente: si tu mano ya tiene la marca, no entras. La lista de marcados no se olvida. Esta es la clave-ya-vista: guardas de forma persistente cada clave que procesaste y la consultas para siempre (o por mucho tiempo). Es la estrategia robusta, y es la que exige un almacén serio.

La tercera: el portero usa un contador automático que la empresa del evento le prestó. Viene en una caja, hace su trabajo, y el portero no ve por dentro cómo funciona ni puede cambiarlo. Le sirve, pero está atado a las decisiones que tomó quien fabricó la caja: cuánta memoria tiene, qué considera "la misma persona", cuándo olvida. Este es el nodo Remove Duplicates: una solución empaquetada de n8n, cómoda, con la contrapartida de que su comportamiento lo definió n8n, no tú.

Las tres deduplican. La pregunta no es "¿cuál es la buena?", sino "¿cuál corresponde a este evento?". Un portero que solo recuerda caras recientes sería un desastre en un evento de varios días, y uno que marca la mano para siempre sería un exceso para una fila que rota cada diez minutos. La estrategia correcta depende del evento, no de cuál suene más sofisticada. Vamos una por una.

Estrategia 1: la ventana de tiempo

La idea es directa: consideras duplicado a un evento solo si llegó dentro de un lapso reciente respecto de uno igual. "Si ya vi este order_id en los últimos diez minutos, es duplicado; si lo veo por primera vez en diez minutos, lo trato como nuevo."

Anatomía. Necesitas guardar, para cada clave, cuándo la viste por última vez. Antes de actuar, comparas: ¿la última vez que vi esta clave fue hace menos de N minutos? Si sí, descarta. Si no (o si nunca la vi), procesa y actualiza la marca de tiempo. Un detalle que conviene notar desde ya: incluso la ventana de tiempo necesita guardar algo entre ejecuciones —al menos la clave y su última marca de tiempo—, así que tampoco escapa de necesitar un almacén persistente. No es "la opción sin base de datos"; es "la opción que además olvida lo viejo". La diferencia con la clave-ya-vista no es tener o no tener almacén, sino qué haces con las entradas viejas: la ventana las deja caducar, la clave-ya-vista las conserva.

Cuándo es la correcta. La ventana de tiempo brilla contra el caso más común de duplicado: el reintento casi inmediato. El sistema que llama a tu webhook no recibió tu respuesta a tiempo, así que reintenta a los pocos segundos o minutos. Los dos disparos llegan pegados. Una ventana de, digamos, quince minutos los atrapa sin esfuerzo, y tiene una ventaja real: no necesitas recordar la clave para siempre. Puedes limpiar las marcas viejas, así que el almacén no crece sin límite. Para flujos de altísimo volumen donde recordar cada clave de la historia sería caro, la ventana es un balance sensato.

Dónde se queda corta. El problema es el duplicado que llega fuera de la ventana. Imagina que el pedido ORD-2041 se procesó hoy, y por un reproceso manual, un respaldo que se reenvió o un error aguas arriba, el mismo pedido vuelve a entrar tres días después. Si tu ventana es de quince minutos, ese duplicado tardío pasa como nuevo, y cobras de nuevo. La ventana asume que los duplicados llegan juntos, y esa suposición es cierta para los reintentos automáticos y falsa para casi todo lo demás.

Hay además una decisión incómoda: ¿de qué tamaño haces la ventana? Muy corta, y se te escapan duplicados que llegaron con retraso. Muy larga, y el almacén crece tanto que ya no tienes la ventaja de poder olvidar —te acercas a la clave-ya-vista, pero con la fragilidad extra de una comparación de tiempos—. No hay un tamaño correcto universal; depende de qué tan separados llegan tus duplicados, que muchas veces no sabes de antemano.

La regla: la ventana de tiempo es buena cuando sabes que tus duplicados llegan juntos y necesitas no acumular claves para siempre. Es insuficiente cuando un duplicado puede llegar tarde y el efecto es costoso —como un cobro—.

Estrategia 2: la clave-ya-vista

Esta es la estrategia robusta, y es la que la lección 5 convierte en una tabla. La idea: guardas cada clave que procesaste, sin caducidad (o con una caducidad muy larga y deliberada), y antes de actuar preguntas "¿esta clave ya está en mi lista de vistas?". Si está, es duplicado, sin importar cuánto tiempo pasó. Es la mano marcada con tinta invisible: la marca no se borra sola.

Anatomía. Un almacén persistente —una tabla— con una columna para la clave, marcada como única. Antes de actuar, consultas o intentas insertar. Si la clave ya existía, descarta; si es nueva, insértala y procede. La lección 5 muestra cómo hacer esa consulta-e-inserción en un solo paso atómico con ON CONFLICT; por ahora quédate con la forma: recordar cada clave, para siempre, y consultar antes de actuar.

Cuándo es la correcta. Siempre que el efecto sea costoso y un duplicado tardío sea posible. Que es, exactamente, el caso de order-triage: crear un cobro es costoso, y un pedido podría reingresar días después por mil razones. Contra ese escenario, la clave-ya-vista es la única de las tres que no falla, porque no hace ninguna suposición sobre cuándo llega el duplicado. La marca está o no está; el tiempo no entra en la decisión.

Dónde se queda corta. Tiene dos costos honestos. Uno: el almacén crece. Si recuerdas cada clave para siempre, la tabla acumula una fila por cada trabajo único que hiciste en la vida del sistema. Para la mayoría de los sistemas esto no es problema —una tabla con una columna indexada maneja millones de filas sin despeinarse, y consultarla por clave es instantáneo—, pero es un costo real que a volúmenes enormes hay que gestionar (por ejemplo, archivando claves muy viejas que ya no pueden reingresar). Dos: necesitas un almacén de verdad, con unicidad y atomicidad, que es justo lo que Static Data no te daba. En otras palabras, la clave-ya-vista es más robusta pero te obliga a tener una base de datos. Ese "costo" es precisamente lo que este módulo asume desde el principio.

La regla: la clave-ya-vista es la estrategia por defecto para efectos costosos e irreversibles. No supone nada sobre el tiempo, así que atrapa el duplicado de hace cinco minutos y el de hace cinco días por igual. Su precio es una tabla que crece, y ese precio casi siempre vale la pena.

Estrategia 3: el nodo Remove Duplicates, con sus dos caras

n8n trae un nodo llamado Remove Duplicates que parece resolver todo esto con un clic. Vale la pena entenderlo bien, porque es útil, pero es fácil creer que hace más —o menos— de lo que hace. La clave es que tiene varios modos, y hacen cosas muy distintas. Al momento de escribir esta guía, la documentación describe estas operaciones; conviene que confirmes las de tu versión en el propio nodo:

Modo A — "Remove Items Repeated Within Current Input". Elimina duplicados dentro del lote de items de una sola ejecución. Si en una misma corrida llegan cincuenta items y tres tienen el mismo order_id, este modo deja uno y descarta los otros dos.

Este modo tiene un límite tajante, y es el más malentendido del nodo: solo mira los items de la ejecución actual. No sabe nada de ejecuciones anteriores. Para el caso central del módulo —el webhook que dispara dos veces en dos ejecuciones distintas— este modo no sirve, porque cada disparo es una ejecución separada con su propio lote, y el modo A nunca ve los dos juntos. Es un error clásico: alguien pone un Remove Duplicates esperando que atrape el doble disparo, y no atrapa nada, porque los dos disparos nunca coinciden en el mismo lote.

Modo B — "Remove Items Processed in Previous Executions". Este sí compara contra ejecuciones anteriores. n8n mantiene, por dentro, un almacén de deduplicación propio que recuerda las claves ya vistas, y este modo consulta ahí. Tiene un parámetro History Size (por defecto 10,000, según la documentación al escribir esta guía) que limita cuántas claves recuerda, y un alcance que puede ser de nodo o de workflow. Es, en el fondo, una implementación empaquetada de la clave-ya-vista.

Ejemplo trabajado: los dos modos frente al doble disparo

Pongamos los dos modos frente a nuestro caso para ver la diferencia.

Con el modo A, el workflow sería algo como:

Webhook  ──►  Remove Duplicates (Within Current Input, campo: order_id)  ──►  crear cobro

Qué esperar. El primer disparo entra con ORD-2041 (un item), pasa el nodo —no hay nada que deduplicar dentro de un solo item—, y crea el cobro. El segundo disparo, minutos después, entra con ORD-2041 en otra ejecución, pasa el nodo igual —de nuevo, un solo item en su lote, nada que deduplicar—, y crea un segundo cobro. El nodo no falló; simplemente nunca vio los dos disparos juntos. El modo A es ciego entre ejecuciones.

Con el modo B, el nodo sí recordaría ORD-2041 del primer disparo y descartaría el segundo. Funcionaría. Entonces, ¿por qué no terminamos aquí el módulo y usamos el modo B?

Porque el modo B, aunque resuelve la mecánica, es el mismo tipo de almacén que la lección 2 nos enseñó a desconfiar como fuente de verdad del sistema. Fíjate en los paralelos:

  • Es una caja negra que no puedes consultar ni auditar. El almacén de dedup de n8n no es una tabla que puedas abrir con un SELECT para responder "¿qué claves recuerda ahora mismo?" o "¿cuándo vio ORD-2041?". Cuando algo salga raro, no tienes dónde mirar. El ledger de la lección 3 existe justamente para poder mirar.
  • Está acotado en tamaño. History Size por defecto 10,000: cuando entra la clave número 10,001, la más vieja se cae. Si un duplicado tardío usa una clave que ya se salió de la historia, pasa como nuevo. Tú no controlas ese olvido con la precisión que un cobro exige.
  • Está acoplado al nodo o al workflow. Igual que Static Data, ese almacén vive pegado a la instancia de n8n y su alcance es el nodo o el workflow. Otro flujo que necesite saber "¿este pedido ya se cobró?" no puede consultarlo. Y al mover o reimportar el workflow, la continuidad de ese estado no está bajo tu control.
  • Deduplica descartando items, no ramificando. El nodo quita los duplicados del flujo. Eso está bien para "limpiar una lista", pero para un sistema idempotente muchas veces quieres saber que algo fue duplicado y hacer algo con esa información —registrarlo en el ledger, responder al webhook con "ya procesado", alertar si se repite demasiado—. Una tabla propia con ON CONFLICT te da esa bifurcación explícita "primera vez / duplicado"; el nodo, por diseño, solo hace desaparecer al duplicado.

Nada de esto significa que Remove Duplicates sea malo. Para deduplicar un feed, una lista importada, o eventos de bajo riesgo donde recordar 10,000 claves recientes basta y no necesitas auditar, el modo B es cómodo y correcto. Es la misma lógica de la lección 2 con Static Data: la herramienta empaquetada tiene su lugar, y ese lugar no es la verdad de la que depende que no cobres dos veces.

La regla: el modo A (dentro del input) sirve para limpiar duplicados de un mismo lote, y es ciego entre ejecuciones. El modo B (ejecuciones previas) sí cruza ejecuciones, pero como caja negra acotada y acoplada; úsalo para dedup de bajo riesgo, no como fuente de verdad del sistema. Para la idempotencia de un efecto costoso, la clave-ya-vista en una tabla tuya te da control, auditoría y atomicidad que el nodo no.

Cómo elegir: la tabla de decisión

Con las tres sobre la mesa, la elección se resuelve con pocas preguntas.

Tu situaciónEstrategia
Duplicados dentro de un mismo lote de una ejecución (una lista con repetidos)Remove Duplicates, modo A (dentro del input)
Reintentos automáticos que llegan juntos, y puedes olvidar lo viejoVentana de tiempo
Efecto costoso o irreversible (un cobro), duplicado puede llegar tardeClave-ya-vista en una tabla propia (lección 5)
Dedup de bajo riesgo entre ejecuciones, sin necesidad de auditar, volumen moderadoRemove Duplicates, modo B (ejecuciones previas)
Necesitas consultar, auditar o ramificar según "primera vez / duplicado"Tabla propia (Remove Duplicates no te lo da)

Y la regla de una línea que resume el módulo:

Para limpiar una lista, usa el nodo. Para la verdad de la que depende un efecto costoso, usa tu propia tabla.

Fíjate en que la tabla propia gana justo en las filas donde el error cuesta dinero o donde necesitas ver qué pasó. No es que sea "mejor" en abstracto; es que da control, auditoría y atomicidad, y esas tres cosas son exactamente lo que un sistema idempotente serio necesita y una caja negra no ofrece.

Dos matices que evitan bugs: combinar estrategias y el falso duplicado

Dos aclaraciones antes de construir la tabla, porque las dos son fuente de errores reales.

Las estrategias no son excluyentes; se combinan. Puede sonar a que tienes que elegir una y descartar el resto, y no es así. En order-triage vas a terminar usando dos a la vez: el run ledger de la lección 3, que registra la historia rica de cada ejecución para auditar y recuperar, y la tienda de dedup de la lección 5, que decide el sí/no atómico antes de actuar. El ledger no deduplica; la tienda no guarda la historia completa. Juntos cubren las dos necesidades. E incluso podrías sumar una ventana de tiempo encima de la clave-ya-vista para un propósito distinto —por ejemplo, permitir que un pedido legítimamente repetido (una recompra real del mismo cliente) se procese si llega mucho tiempo después, combinando la clave con un componente temporal—. La pregunta no es "¿cuál de las tres?", sino "¿qué necesito responder, y qué estrategia responde cada parte?".

El falso duplicado: deduplicar de más también es un bug. Toda esta lección se preocupa por el duplicado que se te escapa. Hay un error simétrico y menos obvio: descartar como duplicado algo que en realidad era nuevo y legítimo. Y su origen casi siempre es el mismo: elegiste mal la clave. Piénsalo con un caso de Cumbre. Si tu idempotency_key fuera solo el nombre del cliente, entonces dos pedidos distintos del mismo cliente —Luna Coffee compró el lunes y volvió a comprar el jueves— tendrían la misma clave, y tu sistema descartaría la segunda compra como si fuera un duplicado. No cobraste de más; cobraste de menos, y perdiste una venta real, en silencio. Es el reflejo exacto del cobro duplicado, y suele ser más difícil de detectar porque "no pasó nada" se ve como éxito.

La lección aquí conecta directo con el módulo 2: la calidad de tu deduplicación es la calidad de tu clave. Una buena idempotency_key identifica el trabajo exacto —este pedido, con este contenido— de modo que dos disparos del mismo trabajo compartan clave y dos trabajos genuinamente distintos tengan claves distintas. Por eso en la lección 3 la clave sintética combinaba order_id con el total: para que un mismo pedido produzca siempre la misma clave, pero un pedido distinto no colisione con él. Cuando la tienda de dedup de la lección 5 descarte algo, va a descartar exactamente lo que su clave dice que es duplicado —ni más ni menos—. Elegir esa clave con cuidado es lo que evita los dos errores a la vez: el duplicado que se escapa y el nuevo que se descarta.

Errores comunes

Poner un Remove Duplicates esperando que atrape el doble disparo (práctico). Qué pasa: alguien agrega el nodo en modo "Within Current Input" convencido de que va a frenar el webhook que dispara dos veces, y siguen apareciendo cobros duplicados. Por qué pasa: ese modo solo ve el lote de la ejecución actual, y los dos disparos son dos ejecuciones distintas que nunca comparten lote. Cómo detectarlo: si tu Remove Duplicates está en modo "within input" y esperas que cruce ejecuciones, ahí está el error. Cómo corregirlo: para cruzar ejecuciones necesitas el modo B (con sus límites) o, para un efecto costoso, la tabla propia de la lección 5.

Elegir una ventana de tiempo para un efecto costoso (conceptual). Qué pasa: se deduplica con una ventana de quince minutos, funciona en todas las pruebas, y meses después un reproceso manual reingresa un pedido viejo y lo cobra de nuevo. Por qué pasa: la ventana asume que los duplicados llegan juntos, y ese reingreso llegó días tarde. Cómo detectarlo: si tu deduplicación "olvida" claves pasado un tiempo y el efecto es un cobro, tienes esta bomba puesta. Cómo corregirlo: para efectos costosos usa clave-ya-vista, que no supone nada sobre el tiempo. La ventana es para reintentos automáticos de bajo riesgo, no para dinero.

Confiar en el History Size sin pensar en su borde (práctico). Qué pasa: se usa el modo B de Remove Duplicates y todo va bien hasta que, con el volumen, las claves viejas empiezan a caerse del almacén de 10,000, y un duplicado tardío cuya clave ya se salió pasa como nuevo. Por qué pasa: el almacén es acotado y olvida lo más viejo cuando se llena. Cómo detectarlo: si tu volumen de claves únicas supera con holgura el History Size y aún así dependes de él para no duplicar, es esto. Cómo corregirlo: para volumen alto y efectos costosos, una tabla propia sin límite artificial (o con archivado deliberado) es más segura, y además la puedes consultar.

Tratar la deduplicación como una sola técnica (conceptual). Qué pasa: alguien aprende una estrategia —la que sea— y la aplica a todo. Por qué pasa: "deduplicar" suena a una sola cosa. Cómo detectarlo: si usas la misma técnica para limpiar una lista importada y para no duplicar un cobro, probablemente una de las dos está mal servida. Cómo corregirlo: reconoce que son una familia; la pregunta correcta es "¿qué terreno es este?" (lote único, reintento junto, efecto costoso con duplicado tardío) y elige la que corresponde.

Deduplicar descartando cuando necesitabas ramificar (conceptual). Qué pasa: se usa Remove Duplicates, el duplicado desaparece, y después no hay forma de saber que existió ni de responderle al sistema que lo mandó. Por qué pasa: el nodo, por diseño, hace desaparecer al duplicado. Cómo detectarlo: si necesitas registrar el duplicado, responder "ya procesado" o alertar cuando algo se repite mucho, y tu herramienta solo lo borra, es esto. Cómo corregirlo: una tabla con ON CONFLICT te da la bifurcación explícita "primera vez / duplicado" y te deja hacer algo distinto en cada rama.

Ejercicios

Ejercicio 1 — Asigna la estrategia. Para cada caso, di qué estrategia usarías y por qué en una frase.

(a) Importas un CSV de 2,000 filas de pedidos y algunas están repetidas dentro del archivo. (b) Un webhook de pagos reintenta automáticamente a los 30 segundos si no respondes, y crear el cobro es costoso. (c) Un pedido puede reingresar por un reproceso manual semanas después, y no debe cobrarse dos veces. (d) Un flujo interno de bajo riesgo que refresca una lista de contactos y no quiere reprocesar los ya vistos en las últimas ejecuciones, sin necesidad de auditar nada.

Ver solución

(a) Remove Duplicates, modo A (within input). Los repetidos están dentro de un mismo lote de una ejecución; es exactamente su caso, y no necesitas cruzar ejecuciones.

(b) Clave-ya-vista en tabla propia (o, como mínimo defendible, una ventana de tiempo). El reintento llega junto, así que una ventana atraparía el caso; pero como el efecto es costoso, conviene la clave-ya-vista, que además cubre el duplicado tardío inesperado. Ante dinero, la opción robusta.

(c) Clave-ya-vista en tabla propia, sin duda. El duplicado llega semanas después: cualquier ventana de tiempo lo dejaría pasar, y el History Size del nodo podría haberlo olvidado. Solo recordar la clave sin caducidad garantiza no cobrar dos veces.

(d) Remove Duplicates, modo B (previous executions). Cruza ejecuciones, el riesgo es bajo, no necesitas auditar y el volumen cabe en el History Size. Es su caso ideal: cómodo y suficiente.

Por qué funciona: fíjate en que el factor decisivo no es "qué tan repetido" sino cuándo llega el duplicado y cuánto cuesta equivocarse. Lote único → modo A. Junto y barato → ventana o modo B. Tarde o caro → tabla propia. Ese es todo el criterio.

Ejercicio 2 — Rompe la ventana. Un compañero implementó dedup con una ventana de diez minutos para order-triage y dice "lleva un mes sin duplicar, está resuelto". Describe un escenario concreto y realista en el que su solución cobraría dos veces, y explica por qué la clave-ya-vista no tendría ese problema.

Ver solución

Un escenario realista: el CRM de Cumbre tuvo una caída ayer, y varios pedidos no se procesaron bien. Hoy, alguien del equipo reenvía el lote del día anterior para reprocesar los que quedaron pendientes. Entre ellos va ORD-2041, que se había cobrado ayer antes de la caída. Ese reenvío llega más de diez minutos —de hecho, más de un día— después del disparo original. La ventana de diez minutos ya olvidó a ORD-2041, así que lo trata como nuevo y crea un segundo cobro.

Por qué la clave-ya-vista no falla ahí: la clave ORD-2041 quedó guardada en la tabla desde ayer, sin caducidad. Cuando el reenvío llega hoy, la consulta encuentra la clave y descarta el pedido, sin importar que hayan pasado 24 horas. La clave-ya-vista no supone nada sobre el tiempo; la ventana supone que los duplicados llegan juntos, y este no llegó junto.

Por qué funciona: el ejercicio muestra que "un mes sin duplicar" no prueba que la solución sea correcta, solo que no llegó todavía el duplicado tardío. Los bugs de las ventanas de tiempo son silenciosos hasta que el escenario correcto —un reproceso, un respaldo, una caída aguas arriba— los despierta.

Ejercicio 3 — Justifica la tabla propia. Tu equipo pregunta: "el nodo Remove Duplicates en modo B ya cruza ejecuciones, ¿para qué montar una tabla en Postgres?". Escribe la respuesta que darías, cubriendo al menos tres razones por las que, para un efecto costoso, la tabla propia es preferible.

Ver solución

Una versión de referencia:

El modo B funciona para muchos casos, y si esto fuera un flujo de bajo riesgo lo usaría sin dudar. Para un cobro, prefiero una tabla propia por tres razones concretas.

Primera, la puedo consultar y auditar. Si un cliente reclama un cobro duplicado, con una tabla hago SELECT * FROM processed_orders WHERE order_id = 'ORD-2041' y veo exactamente cuándo se registró. El almacén interno del nodo es una caja negra: no tengo dónde mirar cuando algo sale raro.

Segunda, controlo el tamaño y la caducidad. El modo B tiene un History Size acotado (10,000 por defecto), y cuando se llena olvida las claves más viejas. Un duplicado tardío cuya clave ya se salió pasaría como nuevo. En mi tabla, decido yo qué recuerdo y por cuánto tiempo; no hay un olvido automático que no controlo.

Tercera, puedo ramificar en vez de solo descartar. Con ON CONFLICT sé si fue la primera vez o un duplicado, y puedo hacer algo distinto en cada caso: registrar el duplicado en el ledger, responder al webhook con "ya procesado", o alertar si un pedido se repite demasiado. El nodo solo hace desaparecer al duplicado; pierdo esa información.

Y de fondo: la tabla es mía, vive separada del workflow, y la ven todos los flujos que la necesiten. Para la verdad de la que depende no cobrar dos veces, quiero ese control, no una caja prestada.

Por qué funciona: la respuesta no descalifica al nodo —reconoce dónde es la opción correcta— y sube el argumento a lo que de verdad importa para un efecto costoso: auditoría, control del olvido y capacidad de ramificar. Son las tres cosas que separan una herramienta cómoda de una fuente de verdad.

Resumen y siguiente paso

En esta lección viste que la deduplicación es una familia de tres estrategias, no una sola técnica. La ventana de tiempo deduplica lo que llega junto y olvida lo viejo: perfecta contra reintentos automáticos, insuficiente contra el duplicado tardío. La clave-ya-vista recuerda cada clave sin caducidad y no supone nada sobre el tiempo: es la estrategia robusta para efectos costosos, y su precio es una tabla que crece. Y el nodo Remove Duplicates tiene dos caras: el modo A limpia duplicados dentro de un mismo lote pero es ciego entre ejecuciones, y el modo B sí cruza ejecuciones pero como una caja negra acotada y acoplada, útil para dedup de bajo riesgo y no como fuente de verdad.

La conclusión que abre la lección 5: para la idempotencia de un efecto costoso e irreversible como un cobro, la estrategia correcta es la clave-ya-vista en una tabla propia, porque te da control sobre el olvido, capacidad de auditar y la posibilidad de ramificar entre "primera vez" y "duplicado" —tres cosas que ninguna de las alternativas empaquetadas ofrece a la vez—.

Antes de avanzar deberías poder: nombrar las tres estrategias y su terreno; explicar por qué el modo A del nodo no atrapa el doble disparo; y dar una razón por la que una tabla propia supera al modo B para un cobro.

La lección 5 construye esa tabla propia y le da su superpoder: INSERT ... ON CONFLICT DO NOTHING, el patrón que convierte "consultar si existe" y "registrar que ahora existe" en un solo paso atómico e indivisible. Ese paso es el que cierra, por fin, la trampa de "verificar y luego actuar" que el módulo 2 dejó abierta y que ni Static Data ni la ventana de tiempo podían resolver. Ahí la clave-ya-vista deja de ser una idea y se vuelve un mecanismo a prueba de la carrera del doble disparo.

Recursos

  • Remove Duplicates node — n8n Docs — las operaciones del nodo: "Remove Items Repeated Within Current Input" (modo A), "Remove Items Processed in Previous Executions" (modo B) y "Clear Deduplication History", más el parámetro History Size y el alcance de nodo o workflow. Confirma ahí las opciones de tu versión.
  • Postgres node — n8n Docs — el nodo con el que la lección 5 implementa la clave-ya-vista en una tabla propia, con la operación que consulta e inserta.
  • PostgreSQL — INSERT ... ON CONFLICT — el mecanismo atómico que la lección 5 usa para la clave-ya-vista; adelántate aquí si quieres ver la sintaxis oficial.
  • Understand n8n's data structure — n8n Docs — cómo fluyen los items dentro de una ejecución, útil para entender por qué el modo A del nodo solo ve el lote actual.