Full load costuma ser a primeira estratégia de um pipeline porque simplifica o problema: a cada execução, o sistema lê todos os dados da origem, aplica a transformação e substitui o destino. Não precisa registrar até onde a execução anterior leu nem interpretar separadamente cada atualização ou exclusão.

Essa simplicidade local deixa de escalar quando se repete em milhares de pipelines. A plataforma passa a reler, recalcular e reescrever informações que já conhecia. Migrar para incremental, porém, exige mais do que ler menos linhas.

A decisão depende de como a informação muda, como a transformação reage e que garantia o consumidor espera do resultado. Essa combinação determina se o pipeline pode apenas acumular fatos ou se precisará preservar informações entre execuções, corrigir saídas, estabelecer completude ou recomputar.

1. Por que o full load é tão comum

Considere as compras de um cartão com limite de R$ 1.000. Cada compra possui um identificador estável, um valor e um status. Neste primeiro recorte, o pipeline soma as compras confirmadas e não estornadas. Esse total reduz o limite disponível, que indica quanto ainda pode ser gasto, e compõe a fatura em preparação, que indica quanto será cobrado no ciclo.

Na primeira execução, a origem contém apenas um registro:

CompraValor atualStatus
C-42R$ 100confirmada

A compra afeta desde o início dois resultados:

  • o limite disponível passa a R$ 900;
  • a fatura em preparação passa a R$ 100.

Antes da execução seguinte, o valor de C-42 é corrigido para R$ 120 e uma nova compra, C-43, acrescenta R$ 80:

CompraValor atualStatus
C-42R$ 120confirmada
C-43R$ 80confirmada

As mudanças na origem e as execuções do pipeline ocorrem em momentos distintos:

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#F8FAFC",
    "primaryColor": "#FFFFFF",
    "primaryTextColor": "#0F172A",
    "primaryBorderColor": "#E2E8F0",
    "secondaryColor": "#F1F5F9",
    "tertiaryColor": "#FFE0F0",
    "lineColor": "#5A6B7D",
    "titleColor": "#0F172A",
    "clusterBkg": "#F1F5F9",
    "clusterBorder": "#E2E8F0",
    "actorBkg": "#FFFFFF",
    "actorBorder": "#E2E8F0",
    "actorTextColor": "#0F172A",
    "actorLineColor": "#CBD5E1",
    "signalColor": "#D6006F",
    "signalTextColor": "#0F172A",
    "labelBoxBkgColor": "#F1F5F9",
    "labelBoxBorderColor": "#E2E8F0",
    "labelTextColor": "#0F172A",
    "loopTextColor": "#0F172A",
    "noteBkgColor": "#F1F5F9",
    "noteBorderColor": "#CBD5E1",
    "noteTextColor": "#0F172A",
    "activationBkgColor": "#E3F6F9",
    "activationBorderColor": "#007C91",
    "sequenceNumberColor": "#D6006F",
    "arrowheadColor": "#D6006F",
    "fontFamily": "Inter, Segoe UI, Helvetica Neue, Arial, sans-serif",
    "fontSize": "15px"
  },
  "flowchart": {"curve": "basis", "nodeSpacing": 28, "rankSpacing": 36, "padding": 8}
}}%%
sequenceDiagram
    participant O as Compras na origem
    participant A as Pipeline analítico

    Note over O: C-42 · R$ 100 · confirmada
    A->>O: Ler todos os dados (Full load)
    Note over A: Limite disponível · R$ 900<br/>Fatura em preparação · R$ 100
    Note over O: C-42 · corrigida para R$ 120
    Note over O: C-43 · R$ 80 · confirmada
    A->>O: Ler todos os dados (Full load)
    Note over A: Limite disponível · R$ 800<br/>Fatura em preparação · R$ 200

Nas duas execuções, o pipeline repete o mesmo procedimento:

  1. ler todas as compras do ciclo;
  2. selecionar aquelas que estão confirmadas e não foram estornadas;
  3. somar seus valores;
  4. derivar a fatura e o limite disponível a partir dessa soma.

Na segunda execução, o pipeline soma o valor corrigido de C-42 e a nova compra C-43, chegando a R$ 200. A fatura recebe esse total, e o limite disponível resulta de R$ 1.000 - R$ 200 = R$ 800.

