Módulo 11 · Engenharia de Software · Aula 12

Coleta e
Extração

Contrato da fonte, estratégias de captura e idempotência, com a construção de uma extração incremental sobre uma origem transacional que sofre atualização e exclusão.

Watermark CDC Idempotência Postgres · DuckDB

Computação 2 · Prof. Hermano Peixoto · 09/09/2026

OrigemBanco, API, arquivo, evento
contrato
ExtraçãoJanela, watermark, retry
estratégia
Controlebatch_id, rejeitados, replay
auditoria
BronzeFidelidade à origem
destino

Continuidade · da Aula 10 para a Aula 12

Com o lakehouse implantado na Aula 10, esta aula trata da entrada do dado

A Aula 10 decidiu onde e como o dado fica guardado. Esta aula trata de como ele entra, com que garantias e sob qual possibilidade de reprocessamento.

Aulas 2-4Modelagem dimensional
Aulas 8-9Governança de dados
Aula 10Armazenamento
Aula 12Coleta e extração
Aula 13Transformação e carga
A extração é a etapa do pipeline que depende diretamente de um sistema sobre o qual a equipe de dados não tem autoridade. Cada garantia que a origem não oferece precisa ser implementada pelo pipeline.

Roteiro · 120 minutos · uma hora de exposição, uma hora de prática

A primeira hora estabelece o critério, e a segunda constrói a extração em sala

A segunda hora é atividade em grupo: extrair de uma origem transacional que sofre atualização e exclusão, e medir o que cada estratégia perde.

1ª hora · Bloco 01
20 min

Contrato da fonte

A fronteira da extração, o perfil da origem e a semântica de atualização.

1ª hora · Bloco 02
25 min

Estratégias

Full load, incremental por watermark, CDC e idempotência.

1ª hora · Bloco 03
15 min

APIs, arquivos e operação

Paginação, retry, chegada atômica, controle e replay.

2ª hora · Atividade
60 min

Card de trabalho

Extração incremental sobre Postgres, com mutações deliberadas que produzem divergência.

Objetivo: escolher a estratégia de extração pela semântica de atualização da origem, e provar a escolha medindo o que cada uma captura e o que deixa passar.

Bloco 1 · A fronteira

Na extração, a responsabilidade pelo dado passa à equipe de dados

Antes dessa fronteira, o dado obedece às regras de quem o produz. Depois dela, a responsabilidade pela completude e pela rastreabilidade é do pipeline.

origemSistema transacionalOtimizado para escrita e para o negócio, sem compromisso com a análise
extraçãoJanela e watermarkDecide o que ler, quando ler e o que fazer quando a leitura falha
destinoBronzeFidelidade à origem, com carimbo de ingestão e identificador de lote
Assimetria

A origem não foi projetada para leitura analítica

O sistema transacional otimiza a escrita e o atendimento ao usuário. A leitura analítica compete por recurso, e a origem pode limitar, atrasar ou recusar a extração sem aviso. A janela de disponibilidade integra o contrato.

Consequência

A completude exige verificação explícita

A extração que termina sem erro pode ter trazido metade dos registros. A verificação se faz por contagem reconciliada com a origem.

Tratar o encerramento sem exceção como prova de completude. Algumas APIs sinalizam o limite de requisições com página vazia e status 200, e o processo que não detecta esse caso termina normalmente com carga incompleta.

Bloco 1 · Perfil da fonte

O levantamento da origem antecede a primeira linha de código

Nove atributos determinam a estratégia de extração. O levantamento posterior à implementação tende a exigir a reescrita do pipeline.

1

Responsável pela origem e canal de comunicação em caso de mudança.

2

Chave natural que identifica o registro de forma estável ao longo do tempo.

3

Semântica de atualização: a origem corrige a linha, ou insere uma nova?

4

Exclusão física ou lógica, e como ela é observável de fora.

5

Timezone dos carimbos de tempo e se há horário de verão na série.

6

Janela de disponibilidade: quando a origem aceita carga de leitura.

7

Limites de requisição, de volume por página e de tempo de conexão.

8

Campos sensíveis, classificados antes da primeira extração.

9

Volume e crescimento, que determinam se o full load continua viável.

Assumir a existência de um campo de atualização confiável. Muitas origens alteram a linha por rotina administrativa sem tocar nesse campo, e a alteração nunca chega ao destino.

Bloco 1 · Semântica de atualização

O comportamento da origem diante da correção define o que é possível capturar

