O Modelo Mental de Batch, Streaming e Orquestração
Todo pipeline de dados, não importa qual ferramenta o construa, está respondendo às mesmas três perguntas.
Quanta quantidade de dados um único run deste pipeline processa - um chunk delimitado, ou um stream ilimitado e contínuo?
O que decide quando e em qual ordem cada etapa deste pipeline é executada?
E o que acontece na segunda vez que uma etapa é executada com a mesma entrada, seja por causa de um retry, um backfill ou uma mensagem redelivrada?
Noções Básicas de Engenharia de Dados mostra a API concreta para construir jobs batch e de streaming; esta página é sobre os três conceitos por trás de cada um deles - batch versus streaming como um trade-off, o DAG como um grafo de dependência em vez de um schedule, e idempotência como a propriedade que torna a recuperação de falhas segura.
Resumo
- Um pipeline de dados é definido por como ele delimita sua entrada (batch vs. streaming), como ele sequencia trabalhos dependentes (um DAG de tarefas) e quão seguramente ele pode ser reexecutado (idempotência) - toda ferramenta de orquestração está realmente apenas automatizando essas três preocupações.
- Por que Importa: Pipelines falham - jobs travam, workers morrem, mensagens são redelivradas - e um pipeline que não é idempotente transforma cada falha em um incidente de qualidade de dados em vez de um retry de rotina.
- Conceitos Chave: batch vs. streaming, DAG (directed acyclic graph - grafo direcionado acíclico), orquestração vs. agendamento (scheduling), idempotência, watermark, garantia de entrega.
- Quando Usar: Batch para relatórios delimitados e periódicos e backfills; streaming para reações de baixa latência e orientadas a eventos; um orquestrador baseado em DAG sempre que tarefas tiverem dependências reais entre si, não apenas um horário para executar.
- Limitações / Trade-offs: Streaming compra latência ao custo de complexidade de coordenação (watermarks, estado, ordenação); batch compra simplicidade e completude ao custo de obsolescência (staleness); idempotência tem que ser projetada em cada sink, não é uma configuração que você habilita.
- Tópicos Relacionados: orquestração de workflows (Airflow, Prefect, Dagster), processamento de streams (Kafka, Faust), computação distribuída (PySpark), camadas de transformação (dbt).
Fundamentos
Um pipeline batch processa um conjunto de dados delimitado e de tamanho conhecido - "pedidos de ontem", "arquivos de log deste mês" - e termina; executá-lo novamente mais tarde processa o próximo chunk delimitado.
Um pipeline streaming processa uma sequência ilimitada de eventos à medida que eles chegam, sem um "fim" natural para esperar, o que significa que ele tem que produzir resultados úteis continuamente em vez de após uma passagem completa pelos dados.
Este é um trade-off genuíno, não apenas duas implementações da mesma ideia: batch pode olhar todos os dados antes de decidir uma resposta (um total do dia completo, um join completo), enquanto streaming tem que responder apenas com o que chegou até agora, aceitando que dados que chegam atrasados podem precisar ser reconciliados depois.
Um DAG (directed acyclic graph) é a segunda ideia central: é um grafo de tarefas e dependências - "tarefa B precisa da saída da tarefa A" - sem ciclos, e criticamente, um DAG define o que depende do quê, não quando as coisas rodam.
Uma leitura comum equivocada é pensar em um DAG como uma sequência fixa, como uma lista numerada de etapas; na realidade, um scheduler (agendador) é livre para executar quaisquer duas tarefas sem dependência entre elas em paralelo, ou em qualquer ordem, desde que as dependências declaradas de cada tarefa sejam concluídas primeiro.
Orquestração é a camada que transforma uma definição de DAG em execução real: ela decide quais tarefas estão prontas para rodar (suas dependências foram satisfeitas), retenta tarefas falhas e rastreia o estado de cada run - isso é distinto do agendamento (scheduling) simples, que apenas decide quando um job começa, sem noção de dependência entre jobs.
Idempotência é a propriedade de que executar uma operação duas vezes com a mesma entrada produz o mesmo resultado como executá-la uma vez - e é a propriedade que torna cada retry, backfill e mensagem redelivrada segura em vez de perigosa.
Mecânicas e Interações
A escolha batch/streaming e o requisito de idempotência estão mais conectados do que parecem inicialmente: qualquer sistema de streaming que promete entrega confiável sob falha precisa redelivrar mensagens às vezes, e apenas o processamento idempotente torna essa redelivração inofensiva em vez de duplicar dados.
Sistemas de streaming oferecem principalmente entrega at-least-once (pelo menos uma vez) por padrão - uma mensagem pode ser processada mais de uma vez após uma falha e reinício, porque confirmar "eu processei isso" e "commit o resultado" não pode ser tornado perfeitamente atômico entre dois sistemas diferentes (o message broker e o sink de saída) sem coordenação extra.
O processamento exactly-once (exatamente uma vez), onde é alegado, é geralmente alcançado combinando entrega at-least-once com um sink idempotente - por exemplo, escrevendo resultados com chave por um ID de mensagem para que uma escrita duplicada simplesmente sobrescreva a mesma linha em vez de adicionar uma segunda - não por alguma garantia de nível inferior de que duplicatas nunca ocorrem.
Um watermark é como os sistemas de streaming raciocinam sobre "feito o suficiente": já que um stream nunca termina de verdade, um watermark é uma afirmação heurística de que todos os dados até algum ponto no tempo do evento provavelmente chegaram, permitindo que agregações em janelas (como "eventos por janela de 5 minutos") finalizem um resultado enquanto aceitam que uma pequena fração de dados muito atrasados pode ser perdida ou tratada separadamente.
Orquestradores batch enfrentam uma versão paralela do mesmo problema de idempotência: se um job noturno travar no meio e for retentado, anexar sua saída parcial uma segunda vez duplicaria as contagens de linhas - a correção padrão é fazer a saída de cada run ser chaveada por uma partição estável (uma data, um ID de batch) e ter o job sobrescrever essa partição em vez de anexar cegamente.
Orquestradores baseados em DAG (Airflow, Prefect, Dagster) também codificam quanta história um run de tarefa "pertence" através de um data interval (intervalo de dados) - o intervalo de tempo lógico que um run representa - que é o que permite que um backfill reexecute os runs do DAG do mês passado e obtenha os dados do mês passado de volta, em vez de acidentalmente processar os dados de hoje sob o rótulo de ontem.
# um DAG expressa dependência, não ordem de execução:
# extract_orders e extract_customers não têm dependência um do outro,
# então um orquestrador é livre para executá-los concorrentemente
extract_orders >> transform_orders
extract_customers >> transform_orders # transform espera por AMBAS as tarefas upstream
transform_orders >> load_warehouse # load só começa quando transform terminaConsiderações Avançadas e Aplicações
Em escala, a escolha batch versus streaming deixa de ser binária e se torna um espectro: sistemas de micro-batch (structured streaming no PySpark, por exemplo) processam chunks delimitados pequenos e frequentes, trocando parte da latência do streaming pela lógica mais simples de dados delimitados do batch.
Escolher entre Airflow, Prefect e Dagster é menos sobre qual engine de DAG é "melhor" e mais sobre qual unidade de abstração corresponde ao modelo mental da equipe - Airflow se concentra em tarefas e operadores, Prefect se concentra em funções Python simples com anotações leves, e Dagster se concentra em ativos de dados (as saídas que um pipeline produz) em vez das tarefas que os produzem.
A validação de dados pertence como um nó de primeira classe no DAG, não como um pensamento posterior: um pipeline que carrega dados ruins em um schedule é muitas vezes pior do que um que falha ruidosamente, porque consumidores downstream (dashboards, features de ML, outros pipelines) podem confiar na saída sem saber que ela está errada.
A observabilidade para pipelines tem que responder perguntas que o monitoramento batch e o monitoramento de streaming fazem de forma diferente: batch pergunta "este run terminou, e como suas contagens de linhas se comparam ao histórico", enquanto streaming pergunta "quão atrasado está este consumidor" (consumer lag) e "quantas mensagens estão presas em uma fila de mensagens mortas (dead-letter queue)".
A escolha do formato de arquivo interage com tudo isso: formatos colunares como Parquet suportam poda de partições (partition pruning) e pushdown de predicados que tornam os padrões de sobrescrita de partição idempotentes baratos, enquanto formatos orientados a linhas tornam o reprocessamento seletivo de apenas uma partição muito mais caro.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Batch | Raciocínio simples, pode ver todos os dados antes de responder, backfills fáceis | Latência medida em minutos-a-horas; obsoleto por definição entre runs | Relatórios agendados, backfills históricos, agregação não crítica em tempo |
| Micro-batch | Une a simplicidade do batch com entrega quase em tempo real | Ainda adiciona um piso de latência de agendamento/trigger; não verdadeiramente orientado a eventos | Dashboards quase em tempo real onde latência de segundos-a-minutos é aceitável |
| Streaming | Menor latência, reage a eventos individuais | Requer watermarks/windowing, mais difícil de raciocinar sobre "feito", sobrecarga de gerenciamento de estado | Detecção de fraude, alertas em tempo real, arquiteturas orientadas a eventos |
| Orquestração DAG (Airflow/Prefect/Dagster) | Grafo de dependência explícito, retries, backfills, agendamento em um único sistema | Adiciona um sistema operacional para rodar e monitorar; exagero para um script linear simples | Pipelines multi-etapas com dependências reais entre tarefas em várias fontes |
Concepções Errôneas Comuns
- "Um DAG é apenas uma palavra chique para uma sequência de etapas." - Um DAG apenas codifica dependências; tarefas sem dependência entre si podem rodar em paralelo ou em qualquer ordem, que é exatamente o que permite que orquestradores acelerem pipelines sem alterar a definição do DAG.
- "Entrega exatamente-uma-vez significa que duplicatas literalmente não podem acontecer." - Em sistemas distribuídos, "exatamente-uma-vez" quase sempre significa entrega pelo-menos-uma-vez combinada com um sink idempotente que absorve duplicatas, não uma garantia de que uma mensagem é enviada ou processada apenas uma única vez.
- "Streaming é estritamente melhor que batch porque é mais rápido." - Streaming troca a capacidade de olhar todos os dados antes de responder, força trade-offs de watermark/dados atrasados, e adiciona complexidade operacional real que batch simplesmente não tem que lidar.
- "Idempotência é um recurso de biblioteca que você ativa." - Idempotência tem que ser projetada na forma como uma tarefa escreve sua saída (sobrescritas com chave, upserts por chave natural, substituição de partição) - nenhum orquestrador ou framework pode torná-lo idempotente para você uma escrita inerentemente de anexação, sem chave.
- "Um backfill é apenas executar o pipeline novamente para datas antigas." - Um backfill correto depende do conceito de data interval do DAG para que cada run histórico seja escopado para o intervalo de tempo lógico correto; executar o código do pipeline "de hoje" em dados antigos sem esse escopo facilmente rotula incorretamente ou duplica resultados.
FAQs
Qual é a diferença real entre batch e streaming, além de "batch é mais lento"?
- Batch processa uma entrada delimitada e de tamanho conhecido e pode ver tudo antes de produzir uma resposta.
- Streaming processa uma sequência ilimitada e deve produzir resultados úteis continuamente, sem nunca ver "todos" os dados.
- Isso muda que tipos de respostas são possíveis - um total batch é exato, um total de streaming até agora é provisório até que dados atrasados sejam reconciliados.
Um DAG define a ordem em que as tarefas são executadas?
Não diretamente - um DAG define dependências entre tarefas, e um orquestrador é livre para executar quaisquer duas tarefas sem dependência entre elas concorrentemente ou em qualquer ordem, desde que todas as dependências upstream declaradas de cada tarefa tenham sido concluídas primeiro.
Qual é a diferença entre agendamento (scheduling) e orquestração?
Agendamento decide quando um job começa (um trigger tipo cron), enquanto orquestração adicionalmente rastreia dependências entre tarefas, retenta falhas e gerencia o estado da execução de um DAG inteiro - um agendador sem modelo de dependência não é um orquestrador.
Por que a idempotência importa tanto para a confiabilidade do pipeline?
Falhas são rotineiras em pipelines distribuídos - workers travam, redes particionam, mensagens são redelivradas - e idempotência é o que transforma "esta tarefa rodou duas vezes" de um evento que corrompe dados para um no-op inofensivo, que é o que torna retries automáticos e backfills seguros para executar sem limpeza manual.
Como o processamento "exatamente-uma-vez" é realmente alcançado na prática?
- A entrega em si é quase sempre pelo-menos-uma-vez: uma mensagem pode ser redelivrada após uma falha.
- A ilusão de "exatamente-uma-vez" vem de escrever para um sink idempotente - por exemplo, um upsert com chave por ID de mensagem.
- Uma entrega duplicada então apenas sobrescreve o mesmo registro em vez de criar um segundo.
O que é um watermark em streaming e por que ele é necessário?
Um watermark é uma afirmação heurística de que todos os eventos até um certo ponto no tempo do evento provavelmente chegaram, o que permite que uma agregação em janela (como uma contagem de 5 minutos) finalize e emita um resultado em vez de esperar para sempre por dados que teoricamente ainda podem chegar atrasados.
Como um job batch permanece idempotente entre retries?
Escrevendo a saída escopada para uma chave de partição estável - uma data, um ID de run, um data interval - e sobrescrevendo essa partição em vez de anexá-la, de modo que um run retentado substitua sua própria saída anterior (possivelmente parcial) em vez de duplicar linhas ao lado dela.
Qual é a diferença entre Airflow, Prefect e Dagster em nível de modelo mental?
Airflow centra seu modelo em tarefas e operadores conectados em um DAG; Prefect centra em funções Python comuns com decoração leve para rastreamento de dependências; Dagster centra nos ativos de dados que um pipeline produz em vez das tarefas que os produzem - todos os três ainda orquestram DAGs de trabalho dependente por baixo.
Quando micro-batch faz mais sentido do que batch puro ou streaming puro?
Micro-batch se encaixa quando a saída quase em tempo real importa (segundos-a-minutos, não horas), mas a equipe quer continuar raciocinando sobre pequenos chunks delimitados em vez de construir infraestrutura de streaming completa com watermarks e consumidores stateful de longa duração.
Por que dashboards ou pipelines downstream precisam saber se uma etapa de validação de dados falhou, e não apenas se o load foi bem-sucedido?
Um pipeline que "bem-sucedido" enquanto carrega dados inválidos, incompletos ou duplicados é muitas vezes mais prejudicial do que um que falha ruidosamente, porque os consumidores downstream não têm sinal para desconfiar da saída - é por isso que a validação de dados é tratada como um nó de DAG com seu próprio estado de aprovação/falha, não uma verificação lateral.
O que significa "consumer lag" em um contexto de streaming e por que observá-lo?
Consumer lag é a lacuna entre a última mensagem produzida para um stream e a última mensagem que um determinado consumidor processou; um lag crescente significa que o consumidor não consegue acompanhar o volume de entrada, o que é um sinal de alerta precoce antes que um pipeline visivelmente fique para trás ou perca dados.
Um único script Python que chama funções em sequência é "um DAG"?
Apenas no sentido trivial de que uma sequência estrita é tecnicamente um grafo de dependência (linear) - o valor prático de um orquestrador DAG real aparece quando existem branches genuinamente independentes que poderiam rodar em paralelo, ou tarefas que precisam de retries, backfills e agendamento ciente de dependências que um script simples não tem framework para.
Como a escolha do formato de arquivo (Parquet vs. CSV/JSON) interage com o reprocessamento idempotente?
Formatos colunares como Parquet suportam poda de partições, então sobrescrever ou reprocessar apenas uma partição (uma data, um intervalo de chaves) toca apenas os arquivos relevantes; formatos orientados a linhas como CSV geralmente exigem reescrita ou varredura de um arquivo inteiro mesmo para corrigir uma partição, tornando o reprocessamento parcial idempotente muito mais caro.
Relacionados
- Noções Básicas de Engenharia de Dados - a API concreta de batch/streaming/pipeline em que o modelo desta página se baseia.
- Airflow - DAGs, data intervals e backfills na prática.
- Prefect e Dagster - abstrações de orquestração alternativas (funções e ativos de dados).
- Streaming (Kafka / Faust) - garantias de entrega, offsets e agregação em janela na prática.
- Confiabilidade de Workflow - o fluxo de decisão de idempotência, retry e observabilidade referenciado acima.
- PySpark - execução distribuída batch e micro-batch (structured streaming).
Versões do Stack: Esta página foi escrita para Python 3.14 (estável) / 3.13 (manutenção); os conceitos (batch/streaming, DAGs, idempotência) são agnósticos de ferramenta e não estão atrelados a uma versão específica de orquestrador ou biblioteca de processamento de stream.