Essa reconstrução é a simplificação central: cada execução deriva os dois resultados de todos os dados que a origem disponibiliza naquele momento. Ela pressupõe que a origem ofereça uma versão coerente para leitura; o full load, sozinho, não cria essa garantia.

Ainda assim, ele é uma escolha razoável quando reler um volume pequeno custa pouco, quando a execução ocorre poucas vezes ou quando o destino precisa ser construído pela primeira vez.

As duas estratégias compõem o novo resultado de formas diferentes:

  • full load: calcula novamente a partir de todos os dados lidos na execução atual; o resultado da execução anterior não participa do cálculo;
  • incremental: parte do resultado anterior e aplica somente as mudanças recebidas desde então.

O incremental, portanto, precisa reutilizar resultados e preservar as informações necessárias para interpretar cada mudança corretamente.

2. Como o incremental atualiza um resultado

Depois da primeira carga, o pipeline parte da fatura de R$ 100 e do limite disponível de R$ 900, sem executar outro full load. Ele também preserva o registro atual de C-42 em uma estrutura chamada estado por chave.

Essa estrutura guarda apenas as informações necessárias para interpretar a próxima mudança; ela não é o total calculado nem precisa repetir todas as colunas da origem.

Quando o valor de C-42 é corrigido para R$ 120, a chave permite localizar a compra anterior, substituir seu valor e calcular o efeito da correção:

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#F8FAFC",
    "primaryColor": "#FFFFFF",
    "primaryTextColor": "#0F172A",
    "primaryBorderColor": "#E2E8F0",
    "secondaryColor": "#F1F5F9",
    "tertiaryColor": "#FFE0F0",
    "lineColor": "#5A6B7D",
    "titleColor": "#0F172A",
    "clusterBkg": "#F1F5F9",
    "clusterBorder": "#E2E8F0",
    "actorBkg": "#FFFFFF",
    "actorBorder": "#E2E8F0",
    "actorTextColor": "#0F172A",
    "actorLineColor": "#CBD5E1",
    "signalColor": "#D6006F",
    "signalTextColor": "#0F172A",
    "labelBoxBkgColor": "#F1F5F9",
    "labelBoxBorderColor": "#E2E8F0",
    "labelTextColor": "#0F172A",
    "loopTextColor": "#0F172A",
    "noteBkgColor": "#F1F5F9",
    "noteBorderColor": "#CBD5E1",
    "noteTextColor": "#0F172A",
    "activationBkgColor": "#E3F6F9",
    "activationBorderColor": "#007C91",
    "sequenceNumberColor": "#D6006F",
    "arrowheadColor": "#D6006F",
    "fontFamily": "Inter, Segoe UI, Helvetica Neue, Arial, sans-serif",
    "fontSize": "15px"
  },
  "flowchart": {"curve": "basis", "nodeSpacing": 28, "rankSpacing": 36, "padding": 8}
}}%%
flowchart LR
    classDef active fill:#FFE0F0,stroke:#D6006F,color:#0F172A,stroke-width:2px;
    classDef muted fill:#FFFFFF,stroke:#E2E8F0,color:#5A6B7D,stroke-width:1px;
    classDef support fill:#E3F6F9,stroke:#007C91,color:#0F172A,stroke-width:1.5px,stroke-dasharray:4 2;
    classDef warning fill:#FFF4D6,stroke:#9A5B00,color:#0F172A,stroke-width:2px;

    D["<b>Mudança recebida</b><br/>Compra: C-42<br/>Valor atual: R$ 120"] --> Q["Localizar e atualizar o <b>estado por chave</b>"]
    S["<b>Estado por chave</b><br/>Compra: C-42<br/>Valor atual: R$ 100<br/>Status: confirmada"] --> Q
    Q --> S2["<b>Estado por chave</b><br/>Compra: C-42<br/>Valor atual: R$ 120<br/>Status: confirmada"]
    Q --> DV["<b>Mudança no valor comprometido (ΔV)</b><br/>− R$ 100 + R$ 120 = + R$ 20"]
    DV --> V["<b>Resultados atualizados</b><br/>Fatura: R$ 100 → R$ 120<br/>Limite: R$ 900 → R$ 880"]
    class Q active;
    class D,DV muted;
    class S,S2 support;
    class V active;

Esse processo tem duas etapas.

Primeiro: calcular a mudança no resultado