A origem pode apresentar três comportamentos, cada um com consequência distinta para a estratégia de extração.

Somente inserção

A origem nunca altera o passado

Cada fato gera uma linha nova, e a correção entra como novo registro. É o caso mais simples: o incremental por carimbo de criação captura todas as inserções, desde que a janela tenha sobreposição para registros de visibilidade tardia.

Estratégia: incremental por timestamp de criação.

Atualização no lugar

A origem corrige a própria linha

O registro muda de estado sem mudar de identidade. A captura depende de um campo de atualização que a origem mantenha de forma disciplinada em toda escrita.

Estratégia: incremental por timestamp de atualização, com verificação periódica.

Exclusão física

A origem remove a linha

O registro desaparece sem deixar rastro consultável. Nenhuma consulta ao estado atual revela o que existia antes, porque não há o que ler.

Estratégia: captura pelo log de transações, ou reconciliação periódica de chaves.

Pergunta de projeto: na origem do parceiro do projeto, o cancelamento de um pedido produz uma linha nova, altera a linha existente ou a remove? A resposta determina qual das três estratégias é admissível, e as três não são intercambiáveis.

Bloco 1 · Tipos de origem

Cada tipo de origem impõe um contrato e um modo de falha

O que se negocia com quem mantém a origem, e como a extração degrada quando o contrato não é cumprido.

OrigemO que precisa ser negociadoModo de falha característicoDefesa correspondente
Banco relacionalRéplica de leitura, janela e acesso ao logA consulta analítica concorre com a transação e é interrompidaExtrair da réplica, em janela acordada e com lote limitado
APILimite de requisições, paginação e contagem declaradaHTTP 429 sem espera, ou página vazia com status 200 interpretada como fimRespeitar Retry-After, aplicar retry com backoff e validar pela contagem declarada
ArquivoConvenção de nome, checksum e sinal de conclusãoLeitura do arquivo enquanto ele ainda está sendo gravadoSeparar landing de processamento e mover após o checksum
EventoSchema registrado, retenção e garantia de entregaReentrega do mesmo evento após falha parcialIngestão idempotente por chave natural e janela
O contrato da fonte é documento de projeto, versionado e acessível a toda a equipe.

Bloco 2 · Estratégias de extração

A semântica de atualização da origem determina as estratégias de extração admissíveis

O volume determina o custo e a semântica de atualização determina a corretude; entre as estratégias corretas, escolhe-se a de menor custo.

Full load

custo alto · corretude alta

Como funciona
Lê a origem inteira a cada execução e substitui o destino por completo.
Captura
Inserção, atualização e exclusão, sem exigir campo de controle.
Limite
O custo cresce com o acervo, e a janela de origem pode não comportá-lo.
Use quando
O volume permite, ou como reconciliação periódica de outra estratégia.

Incremental por carimbo

custo baixo · corretude condicional

Como funciona
Lê o que mudou desde a última marca de tempo processada com sucesso.
Captura
Inserção e atualização, desde que a origem mantenha o campo com disciplina.
Limite
A exclusão física nunca chega, e o destino diverge de forma permanente.
Use quando
A origem só insere, ou mantém o campo de atualização de forma verificada.

Captura pelo log

custo médio · corretude alta

Como funciona
Lê o log de transações da origem e recebe cada operação como evento.
Captura
Inserção, atualização e exclusão, com a ordem em que ocorreram.
Limite
Exige retenção do log na origem, acesso privilegiado e garantia de ordenação.
Use quando
Exclusões e correções retroativas alteram a resposta analítica.
Regra de decisão: a origem que apaga registros exclui o incremental por carimbo de tempo como estratégia única. A alternativa admissível é a captura pelo log, ou a reconciliação periódica por full load.

Bloco 2 · Watermark

A marca d'água registra até onde a extração chegou com sucesso

É um valor persistido, avançado apenas quando o lote inteiro conclui. A janela seguinte parte dele, com sobreposição deliberada.

09:00lote anterior
10:00lote anterior
11:00sobreposição
12:00lote atual
13:00evento atrasado
excluído na origem
capturado pela janela chega depois da marca e exige sobreposição jamais capturado por esta estratégia
Sobreposição

A janela seguinte recobre o fim da anterior

O evento gravado na origem no instante da virada da janela pode não estar visível quando a extração lê. A sobreposição de alguns minutos recupera esse registro, e a deduplicação por chave natural remove a repetição resultante.

