El Modelo Mental de Procesamiento por Lotes, en Streaming y Orquestación
Cada pipeline de datos, sin importar la herramienta que lo construya, responde a las mismas tres preguntas.
¿Cuántos datos procesa una ejecución de este pipeline: un fragmento delimitado o un flujo continuo e ilimitado?
¿Qué decide cuándo y en qué orden se ejecuta cada paso de este pipeline?
¿Y qué sucede la segunda vez que un paso se ejecuta sobre la misma entrada, ya sea por un reintento, un backfill o un mensaje reentregado?
Fundamentos de Ingeniería de Datos muestra la API concreta para construir trabajos por lotes y en streaming; esta página trata sobre los tres conceptos subyacentes a cada uno de ellos: lotes frente a streaming como una compensación, el DAG como un grafo de dependencias en lugar de un planificador, y la idempotencia como la propiedad que hace que la recuperación de fallos sea segura.
Resumen
- Un pipeline de datos se define por cómo delimita su entrada (por lotes vs. en streaming), cómo secuencia el trabajo dependiente (un DAG de tareas) y cuán seguro puede ser reejecutado (idempotencia); cada herramienta de orquestación simplemente automatiza estas tres preocupaciones.
- Por Qué Importa: Los pipelines fallan: los trabajos se caen, los trabajadores mueren, los mensajes se reentregán; y un pipeline que no es idempotente convierte cada fallo en un incidente de calidad de datos en lugar de un reintento rutinario.
- Conceptos Clave: por lotes vs. en streaming, DAG (grafo dirigido acíclico), orquestación vs. planificación, idempotencia, marca de agua (watermark), garantía de entrega.
- Cuándo Usar: Por lotes para informes delimitados y periódicos y backfills; en streaming para reacciones de baja latencia y basadas en eventos; un orquestador basado en DAG siempre que las tareas tengan dependencias reales entre sí, no solo una hora para ejecutarse.
- Limitaciones / Compensaciones: El streaming compra latencia a costa de complejidad de coordinación (marcas de agua, estado, ordenación); el procesamiento por lotes compra simplicidad y completitud a costa de datos obsoletos; la idempotencia debe diseñarse en cada sink, no es una configuración que se activa.
- Temas Relacionados: orquestación de flujos de trabajo (Airflow, Prefect, Dagster), procesamiento de flujos (Kafka, Faust), cómputo distribuido (PySpark), capas de transformación (dbt).
Fundamentos
Un pipeline por lotes procesa un conjunto de datos delimitado y de tamaño conocido - "los pedidos de ayer", "los archivos de registro de este mes" - y finaliza; volver a ejecutarlo más tarde procesa el siguiente fragmento delimitado.
Un pipeline en streaming procesa una secuencia ilimitada de eventos a medida que llegan, sin un "final" natural al que esperar, lo que significa que debe producir resultados útiles de forma continua en lugar de después de un pase completo sobre los datos.
Esta es una compensación genuina, no solo dos implementaciones de la misma idea: el procesamiento por lotes puede ver todos los datos antes de decidir una respuesta (un total de un día completo, una unión completa), mientras que el procesamiento en streaming tiene que responder solo con lo que ha llegado hasta el momento, aceptando que los datos que llegan tarde pueden necesitar ser reconciliados después.
Un DAG (grafo dirigido acíclico) es la segunda idea central: es un grafo de tareas y dependencias - "la tarea B necesita la salida de la tarea A" - sin ciclos, y fundamentalmente, un DAG define qué depende de qué, no cuándo se ejecutan las cosas.
Una lectura errónea común es pensar en un DAG como una secuencia fija, como una lista numerada de pasos; en realidad, un planificador es libre de ejecutar dos tareas cualesquiera sin dependencia entre ellas en paralelo, o en cualquier orden, siempre que las dependencias declaradas de cada tarea se completen primero.
La orquestación es la capa que convierte una definición de DAG en ejecución real: decide qué tareas están listas para ejecutarse (sus dependencias se satisfacen), reintenta las tareas fallidas y rastrea el estado de cada ejecución; esto es distinto de la planificación simple, que solo decide cuándo comienza un trabajo, sin noción de dependencia entre trabajos.
La idempotencia es la propiedad de que ejecutar una operación dos veces con la misma entrada produce el mismo resultado que ejecutarla una vez, y es la propiedad que hace que cada reintento, backfill y mensaje reentregado sea seguro en lugar de peligroso.
Mecánicas e Interacciones
La elección entre lotes y streaming y el requisito de idempotencia están más conectados de lo que parecen inicialmente: cualquier sistema de streaming que prometa entrega fiable bajo fallo tiene que reentregar mensajes a veces, y solo el procesamiento idempotente hace que esa reentrega sea inofensiva en lugar de duplicar datos.
Los sistemas de streaming en su mayoría ofrecen entrega al menos una vez por defecto: un mensaje puede ser procesado más de una vez después de un fallo y reinicio, porque reconocer "procesé esto" y "confirmar el resultado" no se puede hacer perfectamente atómico entre dos sistemas diferentes (el intermediario de mensajes y el sink de salida) sin coordinación adicional.
El procesamiento exactamente una vez, cuando se afirma, generalmente se logra combinando la entrega al menos una vez con un sink idempotente; por ejemplo, escribir resultados con clave por un ID de mensaje para que una escritura duplicada simplemente sobrescriba la misma fila en lugar de agregar una segunda, no por alguna garantía de nivel inferior de que los duplicados nunca ocurran.
Una marca de agua (watermark) es cómo los sistemas de streaming razonan sobre "suficientemente hecho": dado que un flujo nunca termina realmente, una marca de agua es una afirmación heurística de que probablemente han llegado todos los datos hasta algún punto en el tiempo del evento, lo que permite que las agregaciones por ventanas (como "eventos por ventana de 5 minutos") finalicen un resultado mientras se acepta que una pequeña fracción de datos muy tardíos pueden perderse o manejarse por separado.
Los orquestadores por lotes se enfrentan a una versión paralela del mismo problema de idempotencia: si un trabajo nocturno falla a mitad de camino y se reintenta, agregar su salida parcial una segunda vez contaría las filas dos veces; la solución estándar es hacer que la salida de cada ejecución esté claveada por una partición estable (una fecha, un ID de lote) y que el trabajo sobrescriba esa partición en lugar de agregar ciegamente.
Los orquestadores basados en DAG (Airflow, Prefect, Dagster) también codifican cuánta historia representa una ejecución de tarea mediante un intervalo de datos, que es el rango de tiempo lógico que representa una ejecución, lo que permite que un backfill reejecute las ejecuciones del DAG del mes pasado y obtenga los datos del mes pasado, en lugar de procesar accidentalmente los datos de hoy bajo la etiqueta de ayer.
# un DAG expresa dependencia, no orden de ejecución:
# extract_orders y extract_customers no tienen dependencia entre sí,
# por lo que un orquestador es libre de ejecutarlos concurrentemente
extract_orders >> transform_orders
extract_customers >> transform_orders # transform espera a AMBAS tareas upstream
transform_orders >> load_warehouse # load solo comienza una vez que transform finalizaConsideraciones Avanzadas y Aplicaciones
A escala, la elección entre lotes y streaming deja de ser binaria y se convierte en un espectro: los sistemas de micro-lotes (structured streaming en PySpark, por ejemplo) procesan fragmentos delimitados pequeños y frecuentes, intercambiando parte de la latencia del streaming por el razonamiento más simple de datos delimitados del procesamiento por lotes.
Elegir entre Airflow, Prefect y Dagster es menos sobre qué motor de DAG es "mejor" y más sobre qué unidad de abstracción coincide con el modelo mental del equipo: Airflow se centra en tareas y operadores, Prefect se centra en funciones Python simples con anotaciones ligeras, y Dagster se centra en los activos de datos (las salidas que produce un pipeline) en lugar de las tareas que los producen.
La validación de datos pertenece como un nodo de primera clase en el DAG, no como una ocurrencia tardía: un pipeline que carga datos incorrectos según lo programado a menudo es peor que uno que falla ruidosamente, porque los consumidores posteriores (paneles, características de ML, otros pipelines) pueden confiar en la salida sin saber que es incorrecta.
La observabilidad para los pipelines tiene que responder preguntas que el monitoreo por lotes y el monitoreo en streaming hacen de manera diferente: el procesamiento por lotes pregunta "¿terminó esta ejecución y cómo se compararon sus recuentos de filas con el historial?", mientras que el procesamiento en streaming pregunta "¿cuánto retraso tiene este consumidor?" (retraso del consumidor) y "¿cuántos mensajes están atascados en una cola de mensajes muertos?".
La elección del formato de archivo interactúa con todo esto: los formatos columnares como Parquet admiten la poda de particiones y la inserción de predicados que hacen que los patrones de sobrescritura de particiones idempotentes sean baratos, mientras que los formatos orientados a filas hacen que el reprocesamiento selectivo de solo una partición sea mucho más costoso.
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Por Lotes | Razonamiento simple, puede ver todos los datos antes de responder, backfills fáciles | Latencia medida en minutos-horas; obsoleto por definición entre ejecuciones | Informes programados, backfills históricos, agregación no crítica en tiempo |
| Micro-lotes | Une la simplicidad de lotes con entrega casi en tiempo real | Todavía agrega un piso de latencia de planificación/disparo; no es verdaderamente impulsado por eventos | Paneles casi en tiempo real donde la latencia de segundos a minutos es aceptable |
| En Streaming | Latencia más baja, reacciona a eventos individuales | Requiere marcas de agua/ventanas, más difícil de razonar sobre "hecho", sobrecarga de gestión de estado | Detección de fraude, alertas en tiempo real, arquitecturas impulsadas por eventos |
| Orquestación DAG (Airflow/Prefect/Dagster) | Grafo de dependencia explícito, reintentos, backfills, planificación en un solo sistema | Agrega un sistema operativo para ejecutar y monitorear; excesivo para un script lineal simple | Pipelines de múltiples pasos con dependencias de tareas reales entre fuentes |
Conceptos Erróneos Comunes
- "Un DAG es solo una palabra elegante para una secuencia de pasos." - Un DAG solo codifica dependencias; las tareas sin dependencia entre sí pueden ejecutarse en paralelo o en cualquier orden, que es exactamente lo que permite a los orquestadores acelerar los pipelines sin cambiar la definición del DAG.
- "La entrega exactamente una vez significa que los duplicados literalmente no pueden ocurrir." - En sistemas distribuidos, "exactamente una vez" casi siempre significa entrega al menos una vez combinada con un sink idempotente que absorbe duplicados, no una garantía de que un mensaje se envíe o procese solo una vez.
- "El procesamiento en streaming es estrictamente mejor que el procesamiento por lotes porque es más rápido." - El procesamiento en streaming renuncia a la capacidad de ver todos los datos antes de responder, fuerza compensaciones de marcas de agua/datos tardíos y agrega una complejidad operativa real que el procesamiento por lotes simplemente no tiene que manejar.
- "La idempotencia es una característica de biblioteca que activas." - La idempotencia debe diseñarse en cómo una tarea escribe su salida (sobrescrituras claveadas, inserciones/actualizaciones por clave natural, reemplazo de partición); ningún orquestador o framework puede hacer que una escritura inherentemente de solo anexión y sin clave sea idempotente por ti.
- "Un backfill es solo volver a ejecutar el pipeline para fechas antiguas." - Un backfill correcto se basa en el concepto de intervalo de datos del DAG para que cada ejecución histórica se limite al rango de tiempo lógico correcto; ejecutar el código del pipeline "de hoy" contra datos antiguos sin esa limitación fácilmente etiqueta erróneamente o duplica resultados.
Preguntas Frecuentes
¿Cuál es la diferencia real entre procesamiento por lotes y en streaming, más allá de que "el procesamiento por lotes es más lento"?
- El procesamiento por lotes procesa una entrada delimitada y de tamaño conocido y puede verla toda antes de producir una respuesta.
- El procesamiento en streaming procesa una secuencia ilimitada y debe producir resultados útiles continuamente, sin ver nunca "todos" los datos.
- Esto cambia qué tipos de respuestas son posibles: un total por lotes es exacto, un total en streaming hasta ahora es provisional hasta que se reconcilien los datos tardíos.
¿Define un DAG el orden en que se ejecutan las tareas?
No directamente: un DAG define las dependencias entre tareas, y un orquestador es libre de ejecutar dos tareas cualesquiera sin dependencia entre ellas de forma concurrente o en cualquier orden, siempre que todas las dependencias ascendentes declaradas de cada tarea hayan finalizado primero.
¿Cuál es la diferencia entre planificación (scheduling) y orquestación?
La planificación decide cuándo se inicia un trabajo (un disparador tipo cron), mientras que la orquestación además rastrea las dependencias entre tareas, reintenta fallos y gestiona el estado de la ejecución de todo un DAG; un planificador sin modelo de dependencia no es un orquestador.
¿Por qué la idempotencia importa tanto para la fiabilidad del pipeline?
Los fallos son rutinarios en los pipelines distribuidos: los trabajadores se caen, las redes se particionan, los mensajes se reentregán; y la idempotencia es lo que convierte "esta tarea se ejecutó dos veces" de un evento que corrompe datos a una operación nula inofensiva, que es lo que hace que los reintentos automáticos y los backfills sean seguros de ejecutar sin limpieza manual.
¿Cómo se logra realmente el procesamiento "exactamente una vez" en la práctica?
- La entrega en sí misma es casi siempre al menos una vez: un mensaje puede ser reentregado después de un fallo.
- La ilusión de "exactamente una vez" proviene de escribir en un sink idempotente, por ejemplo, una inserción/actualización (upsert) claveada por ID de mensaje.
- Una entrega duplicada entonces simplemente sobrescribe el mismo registro en lugar de crear uno segundo.
¿Qué es una marca de agua (watermark) en streaming y por qué es necesaria?
Una marca de agua es una afirmación heurística de que probablemente han llegado todos los eventos hasta un cierto punto en el tiempo del evento, lo que permite que una agregación por ventanas (como un recuento de 5 minutos) finalice y emita un resultado en lugar de esperar para siempre datos que teóricamente aún podrían llegar tarde.
¿Cómo se mantiene un trabajo por lotes idempotente a través de reintentos?
Escribiendo la salida limitada a una clave de partición estable (una fecha, un ID de ejecución, un intervalo de datos) y sobrescribiendo esa partición en lugar de anexarla, de modo que una ejecución reintentada reemplace su propia salida previa (posiblemente parcial) en lugar de duplicar filas junto a ella.
¿Cuál es la diferencia entre Airflow, Prefect y Dagster a nivel de modelo mental?
Airflow centra su modelo en tareas y operadores conectados en un DAG; Prefect se centra en funciones Python ordinarias con una ligera decoración para el seguimiento de dependencias; Dagster se centra en los activos de datos que produce un pipeline en lugar de las tareas que los producen; los tres todavía orquestan DAGs de trabajo dependiente por debajo.
¿Cuándo tiene más sentido el micro-lote que el lote puro o el streaming puro?
El micro-lote encaja cuando la salida casi en tiempo real es importante (segundos a minutos, no horas), pero el equipo quiere seguir razonando sobre pequeños fragmentos delimitados en lugar de construir una infraestructura de streaming completa con marcas de agua y consumidores con estado de larga duración.
¿Por qué los paneles o los pipelines descendentes necesitan saber si un paso de validación de datos falló, no solo si la carga tuvo éxito?
Un pipeline que "tiene éxito" mientras carga datos inválidos, incompletos o duplicados es a menudo más perjudicial que uno que falla ruidosamente, porque los consumidores descendentes no tienen ninguna señal para desconfiar de la salida; es por eso que la validación de datos se trata como un nodo DAG con su propio estado de aprobación/fallo, no como una verificación secundaria.
¿Qué significa "retraso del consumidor" (consumer lag) en un contexto de streaming y por qué vigilarlo?
El retraso del consumidor es la brecha entre el último mensaje producido en un flujo y el último mensaje que un consumidor dado ha procesado; un retraso creciente significa que el consumidor no puede seguir el volumen entrante, lo que es una señal de advertencia temprana antes de que un pipeline se quede visiblemente atrás o pierda datos.
¿Es un script Python único que llama a funciones en secuencia "un DAG"?
Solo en el sentido trivial de que una secuencia estricta es técnicamente un grafo de dependencia (lineal); el valor práctico de un orquestador DAG real se muestra una vez que hay ramas genuinamente independientes que podrían ejecutarse en paralelo, o tareas que necesitan reintentos, backfills y planificación consciente de dependencias para las cuales un script simple no tiene marco.
¿Cómo interactúa la elección del formato de archivo (Parquet vs. CSV/JSON) con el reprocesamiento idempotente?
Los formatos columnares como Parquet admiten la poda de particiones, por lo que sobrescribir o reprocesar solo una partición (una fecha, un rango de claves) solo toca los archivos relevantes; los formatos orientados a filas como CSV generalmente requieren reescribir o escanear un archivo completo incluso para arreglar una partición, lo que hace que el reprocesamiento parcial idempotente sea mucho más costoso.
Relacionados
- Fundamentos de Ingeniería de Datos - la API concreta de lotes/streaming/pipelines sobre la que se asienta el modelo de esta página.
- Airflow - DAGs, intervalos de datos y backfills en la práctica.
- Prefect y Dagster - abstracciones alternativas de orquestación (funciones y activos de datos).
- Streaming (Kafka / Faust) - garantías de entrega, offsets y agregación por ventanas en la práctica.
- Fiabilidad de Flujos de Trabajo - el flujo de decisiones de idempotencia, reintento y observabilidad mencionado anteriormente.
- PySpark - ejecución distribuida por lotes y micro-lotes (structured streaming).
Versiones de Stack: Esta página fue escrita para Python 3.14 (estable) / 3.13 (mantenimiento); los conceptos (lotes/streaming, DAGs, idempotencia) son agnósticos a la herramienta y no están vinculados a una versión específica de orquestador o biblioteca de procesamiento de flujos.