Para corrigir C-42, o valor anterior precisa sair e o novo precisa entrar:

ΔV = - R$ 100 + R$ 120 = + R$ 20

ΔV é a mudança que a transformação produz no valor comprometido pelas compras. A correção fornece o novo valor, enquanto o estado por chave fornece o anterior. O estado atualizado permanece disponível para mudanças posteriores; ΔV descreve apenas o efeito da correção atual.

Depois: aplicar a mudança ao resultado existente

Somente depois de obter ΔV, o pipeline atualiza os resultados armazenados. Como a correção acrescentou R$ 20 ao valor comprometido, ela aumenta a fatura e reduz o limite na mesma medida:

fatura: R$ 100 + R$ 20 = R$ 120
limite: R$ 900 - R$ 20 = R$ 880

Por isso, os resultados anteriores também precisam permanecer disponíveis entre atualizações. Enquanto a fatura está aberta, cada ΔV altera os dois resultados em sentidos opostos:

Mudança recebidaΔVLimite após a mudançaFatura após a mudança
C-42 confirmada por R$ 100+ R$ 100R$ 900R$ 100
C-42 corrigida para R$ 120+ R$ 20R$ 880R$ 120
C-43 confirmada por R$ 80+ R$ 80R$ 800R$ 200
C-42 estornada- R$ 120R$ 920R$ 80

Depois do fechamento, a fatura deixa de aceitar novos lançamentos daquele ciclo; um pagamento, por sua vez, libera limite sem alterar a cobrança emitida.

Na literatura de bancos de dados, atualizar o resultado de uma consulta sem calculá-lo inteiro novamente é chamado de manutenção incremental de views. O survey de Gupta e Mumick mostra que a estratégia depende da consulta, do tipo de mudança e das informações disponíveis. O DBSP oferece uma formulação moderna na qual operadores preservam informações entre atualizações, recebem mudanças da entrada e produzem mudanças na saída.

O custo da atualização depende da agregação

Antes de C-42 ser estornada, o estado por chave era:

CompraValor atualStatus
C-42R$ 120confirmada
C-43R$ 80confirmada

Essas compras produzem SUM = R$ 200 e MAX = R$ 120, a maior compra do ciclo. Quando C-42 é estornada, o pipeline localiza diretamente sua linha e descobre que R$ 120 deixaram de fazer parte das agregações.

SUM: atualizar o resultado anterior

O resultado armazenado de SUM é R$ 200. Depois de localizar C-42, basta retirar seu valor:

R$ 200 - R$ 120 = R$ 80

Com acesso direto à compra e ao resultado anterior, essa atualização leva tempo esperado constante, O(1). Ela não precisa percorrer as demais chaves.

MAX: procurar o resultado substituto

O resultado armazenado de MAX é R$ 120. Como esse valor pertencia a C-42, o estorno remove justamente o máximo atual. O resultado anterior não informa qual valor deve substituí-lo.

Usando somente o estado por chave, o pipeline percorre as compras confirmadas e não estornadas, encontra R$ 80 em C-43 e substitui o resultado de MAX. No pior caso, essa busca visita todas as linhas e custa O(n).

Os dois cálculos usam o mesmo estado por chave e mantêm apenas o resultado da agregação. A diferença está na operação: SUM deriva o novo total do valor anterior e da compra retirada; MAX, quando perde o máximo atual, precisa procurar o próximo candidato.

O DBSP parte da hipótese de que a mudança recebida é muito menor que a base. Quando quase tudo muda, ou quando atualizar uma agregação exige percorrer todo o estado por chave, a vantagem sobre o full load diminui. Nesse caso, o full load pode ser a escolha mais simples.

3. Quando uma mudança não traz contexto suficiente