Avanço condicionado

A marca avança apenas com o lote concluído

Avançar a marca antes de a gravação concluir cria uma lacuna permanente: a janela seguinte parte de um ponto cujo conteúdo nunca foi persistido, e nenhuma execução posterior o alcança.

Avançar a marca d'água no início do lote. Uma falha no meio da gravação produz lacuna permanente, porque a janela seguinte parte de ponto posterior ao último registro gravado.

Bloco 2 · Divergência

O incremental por carimbo diverge de três formas distintas

As três produzem destino incorreto sem emitir erro, o que dificulta sua detecção em produção.

Evento na origemO que a extração observaEfeito no destinoCorreção
Exclusão físicaA linha desaparece, sem alteração de carimboO registro permanece no destino indefinidamenteCaptura pelo log, ou reconciliação de chaves por anti-junção
Atualização sem carimboNada, porque o campo de controle não se moveuO destino guarda o valor anterior como se fosse o vigenteGatilho na origem, ou reconciliação periódica por full load
Escrita retroativaUm registro com carimbo anterior à marca d'águaO registro nunca entra, porque a janela já passou daquele pontoFiltrar pelo campo de atualização mantido pela origem, com sobreposição, e reconciliar periodicamente
A divergência é cumulativa. Cada execução acrescenta registros ao desvio, e nenhuma execução posterior o corrige por conta própria.
A defesa mínima é a reconciliação: comparar periodicamente a contagem e a soma de uma medida entre origem e destino, e declarar a tolerância aceita.

Bloco 2 · Captura pelo log

O log de transações registra a operação, e não apenas o estado final

O banco já grava cada escrita em um log para garantir durabilidade e recuperação. A captura de mudanças lê esse mesmo log.

-- o que a consulta ao estado atual devolve SELECT * FROM pedido WHERE id = 4711; -- (nenhuma linha: foi excluído) -- o que o log de transações preservou {op: "c", id: 4711, status: "criado", lsn: 88123} {op: "u", id: 4711, status: "pago", lsn: 88470} {op: "d", id: 4711, anterior: {...}, lsn: 88902}
Consequência analítica: a exclusão passa a ser um fato datado e auditável. O destino registra que o pedido existiu, quanto tempo permaneceu e quando deixou de existir.

Confirme a retenção do log na origem: ela define a janela máxima de replay.

Garanta ordenação por chave: as operações do mesmo registro precisam chegar em ordem.

Trate o consumo como ao menos uma vez e torne a aplicação idempotente.

Planeje a carga inicial: o log registra as alterações posteriores ao início da captura, e o acervo anterior exige instantâneo.

Negocie o acesso privilegiado: a leitura do log exige permissão que o DBA controla.

Monitore o atraso de consumo: no PostgreSQL, o slot de replicação retém o WAL não consumido e pode esgotar o disco da origem; em logs com retenção fixa, o trecho expirado gera lacuna.

Iniciar a captura pelo log sem a carga inicial correspondente. O destino passa a conter apenas os registros alterados a partir daquele instante, e o acervo anterior permanece ausente.

Bloco 2 · Idempotência

A mesma janela reprocessada produz o mesmo destino

A idempotência torna o replay seguro e permite corrigir falhas parciais por reexecução.

-- carga idempotente por chave natural MERGE INTO bronze.pedido d USING ( SELECT * FROM extraido QUALIFY row_number() OVER ( PARTITION BY order_id ORDER BY atualizado_em DESC, ingerido_em DESC) = 1 ) o ON d.order_id = o.order_id WHEN MATCHED THEN UPDATE SET ... WHEN NOT MATCHED THEN INSERT ...;
Três elementos

Chave natural, ordenação e desempate

A chave identifica o registro, a ordenação decide qual versão prevalece e a regra de desempate resolve o empate de forma determinística. Sem os três declarados, duas execuções da mesma janela produzem resultados distintos.

Auditoria

batch_id e marca d'água persistidos

Cada execução recebe um identificador, e o registro guarda de qual lote veio. O reprocessamento se torna uma operação declarada, com efeito conhecido antes de ser executada.

Confiar em exclusão seguida de inserção fora de transação. A falha entre as duas operações deixa a janela ausente do destino, e a consulta executada nesse intervalo devolve resultado incorreto sem erro.

Bloco 3 · APIs

A resposta bem-sucedida não constitui prova de completude