Uma mudança isolada nem sempre contém informação suficiente para atualizar o resultado. C-45 aparece primeiro como pendente, com valor desconhecido, e só pode afetar limite e fatura depois da confirmação:

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#F8FAFC",
    "primaryColor": "#FFFFFF",
    "primaryTextColor": "#0F172A",
    "primaryBorderColor": "#E2E8F0",
    "secondaryColor": "#F1F5F9",
    "tertiaryColor": "#FFE0F0",
    "lineColor": "#5A6B7D",
    "titleColor": "#0F172A",
    "clusterBkg": "#F1F5F9",
    "clusterBorder": "#E2E8F0",
    "actorBkg": "#FFFFFF",
    "actorBorder": "#E2E8F0",
    "actorTextColor": "#0F172A",
    "actorLineColor": "#CBD5E1",
    "signalColor": "#D6006F",
    "signalTextColor": "#0F172A",
    "labelBoxBkgColor": "#F1F5F9",
    "labelBoxBorderColor": "#E2E8F0",
    "labelTextColor": "#0F172A",
    "loopTextColor": "#0F172A",
    "noteBkgColor": "#F1F5F9",
    "noteBorderColor": "#CBD5E1",
    "noteTextColor": "#0F172A",
    "activationBkgColor": "#E3F6F9",
    "activationBorderColor": "#007C91",
    "sequenceNumberColor": "#D6006F",
    "arrowheadColor": "#D6006F",
    "fontFamily": "Inter, Segoe UI, Helvetica Neue, Arial, sans-serif",
    "fontSize": "15px"
  },
  "flowchart": {"curve": "basis", "nodeSpacing": 28, "rankSpacing": 36, "padding": 8}
}}%%
sequenceDiagram
    title Como C-45 se torna informação suficiente
    participant M as Mudanças disponíveis
    participant E as Estado por chave
    participant R as Resultados

    M->>E: 30/07 · 14:02 · C-45 pendente · valor desconhecido
    Note over E: C-45 · valor desconhecido · pendente
    Note over E,R: Ainda não é possível produzir um delta
    M->>E: 30/07 · 14:05 · C-45 confirmada · R$ 30
    Note over E: C-45 · R$ 30 · confirmada
    E->>R: Produzir ΔV = + R$ 30
    Note over R: Limite · R$ 920 → R$ 890<br/>Fatura · R$ 80 → R$ 110

Até a confirmação, o pipeline preserva C-45 como uma entrada incompleta do estado por chave. Chamamos essa entrada de estado de reconstrução por chave: o conjunto mínimo de informações mantido até que seja possível formar o registro atual e produzir ΔV.

4. Lançamentos tardios mudam o passado conhecido

Mesmo completa, uma compra pode ficar disponível somente depois de o pipeline processar o período ao qual ela pertence. Esse é um lançamento tardio.

Um lançamento tardio envolve dois horários:

  • tempo do evento: quando a compra aconteceu e a qual ciclo pertence;
  • tempo do processamento: quando ela ficou disponível para o pipeline.

O ciclo de julho termina em 31/07, às 23:59. O batch executado em 01/08, às 00:15, termina com uma fatura de R$ 110 e um limite disponível de R$ 890. Dez minutos depois, outra compra fica disponível:

CompraValor atualStatusTempo do eventoTempo do processamento
C-48R$ 80confirmada31/07, 23:5801/08, 00:25

C-48 pertence ao ciclo já processado, mas só ficou disponível depois da execução das 00:15. Esse atraso também ocorre em batches incrementais; não depende de processamento em tempo real.

O pipeline forma o estado por chave de C-48 e calcula ΔV = + R$ 80. Como a fatura ainda está em preparação, a compra atualiza os dois resultados:

ResultadoValor anteriorMudança aplicadaValor novo
Limite disponívelR$ 890- R$ 80R$ 810
Fatura em preparaçãoR$ 110+ R$ 80R$ 190

Um full load posterior incorporaria C-48 reconstruindo o ciclo inteiro; o incremental aplica a mudança aos resultados já calculados. Nos dois casos, terminar um batch de julho não significa que todos os registros de julho já estejam disponíveis: o passado conhecido ainda pode se ampliar.

O Dataflow Model formaliza a separação entre o tempo em que um evento ocorreu e o tempo em que ficou disponível para processamento.

5. Quando uma resposta precisa ser definitiva

Uma fatura precisa ser fechada mesmo sem a garantia de que todas as compras de julho já chegaram. Neste exemplo, a fatura de julho fecha em 01/08, às 00:30.

Todas as compras abaixo foram realizadas em julho: as disponíveis até o horário de fechamento ainda podem entrar na fatura; as que se tornam disponíveis depois seguem para a próxima:

CompraValor atualStatusTempo do eventoTempo do processamento
C-42R$ 120estornada29/07, 11:0029/07, 11:00
C-43R$ 80confirmada28/07, 10:0028/07, 10:00
C-45R$ 30confirmada30/07, 14:0530/07, 14:05
C-48R$ 80confirmada31/07, 23:5801/08, 00:25
C-49R$ 40confirmada31/07, 23:5901/08, 01:15

C-49 foi realizada em julho, mas só ficou disponível às 01:15, 45 minutos depois do fechamento. Como ultrapassou a margem das 00:30, ela não altera a fatura já emitida e segue para a próxima.

Duas fronteiras fecham o resultado

Para emitir a fatura de julho, o pipeline precisa responder a duas perguntas:

  • Quais registros pertencem ao ciclo?
  • Quando todas as entradas relevantes avançaram o suficiente para que a fatura possa ser fechada?

Cada pergunta define uma fronteira.

Completude

FronteiraO que define
Do cicloquais compras pertencem ao ciclo de julho
De processamentocompras disponíveis até 00:30 entram na fatura de julho

A fronteira do ciclo classifica C-48 e C-49 como compras de julho. A de processamento inclui C-48 nessa fatura, pois ela ficou disponível às 00:25, e encaminha C-49 à seguinte, pois ela só chegou às 01:15. Quando os dados vêm de entradas independentes, verificar se todas alcançaram essa segunda fronteira exige coordenação.

Neste contexto, completude significa que todas as entradas relevantes avançaram até a fronteira de processamento. Alcançada essa condição, a fatura pode ser emitida.

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#F8FAFC",
    "primaryColor": "#FFFFFF",
    "primaryTextColor": "#0F172A",
    "primaryBorderColor": "#E2E8F0",
    "secondaryColor": "#F1F5F9",
    "tertiaryColor": "#FFE0F0",
    "lineColor": "#5A6B7D",
    "titleColor": "#0F172A",
    "clusterBkg": "#F1F5F9",
    "clusterBorder": "#E2E8F0",
    "actorBkg": "#FFFFFF",
    "actorBorder": "#E2E8F0",
    "actorTextColor": "#0F172A",
    "actorLineColor": "#CBD5E1",
    "signalColor": "#D6006F",
    "signalTextColor": "#0F172A",
    "labelBoxBkgColor": "#F1F5F9",
    "labelBoxBorderColor": "#E2E8F0",
    "labelTextColor": "#0F172A",
    "loopTextColor": "#0F172A",
    "noteBkgColor": "#F1F5F9",
    "noteBorderColor": "#CBD5E1",
    "noteTextColor": "#0F172A",
    "activationBkgColor": "#E3F6F9",
    "activationBorderColor": "#007C91",
    "sequenceNumberColor": "#D6006F",
    "arrowheadColor": "#D6006F",
    "fontFamily": "Inter, Segoe UI, Helvetica Neue, Arial, sans-serif",
    "fontSize": "15px"
  },
  "flowchart": {"curve": "basis", "nodeSpacing": 28, "rankSpacing": 36, "padding": 8}
}}%%
sequenceDiagram
    title Lançamentos tardios antes e depois do fechamento
    participant N as Compras na ordem de disponibilidade
    participant C as Limite disponível
    participant F as Faturas

    Note over C: R$ 890
    Note over F: Julho (Em preparação) · R$ 110
    N-->F: 31/07 · 23:59 · fronteira do ciclo
    Note over N: 31/07 · 23:58<br/>C-48 · R$ 80
    N->>C: Reduzir R$ 80
    Note over C: R$ 810
    N->>F: Adicionar à fatura de julho
    Note over F: Julho (Em preparação) · R$ 190
    N-->F: 01/08 · 00:30 · fronteira de processamento
    Note over F: Julho (Emitida) · R$ 190
    Note over N: 31/07 · 23:59<br/>C-49 · R$ 40
    N->>C: Reduzir R$ 40
    Note over C: R$ 770
    N->>F: Adicionar à próxima fatura
    Note over F: Julho (Emitida) · R$ 190<br/>Agosto (Em preparação) · R$ 40

Coordenação

A fatura só pode ser emitida quando todas as entradas capazes de alterá-la tiverem processado dados até pelo menos 00:30. Suponha que o pipeline receba compras de duas processadoras de cartões independentes:

%%{init: {
  "theme": "base",
  "themeVariables": {
    "background": "#F8FAFC",
    "primaryColor": "#FFFFFF",
    "primaryTextColor": "#0F172A",
    "primaryBorderColor": "#E2E8F0",
    "secondaryColor": "#F1F5F9",
    "tertiaryColor": "#FFE0F0",
    "lineColor": "#5A6B7D",
    "titleColor": "#0F172A",
    "clusterBkg": "#F1F5F9",
    "clusterBorder": "#E2E8F0",
    "actorBkg": "#FFFFFF",
    "actorBorder": "#E2E8F0",
    "actorTextColor": "#0F172A",
    "actorLineColor": "#CBD5E1",
    "signalColor": "#D6006F",
    "signalTextColor": "#0F172A",
    "labelBoxBkgColor": "#F1F5F9",
    "labelBoxBorderColor": "#E2E8F0",
    "labelTextColor": "#0F172A",
    "loopTextColor": "#0F172A",
    "noteBkgColor": "#F1F5F9",
    "noteBorderColor": "#CBD5E1",
    "noteTextColor": "#0F172A",
    "activationBkgColor": "#E3F6F9",
    "activationBorderColor": "#007C91",
    "sequenceNumberColor": "#D6006F",
    "arrowheadColor": "#D6006F",
    "fontFamily": "Inter, Segoe UI, Helvetica Neue, Arial, sans-serif",
    "fontSize": "15px"
  },
  "flowchart": {"curve": "basis", "nodeSpacing": 28, "rankSpacing": 36, "padding": 8}
}}%%
sequenceDiagram
    title A fatura espera pelas duas processadoras
    participant A as Processadora A
    participant B as Processadora B
    participant F as Fatura de julho

    Note over F: Em preparação
    Note over A: 31/07 · 23:50<br/>C-50 · R$ 100
    A->>F: C-50 processada às 00:20
    Note over A: Processou todas as chegadas até 00:30
    Note over F: Não emitir<br/>Aguardar a processadora B
    Note over B: 31/07 · 23:55<br/>C-51 · R$ 80
    B->>F: C-51 processada às 00:29
    Note over B: Processou todas as chegadas até 00:30
    Note over A,B: A e B processaram as<br/>chegadas até 00:30
    Note over F: Emitir a fatura

A processadora A alcança 00:30 antes da processadora B. Enquanto B estiver atrás, a fatura permanece aberta porque a fronteira comum é limitada pela entrada menos avançada:

fronteira comum = menor fronteira entre as entradas

Quando B também alcança 00:30, a fatura pode ser emitida. Trocar compras e avisos de progresso é comunicação; impedir o fechamento até que ambas satisfaçam a fronteira comum é coordenação.

O limite disponível permanece revisável e incorpora cada mudança assim que ela fica disponível. A fatura espera porque precisa transformar seu valor corrente em definitivo.

6. Aprofundamento teórico: como respostas evoluem e se tornam definitivas

Duas perguntas organizam as possibilidades de resposta:

  • Como ela evolui? Pode apenas acumular conclusões ou substituir as anteriores.
  • Que estabilidade ela oferece? Pode permanecer revisável ou assumir uma versão definitiva.

Monotonicidade responde à primeira pergunta. O teorema CALM (Consistency As Logical Monotonicity) usa essa propriedade para explicar por que uma resposta não monotônica e definitiva precisa de coordenação.

Monotonicidade: preservar ou substituir conclusões

Se eu aprender mais alguma coisa, precisarei retirar uma conclusão que já produzi?

Quando a resposta é não, a computação é monotônica. Quando é sim, ela é não monotônica.

A chegada de C-48 produz efeitos diferentes nas duas respostas:

RespostaAntes de C-48Depois de C-48Conclusão anterior
Compras observadasC-42, C-43 e C-45C-42, C-43, C-45 e C-48permanece
Limite disponívelR$ 890R$ 810é substituída

As compras observadas formam uma resposta monotônica porque conhecer C-48 não retira as compras anteriores. O limite disponível é não monotônico porque R$ 890 deixa de ser a resposta quando o valor passa a R$ 810. A fatura em preparação se comporta como o limite.

Podemos formalizar essa diferença representando como conjuntos tanto a informação conhecida quanto as conclusões produzidas. Chamamos a informação disponível em um momento de I e esse mesmo conhecimento acrescido de novos fatos de J. Portanto, I ⊆ J.

A condição de monotonicidade é:

I ⊆ J ⇒ f(I) ⊆ f(J)

Aqui, f é a transformação que produz a resposta, e significa “está contido em”:

  • I ⊆ J: J contém tudo que o pipeline sabia em I e possivelmente novos fatos;
  • f(I) ⊆ f(J): as conclusões produzidas a partir de I continuam presentes depois que f processa J.

Na primeira linha da tabela, f(I) ⊆ f(J). Na segunda, a condição não vale. Ser não monotônica, porém, não impede que uma resposta seja mantida incrementalmente por meio de correções.

CALM: quando avançar de forma independente não basta

Enquanto a processadora A já alcançou 00:30 e a processadora B ainda não, limite e fatura permanecem sujeitos à mesma incerteza: B ainda pode informar uma compra capaz de alterar seus valores. A diferença está na garantia de cada resposta:

Resposta não monotônicaGarantiaO que o pipeline pode fazer enquanto B está atrasada
limite disponívelrevisávelpublicar o valor atual e corrigi-lo depois
faturadefinitiva depois da emissãomanter em preparação e esperar por B

Uma execução é livre de coordenação quando cada parte pode avançar sem esperar que todas satisfaçam uma condição comum. O limite aceita esse avanço provisório; a emissão da fatura, não.

O teorema CALM formaliza o limite da execução sem coordenação. Nos modelos em que foi demonstrado:

computação monotônica

admite execução consistente sem coordenação

Consistente, aqui, significa que os mesmos fatos levam ao mesmo resultado, independentemente da ordem e da distribuição em que foram processados. Para o caso relevante do exemplo, a consequência é direta: quando uma resposta não monotônica precisa se tornar definitiva, é necessário coordenar.

A fatura continuou sendo mantida por deltas durante todo o ciclo. A coordenação apareceu apenas no fechamento e não exigiu um full load.

O Keeping CALM: When Distributed Consistency is Easy apresenta essa relação, e a prova de Ameloot, Neven e Van den Bussche demonstra a equivalência em um modelo formal específico.

7. Como decidir entre full load e processamento incremental

A escolha não se resume a verificar se uma ferramenta consegue ler menos linhas. Ela exige responder a três perguntas.

1. A mudança possui informação suficiente?

O pipeline precisa de uma chave estável, das informações que distinguem inserções, atualizações e exclusões e dos horários que identificam registros tardios. Quando uma mudança chega incompleta, ele também precisa conseguir formar o estado de reconstrução por chave.

Se a origem não fornece informação suficiente e o pipeline não consegue reconstruir o contexto a partir do que preservou, não há como derivar deltas corretos. Nesse caso, o full load pode ser a única reconstrução confiável.

2. A manutenção custa menos do que a reconstrução?

Ser incrementalizável não torna uma transformação automaticamente vantajosa. O benefício aparece quando o trabalho acompanha o tamanho da mudança, em vez do tamanho da base. Para avaliar isso, é preciso considerar o estado por chave preservado e o custo de atualizar cada agregação. No exemplo, SUM aplica o delta diretamente; MAX pode precisar percorrer todas as chaves válidas para encontrar um substituto.

Quando quase toda a base muda ou quando preservar e consultar essas informações custa tanto quanto recomputar, o full load continua sendo uma escolha coerente.

3. Que garantia o resultado precisa oferecer?

Um resultado pode avançar sem fechamento quando apenas acumula conclusões ou quando o consumidor aceita revisões. Um resultado definitivo precisa declarar quais dados pertencem ao cálculo, até quando serão aceitos e o que acontecerá com registros tardios. Se a computação for não monotônica e depender de entradas distribuídas, o fechamento também exigirá completude e coordenação.

O incremental faz sentido quando as mudanças podem ser interpretadas, manter o resultado custa menos do que reconstruí-lo e o pipeline consegue entregar a garantia esperada.

Quando não é possível reconstruir o contexto das mudanças ou o custo da manutenção se aproxima do custo de reler a base, o full load permanece coerente. Ele também continua útil para inicialização, reconciliação e recuperação.

A escolha, portanto, não é entre uma técnica antiga e outra moderna. É entre reconstruir o resultado ou assumir explicitamente as informações, correções e garantias necessárias para mantê-lo ao longo do tempo.