O status 200 indica que a requisição foi atendida e não informa quantos registros eram esperados.

Paginação

O fim da paginação exige sinal explícito

Encerre a paginação pela contagem total declarada ou pelo cursor de continuação. Algumas APIs sinalizam o limite de requisições com página vazia e status 200, caso que a extração precisa detectar.

Limite e retry

Recuo exponencial com teto

O limite é sinalizado por HTTP 429 (Too Many Requests), em geral com o cabeçalho Retry-After. Repetem-se apenas erros transitórios (429 e 5xx), com intervalo crescente e variação aleatória, respeitando o Retry-After.

Resposta original

Persistência da resposta original

Grave a resposta como veio, antes de qualquer conversão. Quando o formato mudar ou a interpretação se revelar errada, o reprocessamento parte do original em vez de exigir nova extração.

Checkpoint: persista o cursor a cada página concluída. A extração interrompida na página 400 de 900 retoma a partir da página 401.
Encerrar a paginação no primeiro retorno vazio. Quando a API sinaliza o limite com página vazia e status 200, em vez de 429, a extração termina incompleta relatando sucesso.

Bloco 3 · Arquivos

O arquivo precisa estar completo antes de ser considerado disponível

A chegada de um arquivo é um processo com duração, e a leitura durante esse intervalo produz carga parcial que se apresenta como bem-sucedida.

Separe landing de processamento, e mova o arquivo apenas após verificar o checksum.

Exija sinal de conclusão: arquivo auxiliar ou renomeação atômica ao terminar a gravação.

Valide encoding, delimitador e schema antes de processar, e rejeite com motivo registrado.

Detecte reenvio pelo checksum: o mesmo arquivo entregue duas vezes não deve duplicar o destino.

Trate coluna nova como evento de governança, com decisão registrada e não como falha.

Schema drift

A origem muda o arquivo sem avisar

Uma coluna acrescentada, renomeada ou reordenada altera a interpretação de todo o arquivo. Na leitura posicional, o valor de uma coluna passa a ser lido como se fosse de outra, e a carga conclui sem erro.

Defesa: leitura por nome de coluna, validação do schema contra o contrato registrado e alerta quando a diferença aparece.

Ler o arquivo enquanto ele ainda está sendo gravado. A carga sai parcial, o processo relata sucesso e a diferença só aparece na conferência de fechamento.

Bloco 3 · Operação

O registro de execução é o que torna o reprocessamento possível

A tabela de controle transforma a extração em operação auditável, com estado consultável e replay previsível.

-- tabela de controle da extração CREATE TABLE ctl.extracao ( batch_id UUID, -- identifica a execução origem VARCHAR, -- qual fonte janela_inicio TIMESTAMP, -- de onde leu janela_fim TIMESTAMP, -- até onde leu watermark TIMESTAMP, -- marca avançada ao concluir linhas_lidas BIGINT, bytes BIGINT, rejeitadas BIGINT, destino_rejeitos VARCHAR, status VARCHAR, -- concluido | falha | parcial iniciado_em TIMESTAMP, concluido_em TIMESTAMP, erro VARCHAR );
O registro rejeitado permanece acessível com o dado original e o motivo. Sem ele não há como reprocessar nem como explicar a diferença ao time de negócio.
O procedimento de replay deve ser documentado e executado ao menos uma vez fora de incidente, para validar sua eficácia.

Segunda hora · Card de trabalho em sala

Três perguntas medidas sobre uma origem que muda em sala

Em grupo, sobre a origem montada em sala: extrair os pedidos, aplicar mutações deliberadas na origem e medir o que cada estratégia deixou de capturar.

1

Quantas linhas o incremental deixou de capturar?

Compare o destino do incremental com o de um full load posterior às mutações. A diferença deve ser quantificada e explicada.

2

Quantos excluídos permaneceram no destino?

Identifique por anti-junção as chaves do destino ausentes na origem e some a receita afetada a partir de order_payments.

3

Reprocessar a janela altera o destino?

Execute a mesma janela duas vezes e compare contagem e soma de verificação. A carga é idempotente quando ambas coincidem.

10minOrigem no ar e carregada
10minFull load de referência
15minIncremental por marca
15minMutações e divergência
10minReconciliação e replay

Segunda hora · Arquitetura

O Postgres como origem transacional e o DuckDB como extrator

O lakehouse da Aula 10 permanece como destino. O que se acrescenta é uma origem viva, que sofre inserção, atualização e exclusão durante o encontro.

origemPostgres · lojaTabela de pedidos com carimbo de atualização, sujeita a mutação em sala
extraçãoDuckDB · postgresAnexa a origem, aplica a janela e grava o lote com batch_id em bronze.pedido
destinoBronze · MinIOTabela bronze.pedido com chave primária e cópia Parquet no bucket lakehouse
Justificativa do Postgres

A origem precisa poder mudar durante a aula

O arquivo CSV é imutável e não permite observar o que acontece quando a origem corrige e apaga registros. O banco transacional torna a divergência reproduzível e mensurável em sala.

Continuidade

O destino é o mesmo da Aula 10

O bucket, a convenção de prefixo e o formato Parquet permanecem. A aula acrescenta a tabela bronze.pedido, a camada de controle e o carimbo de lote, sem refazer o ambiente.

Segunda hora · Preparação · 10 minutos

A origem transacional no ar e o destino preparado

Um container, a credencial do MinIO recriada na sessão e as tabelas do destino.

# 1. origem transacional docker run -d --name origem -p 5432:5432 \ -e POSTGRES_PASSWORD=olist \ -e POSTGRES_DB=loja postgres:16 # 2. MinIO da Aula 10 continua no ar docker start minio # 3. motor duckdb lakehouse.duckdb
-- 4. extensões e secret (o da Aula 10 não persiste na sessão) INSTALL postgres; LOAD postgres; INSTALL httpfs; LOAD httpfs; CREATE OR REPLACE SECRET minio (TYPE s3, KEY_ID 'minioadmin', SECRET 'minioadmin', ENDPOINT 'localhost:9000', URL_STYLE 'path', USE_SSL false); -- 5. anexar a origem e carregar os pedidos ATTACH 'host=localhost dbname=loja user=postgres password=olist' AS origem (TYPE postgres); CREATE TABLE origem.pedido AS SELECT order_id, customer_id, order_status, CAST(order_purchase_timestamp AS TIMESTAMP) AS comprado_em, CAST(order_purchase_timestamp AS TIMESTAMP) AS atualizado_em FROM read_csv_auto('olist/olist_orders_dataset.csv'); -- 6. destino: schemas e bronze com chave primária CREATE SCHEMA IF NOT EXISTS bronze; CREATE SCHEMA IF NOT EXISTS stg; CREATE SCHEMA IF NOT EXISTS ctl; CREATE TABLE bronze.pedido (order_id VARCHAR PRIMARY KEY, customer_id VARCHAR, order_status VARCHAR, comprado_em TIMESTAMP, atualizado_em TIMESTAMP, batch_id UUID, ingerido_em TIMESTAMP, excluido_em TIMESTAMP);
Verificação de partida: crie ctl.extracao com o DDL da tabela de controle; SELECT count(*) FROM origem.pedido deve devolver 99 441, referência de todas as comparações.

Segunda hora · Full load · 10 minutos

A extração completa estabelece a referência de comparação

Executada uma vez, antes de qualquer mutação, ela define o estado correto contra o qual as estratégias seguintes serão medidas.

-- full load: materializa bronze.pedido com carimbo de lote SET VARIABLE lote = uuid(); INSERT OR REPLACE INTO bronze.pedido SELECT order_id, customer_id, order_status, comprado_em, atualizado_em, getvariable('lote'), now(), NULL FROM origem.pedido; -- cópia Parquet da referência no bucket da Aula 10 COPY bronze.pedido TO 's3://lakehouse/bronze/pedido_full.parquet' (FORMAT parquet, COMPRESSION zstd); -- registra a execução na tabela de controle INSERT INTO ctl.extracao SELECT getvariable('lote'), 'postgres.pedido', NULL, now(), max(atualizado_em), count(*), 0, 0, NULL, 'concluido', now(), now(), NULL FROM origem.pedido;
Anote: a contagem gravada, o tempo de execução e o tamanho do arquivo no bucket. Os três reaparecem na comparação final.
A marca d'água nasce aqui, como o maior valor de atualizado_em observado. Toda janela incremental parte deste ponto.

Segunda hora · Incremental · 15 minutos

A extração incremental lê apenas o que mudou desde a última marca

Cada grupo implementa a janela, a sobreposição e o avanço condicionado da marca d'água.

-- 1. marca d'água da última execução concluída e novo lote SET VARIABLE marca = (SELECT max(watermark) FROM ctl.extracao WHERE status = 'concluido'); SET VARIABLE lote = uuid(); -- 2. janela pelo campo de atualização da origem, com sobreposição de 5 minutos CREATE OR REPLACE TABLE stg.lote AS SELECT * FROM origem.pedido WHERE atualizado_em > getvariable('marca') - INTERVAL 5 MINUTE; -- 3. aplica de forma idempotente pela chave primária order_id INSERT OR REPLACE INTO bronze.pedido SELECT order_id, customer_id, order_status, comprado_em, atualizado_em, getvariable('lote'), now(), NULL FROM stg.lote QUALIFY row_number() OVER (PARTITION BY order_id ORDER BY atualizado_em DESC, order_status) = 1; -- 4. só agora a marca avança, com o lote já persistido INSERT INTO ctl.extracao SELECT getvariable('lote'), 'postgres.pedido', getvariable('marca'), now(), coalesce(max(atualizado_em), getvariable('marca')), count(*), 0, 0, NULL, 'concluido', now(), now(), NULL FROM stg.lote;
Inverter a ordem dos passos 3 e 4. A marca avançada antes da gravação transforma qualquer falha em lacuna permanente, porque a janela seguinte já parte do ponto posterior.

Segunda hora · Mutações · 15 minutos

O incremental por carimbo captura apenas a atualização que move o carimbo

Execute o bloco no DuckDB, que o aplica à origem anexada, e rode o incremental de novo. Os três conjuntos de pedidos são disjuntos.

-- A. atualização disciplinada: move o carimbo (500 pedidos) UPDATE origem.pedido SET order_status = 'canceled', atualizado_em = now() WHERE order_id IN (SELECT order_id FROM origem.pedido WHERE order_status = 'delivered' ORDER BY order_id LIMIT 500); -- B. atualização sem disciplina: NÃO move o carimbo (300 pedidos) UPDATE origem.pedido SET order_status = 'unavailable' WHERE order_id IN (SELECT order_id FROM origem.pedido WHERE order_status = 'delivered' ORDER BY order_id LIMIT 300 OFFSET 1000); -- C. exclusão física: a linha deixa de existir (200 pedidos) DELETE FROM origem.pedido WHERE order_id IN (SELECT order_id FROM origem.pedido WHERE order_status = 'delivered' ORDER BY order_id LIMIT 200 OFFSET 5000);
Caso A · 500 · capturado

O carimbo se moveu e a janela alcança as linhas.

Caso B · 300 · não observado

O carimbo não se moveu, e a extração não percebe a mudança.

Caso C · 200 · jamais capturado

A linha não existe mais na origem para ser lida.

Medida: origem com 99 241 pedidos e destino com 99 441. A contagem revela os 200 excluídos; os 300 divergentes só aparecem na comparação de estado. Os 500 registros incorretos devem ser explicados por escrito.

Segunda hora · Reconciliação e replay · 10 minutos

A reconciliação por chave revela o que a janela não alcança

Sem acesso ao log de transações, a defesa disponível é comparar periodicamente o conjunto de chaves da origem com o do destino.

-- 1. chaves que sumiram da origem: exclusões nunca capturadas SELECT count(*) AS excluidos_nao_propagados FROM bronze.pedido d ANTI JOIN origem.pedido o USING (order_id); -- 2. linhas cujo estado diverge: atualização sem carimbo SELECT count(*) AS estado_divergente FROM bronze.pedido d JOIN origem.pedido o USING (order_id) WHERE d.order_status <> o.order_status; -- 3. marcação lógica da exclusão no destino UPDATE bronze.pedido SET excluido_em = now() WHERE excluido_em IS NULL AND order_id NOT IN (SELECT order_id FROM origem.pedido);
Decisão a registrar

Exclusão marcada ou removida

Marcar a exclusão preserva o histórico e permite responder desde quando o pedido deixou de existir. Remover a linha alinha o destino ao estado atual e apaga a informação. A escolha cabe à área de negócio e deve ser registrada.

Custo

A reconciliação lê a origem inteira

A comparação de chaves tem custo próximo ao do full load e, por isso, é executada periodicamente. A frequência se define pela tolerância a divergência declarada no requisito não funcional.

Espera-se 200 chaves ausentes na origem e 300 linhas com estado divergente, correspondentes às mutações B e C.

Segunda hora · Replay

A mesma janela executada duas vezes produz o mesmo destino

A verificação encerra o laboratório e demonstra que a extração pode ser reprocessada sem intervenção manual.

-- contagem e soma de verificação antes do replay SELECT count(*) AS antes, sum(hash(order_id, order_status)) AS verificacao FROM bronze.pedido; -- reexecuta a MESMA janela: a variável marca permanece a da execução anterior SET VARIABLE lote = uuid(); -- (repetir os passos 2, 3 e 4 do slide do incremental, sem o passo 1) -- depois: os dois números precisam ser idênticos SELECT count(*) AS depois, sum(hash(order_id, order_status)) AS verificacao FROM bronze.pedido; -- e a auditoria registra duas execuções distintas SELECT batch_id, janela_inicio, linhas_lidas, concluido_em FROM ctl.extracao ORDER BY concluido_em DESC LIMIT 2;
Critério de aceite: contagem e soma de verificação permanecem iguais, e a tabela de controle registra dois lotes com identificadores distintos.
Aumento da contagem indica chave de deduplicação incorreta ou ausente; alteração da soma de verificação indica aplicação não determinística.

Segunda hora · Conferência

Os valores de referência para conferir a extração

Números esperados em cada etapa, sobre a tabela de pedidos da Olist com as mutações aplicadas na ordem A, B e C.

EtapaO que verificarReferência
Origem carregadaContagem na tabela do Postgres99 441 pedidos, idênticos ao CSV
Full loadContagem gravada em bronze.pedido99 441, com um único batch_id
Após as mutaçõesContagem na origem99 241, com 200 excluídos fisicamente
IncrementalLinhas trazidas pela janela501: os 500 do caso A e uma linha da sobreposição
ReconciliaçãoAnti-junção e comparação de estado200 chaves ausentes e 300 estados divergentes
Receita afetadaPagamentos dos pedidos excluídos ou divergentes500 pedidos, R$ 76 317,52
ReplayContagem e soma de verificação antes e depoisIdênticas, com dois lotes na tabela de controle
As contagens independem da ordem das mutações; o valor de receita corresponde à ordem A, B e C, porque cada mutação desloca os pedidos delivered disponíveis para a seguinte. Exige-se que a sequência executada esteja registrada junto do resultado.

Segunda hora · Uso de IA · em paralelo

A IA escreve a extração, e o grupo responde pela reconciliação

O assistente acelera a escrita do SQL e do controle. A conferência dos números permanece atribuição do grupo e determina a correção do resultado.

Prompt 1 · Contrato

Levantar antes de extrair

"Este é o schema da tabela de pedidos. Liste as perguntas que preciso responder sobre a origem antes de escolher entre full load, incremental por carimbo e captura pelo log."

Prompt 2 · Geração

Escrever a janela e o controle

"Escreva SQL DuckDB que extraia do Postgres anexado apenas o que mudou desde a última marca d'água, com sobreposição de cinco minutos, aplicação idempotente e registro em tabela de controle."

Prompt 3 · Crítica

Procurar a perda silenciosa

"Aponte, neste pipeline, o que se perde quando a origem apaga uma linha, o que acontece se a execução falhar depois de gravar metade e o que quebra se a mesma janela rodar duas vezes."

Confira a contagem em cada etapa e explique cada diferença antes de seguir.

Verifique se o SQL gerado avança a marca d'água antes ou depois da gravação.

Registre o prompt junto do SQL, uma vez que ele integra o registro da decisão.

Segunda hora · Medição

As evidências que cada grupo mede e registra

Cada evidência corresponde a um valor obtido por execução.

EvidênciaComo obterO que ela demonstra
Custo de cada estratégiaTempo e linhas lidas do full load contra o incrementalQuanto a janela economiza em leitura da origem
Divergência acumuladaAnti-junção entre destino e origem após as mutaçõesQue a perda é silenciosa e cumulativa
Efeito no negócioSoma de payment_value (order_payments) dos pedidos excluídos ou divergentesQue a divergência técnica altera o número apresentado
IdempotênciaContagem antes e depois do replay da mesma janelaQue a reentrega não duplica o dado
AuditoriaLinhas em ctl.extracao ao final do encontroQue cada execução é rastreável e reproduzível
-- receita dos pedidos excluídos ou divergentes no destino SELECT count(*) AS pedidos, round(sum(p.valor), 2) AS receita_afetada FROM bronze.pedido d LEFT JOIN origem.pedido o USING (order_id) LEFT JOIN (SELECT order_id, sum(payment_value) AS valor FROM read_csv_auto('olist/olist_order_payments_dataset.csv') GROUP BY order_id) p USING (order_id) WHERE o.order_id IS NULL OR d.order_status <> o.order_status;
A diferença de 500 pedidos em 99 441 (0,5%) é convertida em receita para avaliar o efeito sobre o número de fechamento.

Ponte com a Aula 1 · Spec-Driven Development

Do tema à especificação executável

A decisão de extração entra no projeto como requisito, decisão registrada e critério verificável.

RF-005

Requisito funcional

Ingerir todas as alterações de pedidos da origem, inclusive exclusões e correções retroativas.

RNF-005

Requisito não funcional

Atraso da marca d'água menor ou igual a uma hora; taxa de rejeição até 0,1%; qualquer janela dos últimos 30 dias reprocessável sem intervenção manual.

ADR-ING-01

Decisão de arquitetura

Captura pelo log de transações em vez de incremental por campo de atualização. Contexto: a origem apaga registros e corrige lançamentos com data retroativa. Consequência: exige retenção do log e garantia de ordenação.

Cenário de aceite

Critério verificável

Dado que o lote de 09/09/2026 já foi carregado, quando reprocesso o mesmo identificador de lote, então o volume no destino permanece igual e a auditoria registra duas execuções.

A escolha entre incremental e captura pelo log é decisão de arquitetura com consequência de custo e de acesso. Registrá-la evita que a próxima equipe a refaça sem conhecer o motivo.

Entrega do card de trabalho

A extração versionada, os três números e a decisão registrada

O que o grupo entrega ao final da segunda hora, e o que será conferido.

Artefato

Pipeline de extração e o relato da divergência

O SQL da carga inicial, da janela incremental e da reconciliação; os três números medidos e a receita afetada; e uma frase por número que declare o efeito sobre a decisão de negócio do parceiro.

1

A origem transacional está no ar e a contagem inicial coincide com a do CSV.

2

A tabela de controle registra cada execução com lote, janela, marca d'água e status.

3

A marca d'água avança somente após a gravação do lote concluir.

4

Os três casos de mutação foram aplicados, e a divergência está medida em contagem e em receita.

5

O replay da mesma janela mantém contagem e soma de verificação e registra duas execuções distintas.

6

A decisão sobre exclusão marcada ou removida está declarada por escrito.

Aula 12 · Síntese

A estratégia de extração determina quais mudanças o destino é capaz de observar, e a reconciliação determina em quanto tempo a divergência é descoberta.

Debezium — Change Data Capture Google SRE — Handling overload RFC 9110 — HTTP Semantics RFC 6585 — 429 Too Many Requests DuckDB — Postgres extension
Pergunta final: na origem do parceiro, quantos dias a divergência levaria para ser percebida sem reconciliação?

Sobre este encontro

Coleta e Extração · 09/09/2026 · Prof. Hermano

Objetivo de aprendizagem

Levantar o contrato de uma fonte, escolher entre full load, incremental por carimbo de tempo e captura pelo log a partir da semântica de atualização da origem, e construir uma extração idempotente cuja divergência seja medida e explicada.

Estratégia do encontro

Primeira hora de exposição dialogada em três blocos, cada um encerrado por checklist de aplicação e erro comum. Segunda hora de atividade em grupo: os estudantes montam uma origem transacional em Postgres com os pedidos da Olist, extraem por full load e por janela incremental para o lakehouse da Aula 10, aplicam mutações deliberadas de atualização e exclusão na origem e medem exatamente o que cada estratégia deixou de capturar.

Estrutura do encontro

  1. 1ª hora · Bloco 1 (20 min) — Contrato da fonte: a fronteira da extração, o perfil da origem, a semântica de atualização e os quatro tipos de origem com seus modos de falha.
  2. 1ª hora · Bloco 2 (25 min) — Estratégias: full load, incremental por marca d'água e captura pelo log, as três formas de divergência do incremental e a idempotência como condição do replay.
  3. 1ª hora · Bloco 3 (15 min) — APIs, arquivos e operação: paginação, recuo exponencial, chegada atômica, schema drift, tabela de controle e procedimento de replay.
  4. 2ª hora · Card de trabalho (60 min) — Em grupo: origem no ar e carregada (10 min), full load de referência (10 min), incremental por marca d'água (15 min), mutações e medição da divergência (15 min), reconciliação, replay e conclusão escrita (10 min).