Lab 67 — Lakehouse: Iceberg, upsert e time travel
O problema, e a empresa que o tem
A Cadência resolveu, no L62, o problema de achar o dado certo: bronze, prata e ouro, cada camada com contrato escrito, catálogo alimentado pelos próprios jobs. O que ficou de fora daquele módulo — dito no próprio callout dele — é o problema oposto de achar dado: e se o dado que você achou estiver errado?
Em agosto de 2026, a conciliação fiscal mensal da Cadência encontrou o motivo: entre 22 e 24 de julho, um erro na tabela de alíquota do ERP aplicou 6,5% de ICMS-ST em vez de 12% para a categoria "cimento e argamassa", nas 12 lojas de Minas Gerais. O erro foi corrigido na fonte no dia 25 — mas os pedidos já processados naqueles três dias continuaram com o valor errado em cadencia_prata.pedidos, e propagados para o agregado diário em cadencia_ouro.vendas_diarias_por_loja. São 189 pedidos, uma fração pequena dos cerca de 42 mil pedidos do trimestre — pequena o bastante pra ninguém ter notado até o fechamento fiscal cruzar o valor declarado com o valor recalculado.
A tabela pedidos em prata é Parquet particionado por data_particao, alimentada pelo job do L62 com partitionOverwriteMode=dynamic. Esse modo já resolve reprocessar o MESMO dia sem duplicar linha — mas a granularidade da operação é a partição inteira: para corrigir 189 linhas específicas, o único caminho hoje é reexecutar o job para os três dias afetados, relendo e reescrevendo cerca de 14 mil pedidos por dia para mudar as poucas dezenas que de fato têm erro em cada um. Funciona, mas é caro, e tem um segundo problema que reprocessar não resolve: depois da correção, ninguém mais consegue provar qual foi o valor ORIGINALMENTE declarado — informação que a Secretaria da Fazenda de Minas Gerais pede exatamente quando uma empresa reporta um ajuste retroativo de imposto.
É a mesma pergunta do L37 — o que o leitor vê depois de uma escrita — só que invertida. Lá, o risco era um usuário não ver a própria escrita por causa de réplica atrasada, e a resposta era garantir leitura consistente logo depois de escrever. Aqui, o risco é o oposto: alguém precisa DELIBERADAMENTE continuar vendo o passado mesmo depois que o presente mudou, porque é o passado que a auditoria fiscal pediu. Um Parquet particionado simples não guarda os dois estados ao mesmo tempo — ele só tem o que está lá agora, e reescrever para corrigir apaga a única prova de como estava antes.
O que este laboratório NÃO faz
Este laboratório não troca o lake inteiro por Iceberg — só a tabela pedidos, que é a que precisa de UPDATE/DELETE pontual. Bronze continua imutável por convenção (L62) e ouro continua Parquet simples, reconstruída do zero a cada execução do job de agregação. Migrar tudo sem um requisito que justifique cada tabela é a armadilha que a seção de anti-padrões deste módulo nomeia.
Objetivos de aprendizagem
- Explicar por que Parquet particionado não suporta UPDATE/DELETE de uma linha sem reescrever a partição inteira.
- Migrar uma tabela de pedidos de Parquet/Hive para uma tabela Iceberg em S3 Tables, preservando o schema existente.
- Executar um UPDATE pontual numa linha histórica e medir quantos arquivos novos isso de fato grava.
- Consultar o estado da tabela ANTES da correção com time travel, por timestamp e por snapshot-id.
- Configurar a retenção de snapshot e a compaction da tabela, calibradas com um requisito de auditoria real, não com o padrão genérico.
- Restringir quem pode rodar UPDATE/DELETE por um papel de IAM isolado, mantendo leitura ampla para o resto do time.
- Provar, com CloudTrail, quem alterou qual pedido e quando.
- Recalcular a camada ouro afetada pela correção sem reprocessar o lake inteiro.
Onde isso cai na certificação
| Conceito | Certificação | Como aparece aqui | O que dominar |
|---|---|---|---|
| Tabela transacional (Iceberg) vs. tabela simples (Hive/Parquet) | MLA-C01 | pedidos migra de Parquet particionado para tabela Iceberg em S3 Tables | por que ACID em lakehouse não é o mesmo que ACID em banco transacional |
| Snapshot e commit atômico | MLA-C01 | cada UPDATE gera um novo snapshot; nenhum leitor vê estado parcial | como o metadata.json aponta para o snapshot vigente, e por que isso é o commit |
| Time travel | MLA-C01 | consulta com FOR SYSTEM_TIME AS OF reconstrói o estado anterior à correção | diferença entre consultar o passado e restaurar o passado |
| Merge-on-read vs. copy-on-write | MLA-C01, SAP-C02 | a correção grava um arquivo de delete e um arquivo de dado novo, sem tocar no antigo | trade-off de latência de escrita contra latência de leitura entre os dois modos |
| Compaction e expiração de snapshot | MLA-C01 | retenção calibrada com o time fiscal consolida delta e descarta snapshot vencido | por que Iceberg sem manutenção acumula custo e degrada leitura |
| Catálogo compatível com Iceberg | MLA-C01, SAP-C02 | Glue Data Catalog reconhece a tabela pedidos como ICEBERG, não como Hive tradicional | diferença entre o que um catálogo Hive exige e o que uma tabela Iceberg exige dele |
Requisitos: funcionais e não funcionais
A terceira coluna abaixo não é enfeite: cada peça nova da arquitetura de produção (seção seguinte) rastreia a uma linha desta tabela. Requisito que não muda o desenho não devia estar aqui.
| Requisito | Valor | Como influencia a arquitetura |
|---|---|---|
| RF1 — corrigir pedido histórico sem reescrever a partição | UPDATE/DELETE pontual, por pedido_id | exige tabela com commit atômico e escrita incremental (merge-on-read) em vez de overwrite de partição inteira — troca Parquet simples por Iceberg |
| RF2 — provar o estado declarado antes da correção | consulta ao snapshot anterior, por timestamp ou snapshot-id | exige reter snapshots antigos por um prazo mínimo combinado com o fiscal, e um catálogo que resolve nome de tabela MAIS versão, não só nome de tabela |
| RNF1 — segurança: só um papel específico corrige | 1 papel de escrita restrito; leitura ampla para o resto do time | separa o papel de leitura já existente do L62 de um papel novo, escopado só a UPDATE/DELETE na tabela pedidos, sem herdar de nenhum outro |
| RNF2 — auditabilidade da correção retroativa | toda alteração rastreável: quem, quando, antes e depois | exige uma trilha de auditoria (CloudTrail) correlacionada ao snapshot resultante, não só um log de aplicação que pode ser apagado |
| RNF3 — custo de armazenamento sob controle | crescimento do bucket previsível mesmo com correções frequentes | exige compaction e expiração de snapshot configuradas na própria tabela, calibradas para não apagar o que a auditoria ainda vai pedir |
| RNF4 — o painel de BI não pode ficar defasado da correção | ouro reflete a correção em menos de 1 dia útil | exige que a correção em prata dispare o recomputo da partição afetada de ouro, e não só aguarde a próxima janela agendada — a mesma pergunta do L37 |
Arquitetura mínima: só o suficiente para corrigir uma linha
Esta é a arquitetura mínima: três serviços, uma tabela. Ela já resolve o problema declarado — corrigir 189 pedidos sem tocar nos outros 13.800 da mesma partição — e é nela que a diferença entre Parquet simples e Iceberg fica visível sem nenhum ruído em volta.
- → UPDATE pedidos SET valor_imposto, valor_total WHERE pedido_id IN (189 valores)
- → pede o metadata.json vigente da tabela pedidos
- → devolve o ponteiro do snapshot atual e a lista de manifests
- → lê os manifests do snapshot e localiza os arquivos que casam com o filtro
- → grava um arquivo de dado com o valor corrigido e um arquivo de delete para as 189 linhas antigas
- → os dois arquivos novos entram no manifest do snapshot seguinte
- → commit atômico: o catálogo passa a apontar para o metadata.json do novo snapshot
- Fora da AWS
- Analytics
- Armazenamento
A tabela Iceberg troca "reescrever a partição inteira" por "escrever dois arquivos pequenos e trocar um ponteiro" — a correção de 189 pedidos não toca em nenhuma das outras linhas do dia. Percorra os passos e repare que o commit é a ÚLTIMA seta: até ali, qualquer leitor concorrente ainda enxerga o snapshot antigo, intacto.
- O pedido de correção chega como SQL, não como reescrita de arquivo. O financeiro não edita Parquet — ele roda um UPDATE contra a tabela. É o Athena, entendendo o formato Iceberg, quem decide o que fisicamente muda no S3 a partir daqui.
- Antes de escrever qualquer coisa, o motor pergunta onde a tabela está agora. O Glue Data Catalog não guarda dado — guarda o endereço do metadata.json vigente. É esse arquivo que lista os manifests do snapshot atual.
- O catálogo responde com o ponteiro, não com os dados. A resposta é só a localização do snapshot vigente. Quem decide quais arquivos de dado interessam para o UPDATE ainda é o próximo passo.
- O manifest diz exatamente quais arquivos têm as linhas do filtro. Iceberg guarda estatística por arquivo de dado — faixa de valor de pedido_id, por exemplo. O motor descarta arquivos que claramente não têm nenhuma das 189 linhas, sem abri-los.
- A escrita é pequena: dois arquivos novos, zero arquivos reescritos. Merge-on-read grava um arquivo de dado só com as linhas corrigidas e um arquivo de delete marcando as antigas como removidas. O Parquet original da partição do dia continua exatamente como estava.
- Os arquivos novos entram no próximo snapshot, ainda não commitado. Existir no S3 não é a mesma coisa que estar visível: até o commit, qualquer leitor concorrente continua enxergando o snapshot anterior, sem as linhas corrigidas.
- O commit é a última seta: um ponteiro novo, não um arquivo reescrito. A troca do metadata.json no catálogo é atômica — antes dela, o mundo vê o snapshot antigo; depois, vê o novo. Não existe um meio-termo visível para ninguém.
Arquitetura de produção: quem pode escrever, e quem prova depois
A arquitetura de produção não muda O QUE a correção faz — o caminho de escrita é o mesmo da mínima. O que muda é tudo que precisa existir ANTES e AO REDOR dela: um papel isolado para quem escreve, uma chave para o que fica em repouso, e uma trilha para quem precisar provar depois o que aconteceu. Cada peça nova rastreia a uma linha da tabela de requisitos.
- → assume o papel de correção fiscal antes de qualquer escrita
- → credencial temporária, escopada só a UPDATE/DELETE na tabela pedidos
- → pede o metadata.json vigente da tabela pedidos
- → devolve o ponteiro do snapshot atual e a lista de manifests
- → lê os manifests do snapshot e localiza os arquivos que casam com o filtro
- → grava um arquivo de dado com o valor corrigido e um arquivo de delete para as 189 linhas antigas
- → arquivo cifrado com a chave do bucket antes de ficar durável
- → toda chamada de UPDATE ou DELETE grava evento de API com identidade e predicado
- → os dois arquivos novos entram no manifest do snapshot seguinte
- → commit atômico: o catálogo passa a apontar para o metadata.json do novo snapshot
- Fora da AWS
- Segurança e identidade
- Analytics
- Armazenamento
- Gestão e governança
O caminho de escrita (financeiro corrige um pedido) não mudou uma linha — compare com a arquitetura mínima. O que existe agora é tudo que uma correção FISCAL, especificamente, exige: papel que não herda de leitura, chave que protege inclusive o snapshot antigo, e uma trilha que sobrevive ao autor da correção ter esquecido o que fez.
- Financeiro assume o papel restrito, não a credencial de sempre. O papel de correção fiscal não herda de nenhum papel de leitura existente — RNF1. Se a política de leitura do L62 mudar amanhã, esse papel de escrita não muda junto.
- A credencial que chega ao motor só sabe corrigir pedidos. Não existe segredo estático para vazar aqui: a credencial vem de assumir um papel IAM, é temporária, e o escopo dela é uma tabela só.
- O motor resolve o ponteiro do snapshot vigente antes de tocar em qualquer arquivo. Igual à arquitetura mínima: o catálogo devolve só o endereço do metadata.json atual, nunca o dado.
- O manifest aponta só para os arquivos com as 189 linhas. A estatística por arquivo do Iceberg evita abrir arquivo que não tem nenhuma linha do filtro — mesmo mecanismo da mínima.
- A escrita grava dois arquivos pequenos, já cifrados — RNF de segurança. A chave do bucket cifra o arquivo de dado e o de delete assim que eles ficam duráveis. Isso vale para o snapshot NOVO e para os antigos retidos para auditoria — os dois ficam protegidos igual.
- Toda chamada de escrita vira evento auditável — RNF2. CloudTrail registra quem, quando e com qual predicado — a peça que faltava para provar, meses depois, que a correção foi feita por quem tinha o papel certo, e não por acidente de outro papel.
- O commit é atômico: um ponteiro novo substitui o antigo, e só ele muda. Igual à mínima — a diferença de produção não está nesta seta, está em tudo que precisou existir ANTES dela para a escrita ser segura e rastreável.
Como funciona ponta a ponta
{
"eventSource": "athena.amazonaws.com",
"eventName": "StartQueryExecution",
"eventTime": "2026-08-07T14:32:09Z",
"userIdentity": {
"type": "AssumedRole",
"arn": "arn:aws:sts::111111111111:assumed-role/cadencia-correcao-fiscal/cli-correcao"
},
"requestParameters": {
"queryString": "UPDATE pedidos SET valor_imposto = ?, valor_total = ? WHERE pedido_id IN (189 valores)",
"workGroup": "cadencia-correcao"
},
"responseElements": {
"queryExecutionId": "6f2c9e10-88a1-4b7e-9c31-a0d5e7f412ab"
}
}
A decisão central
📋 A tabela pedidos precisa aceitar correção pontual de linha histórica, sem reescrever a partição inteira, e sem perder a capacidade de provar o estado anterior à correção para uma auditoria fiscal externa.
Merge-on-read resolve o requisito funcional central — escrita pequena, sem tocar no que não mudou — e o modelo de snapshot do Iceberg resolve o requisito de auditoria de graça: cada correção já É um ponto no tempo consultável, sem precisar de um mecanismo de backup separado só para isso. S3 Tables entrega isso como serviço gerenciado, sem o time da Cadência (dois engenheiros de dados) ter que operar compaction e expiração de snapshot na mão.
Alt: Continuar em Parquet e reprocessar o dia inteiro a cada correção — É o caminho que já existe, sem aprender ferramenta nova — e é exatamente o que gerou o problema deste laboratório. Escala mal com a frequência de correção, e nunca resolve a prova do estado anterior: depois do reprocessamento, o valor antigo simplesmente não existe mais em lugar nenhum.
Alt: Deletar a linha e inserir de novo com o valor certo — Parece mais simples que montar a cláusula SET certa, mas perde qualquer campo que o INSERT novo não replicar exatamente — incluindo colunas que ninguém lembrou de copiar. Também não resolve a auditoria: um DELETE seguido de INSERT não deixa rastro de qual era o valor antigo, a não ser que já exista Iceberg por baixo, que é justamente o que está sendo decidido.
Alt: Manter Parquet e guardar a correção numa tabela separada de ajustes — Funciona para relatório financeiro que sabe somar os dois, mas duplica a lógica de leitura em todo consumidor do lake — cada dashboard, cada job, cada analista precisa lembrar de aplicar o ajuste por cima. É a mesma dívida que o L62 já resolveu ao proibir convenção sem aplicação técnica.
Alt: Migrar TODO o lake para Iceberg de uma vez, não só pedidos — Nenhum requisito desta seção pede isso. Bronze é imutável por natureza — não há UPDATE a suportar — e ouro é reconstruída do zero a cada execução. Migrar as duas sem um requisito que justifique é exatamente o anti-padrão que este módulo nomeia mais abaixo: peça sem requisito é peça por hábito.
Construir: a tabela pedidos em S3 Tables (Iceberg)
Um bucket S3 Tables novo, uma tabela Iceberg, e a integração nativa com o Glue Data Catalog — é só isso que muda em relação ao lake do L62. Bronze e ouro continuam exatamente onde estavam.
# s3tables.tf — a tabela pedidos migra para Iceberg; bronze e ouro NÃO mudam
resource "aws_s3tables_table_bucket" "pedidos" {
name = "${var.projeto}-pedidos-iceberg"
}
# Cifra em repouso: mesma decisão do L62 para o bucket bronze/prata/ouro, agora
# aplicada ao bucket de tabelas. Rotação habilitada desde a criação — trocar
# depois de criado é possível, mas exige uma segunda etapa que ninguém lembra
# de rodar se não vier junto.
resource "aws_kms_key" "pedidos_iceberg" {
description = "Chave do bucket S3 Tables — tabela pedidos (correção fiscal)"
enable_key_rotation = true
}
resource "aws_s3tables_table_bucket_policy" "pedidos" {
table_bucket_arn = aws_s3tables_table_bucket.pedidos.arn
resource_policy = data.aws_iam_policy_document.bucket_cifrado.json
}
resource "aws_s3tables_namespace" "cadencia_prata" {
table_bucket_arn = aws_s3tables_table_bucket.pedidos.arn
namespace = "cadencia_prata"
}
resource "aws_s3tables_table" "pedidos" {
table_bucket_arn = aws_s3tables_table_bucket.pedidos.arn
namespace = aws_s3tables_namespace.cadencia_prata.namespace
name = "pedidos"
format = "ICEBERG"
# Retenção de snapshot: NÃO é o padrão genérico do Iceberg. Foi calibrada com
# o time fiscal em 400 dias — cobre uma auditoria retroativa de até um ano
# fechado mais folga para o trimestre corrente. Reduzir isso sem conversar
# com o fiscal de novo é a forma mais fácil de perder uma prova que ninguém
# sabia que ia precisar.
maintenance_configuration {
iceberg_compaction {
status = "enabled"
target_file_size_mb = 128
}
iceberg_snapshot_management {
status = "enabled"
min_snapshots_to_keep = 10
max_snapshot_age_hours = 9600 # ~400 dias, combinado com o time fiscal
}
}
}
# Habilita a integração nativa do S3 Tables com o Glue Data Catalog — é isso
# que faz o Athena enxergar "pedidos" como uma tabela normal do catálogo, com
# table_type=ICEBERG, sem precisar de um catálogo Iceberg REST separado.
resource "aws_s3tables_table_bucket" "integracao_analytics" {
name = aws_s3tables_table_bucket.pedidos.name
# analytics_integration é habilitado por padrão para bucket criado após a
# disponibilidade da feature — deixado explícito aqui para não depender de
# comportamento implícito do provider.
}
Construir: o papel de IAM e a trilha de auditoria
O papel de correção fiscal não referencia nenhum papel existente do L62 — é assim que a RNF1 (segurança) vira Terraform, não só intenção.
# iam.tf — papel de correção fiscal, isolado do papel de leitura do L62
resource "aws_iam_role" "correcao_fiscal" {
name = "${var.projeto}-correcao-fiscal"
assume_role_policy = data.aws_iam_policy_document.assume_por_identity_center.json
# NAO referencia aws_iam_role.engenheiro_dados nem analista_negocio do L62 —
# nenhuma herança de policy. E o motivo pelo qual a RNF1 fica satisfeita:
# mudar a leitura não muda a escrita, e vice-versa.
}
data "aws_iam_policy_document" "correcao_fiscal" {
statement {
sid = "EscritaRestritaNaTabelaPedidos"
effect = "Allow"
actions = [
"s3tables:GetTableData",
"s3tables:PutTableData",
"s3tables:UpdateTableMetadataLocation",
"s3tables:GetTableMetadataLocation",
]
resources = [aws_s3tables_table.pedidos.arn]
}
statement {
sid = "CatalogoParaResolverATabela"
effect = "Allow"
actions = ["glue:GetTable", "glue:GetDatabase"]
# A ação de catálogo do Glue não aceita ARN de tabela Iceberg individual
# neste conjunto — é metadado de conta/região. O controle granular real
# está na policy do S3 Tables acima, não aqui.
resources = ["*"]
}
# NAO ha glue:DeleteTable, nem s3tables:DeleteTable, nem nenhuma acao em
# bronze ou ouro. O papel so alcanca exatamente a tabela que ele existe
# para corrigir.
}
resource "aws_iam_role_policy" "correcao_fiscal" {
role = aws_iam_role.correcao_fiscal.id
policy = data.aws_iam_policy_document.correcao_fiscal.json
}
# Trilha dedicada — não reaproveita a trilha de organização inteira do L54,
# para o alarme desta seção poder filtrar só por este papel sem ruído de
# tudo mais que acontece na conta.
resource "aws_cloudtrail" "correcao_fiscal" {
name = "${var.projeto}-correcao-fiscal-trail"
s3_bucket_name = aws_s3_bucket.trilha_auditoria.id
include_global_service_events = false
is_multi_region_trail = false
enable_log_file_validation = true
}
Construir: o CLI de correção em .NET 8
Ninguém no time fiscal roda SQL direto contra a tabela. O caminho aprovado é este CLI: lê um CSV já revisado por uma segunda pessoa, assume o papel restrito, e só então monta e executa o UPDATE — em lotes pequenos, com retry com backoff e jitter para throttling do Athena, do mesmo jeito que o L36 já ensinou.
// CorrecaoFiscal.Cli — .NET 8. Único caminho aprovado para corrigir pedidos
// históricos. Não expõe SQL livre: lê um CSV revisado, valida contra um
// schema, e monta o UPDATE internamente.
using System.Globalization;
using Amazon.Athena;
using Amazon.Athena.Model;
using Amazon.SecurityToken;
using Amazon.SecurityToken.Model;
using Microsoft.Extensions.Configuration;
using Polly;
using Polly.Retry;
namespace Cadencia.CorrecaoFiscal.Cli;
public sealed record LinhaCorrecao(string PedidoId, decimal ValorImpostoCorreto, decimal ValorTotalCorreto);
public class Program
{
public static async Task<int> Main(string[] args)
{
var config = new ConfigurationBuilder()
.AddEnvironmentVariables("CADENCIA_")
.Build();
var caminhoCsv = args.Length > 0 ? args[0] : throw new ArgumentException(
"uso: correcao-fiscal <caminho-do-csv-revisado>");
var linhas = LerEValidar(caminhoCsv);
if (linhas.Count == 0)
{
Console.WriteLine("nenhuma linha valida no CSV — nada a corrigir");
return 1;
}
Console.WriteLine($"{linhas.Count} pedido(s) validado(s). Confirme (s/N): ");
if (Console.ReadLine()?.Trim().ToLowerInvariant() != "s")
{
Console.WriteLine("cancelado pelo operador — nenhuma escrita feita");
return 0;
}
using var sts = new AmazonSecurityTokenServiceClient();
var papel = await sts.AssumeRoleAsync(new AssumeRoleRequest
{
RoleArn = config["PapelCorrecaoFiscalArn"],
RoleSessionName = $"cli-correcao-{DateTimeOffset.UtcNow:yyyyMMddHHmmss}",
DurationSeconds = 900, // 15 min — só o tempo de rodar o lote
});
using var athena = new AmazonAthenaClient(
papel.Credentials.AccessKeyId, papel.Credentials.SecretAccessKey,
papel.Credentials.SessionToken);
// Retry com backoff E jitter — igual ao L36. Athena throttling num
// lote de centenas de UPDATE sem isso vira erro em cascata, não
// instabilidade tolerável.
AsyncRetryPolicy politica = Policy
.Handle<TooManyRequestsException>()
.WaitAndRetryAsync(5, tentativa =>
TimeSpan.FromSeconds(Math.Pow(2, tentativa))
+ TimeSpan.FromMilliseconds(Random.Shared.Next(0, 500)));
var executadas = 0;
foreach (var lote in linhas.Chunk(50)) // lote pequeno: menos risco por execução
{
var predicado = string.Join(",", lote.Select(l => $"'{l.PedidoId}'"));
var sql = MontarUpdate(lote);
var execucaoId = await politica.ExecuteAsync(async () =>
{
var resposta = await athena.StartQueryExecutionAsync(new StartQueryExecutionRequest
{
QueryString = sql,
WorkGroup = "cadencia-correcao",
QueryExecutionContext = new QueryExecutionContext { Database = "cadencia_prata" },
});
return resposta.QueryExecutionId;
});
Console.WriteLine($"lote de {lote.Length} pedido(s) — execucao {execucaoId}");
executadas += lote.Length;
}
Console.WriteLine($"concluido: {executadas} pedido(s) corrigido(s). " +
"CloudTrail registrou cada StartQueryExecution com a identidade assumida.");
return 0;
}
// Nunca interpola texto arbitrario do CSV no SQL — só numero (decimal
// validado) e o pedido_id, que já passou por um regex de formato antes
// de chegar aqui. É o que torna seguro montar o UPDATE sem parametro
// nomeado, que o protocolo do Athena não suporta neste ponto.
private static string MontarUpdate(IEnumerable<LinhaCorrecao> lote)
{
var casos = lote.Select(l =>
$"WHEN pedido_id = '{l.PedidoId}' THEN {l.ValorImpostoCorreto.ToString(CultureInfo.InvariantCulture)}");
var ids = lote.Select(l => $"'{l.PedidoId}'");
return "UPDATE pedidos SET valor_imposto = CASE " + string.Join(' ', casos) +
" END WHERE pedido_id IN (" + string.Join(',', ids) + ")";
}
private static List<LinhaCorrecao> LerEValidar(string caminho)
{
var resultado = new List<LinhaCorrecao>();
foreach (var linha in File.ReadLines(caminho).Skip(1))
{
var campos = linha.Split(',');
if (campos.Length != 3) continue;
if (!decimal.TryParse(campos[1], NumberStyles.Number, CultureInfo.InvariantCulture, out var imposto)) continue;
if (!decimal.TryParse(campos[2], NumberStyles.Number, CultureInfo.InvariantCulture, out var total)) continue;
resultado.Add(new LinhaCorrecao(campos[0], imposto, total));
}
return resultado;
}
}
Segurança
| Risco | Probabilidade | Impacto | Prevenção | Detecção | Resposta |
|---|---|---|---|---|---|
| Papel de leitura ganha permissão de escrita por acúmulo de policy | Média | Alto — corrige ou apaga pedido sem trilha, corrompe declaração fiscal | papel de correção fiscal isolado, sem herdar de nenhum papel de leitura | CloudTrail correlaciona toda UPDATE/DELETE com o papel esperado | revogar a policy herdada e reemitir o papel do zero |
| Alguém roda DELETE sem WHERE, ou com WHERE errado, numa tabela fiscal | Baixa, mas catastrófica quando acontece | Alto — perda de linhas sem forma de reconstruir fora de um snapshot antigo | CLI nunca aceita SQL livre — só CSV revisado por uma segunda pessoa | alarme de linhas afetadas por execução, acima do esperado pelo lote | time travel para o snapshot anterior ao commit, e reprocessar a partir dali |
| Chave KMS do bucket S3 Tables sem rotação | Baixa | Médio — não expõe na hora, mas envelhece a chave além do aceitável em auditoria | enable_key_rotation = true desde a criação | Config Rule cobrando rotação de chave habilitada | habilitar rotação; não precisa recriar a chave nem reprocessar dado |
| Snapshot expira antes do prazo que a auditoria fiscal exige | Média, se a retenção usar o padrão genérico do Iceberg | Alto — perde a prova do estado declarado que o fisco pediu | retenção calibrada com o time fiscal (400 dias), documentada por escrito | revisão trimestral da política de retenção contra o calendário fiscal | nenhuma — snapshot expirado não volta; por isso o prazo se calibra ANTES |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EscritaRestritaNaTabelaPedidos",
"Effect": "Allow",
"Action": [
"s3tables:GetTableData",
"s3tables:PutTableData",
"s3tables:UpdateTableMetadataLocation",
"s3tables:GetTableMetadataLocation"
],
"Resource": "arn:aws:s3tables:us-east-1:111111111111:bucket/cadencia-pedidos-iceberg/table/pedidos"
}
]
}
Observabilidade e operação
- Quantas linhas cada UPDATE realmente tocou, contra quantas o financeiro esperava no CSV?
- Quantos snapshots a tabela pedidos acumulou desde a última expiração?
- Quanto tempo uma consulta de time travel leva, comparada a uma consulta no snapshot atual?
- Quantos arquivos de delete existem sem compaction, e isso está degradando a leitura?
- Quem rodou a última alteração na tabela pedidos, e ela bate com o papel esperado?
| Alarme | Limiar inicial | Por que este número |
|---|---|---|
| CloudTrail: UPDATE/DELETE fora do papel de correção fiscal | 1 evento | qualquer ocorrência é uma investigação, não um padrão a tolerar |
| Contagem de snapshots ativos na tabela pedidos | acima de 200 sem expiração aplicada | cada snapshot retido é metadado extra que o planejador varre antes de decidir o que ler |
| Arquivos de delete acumulados sem compaction, por partição | acima de 50 | merge-on-read fica mais caro de resolver na leitura conforme os deltas se acumulam |
| Linhas afetadas por UPDATE, comparado ao total do CSV do lote | qualquer divergência | sinal de predicado mal montado, tocando pedido que não deveria |
Escala e resiliência
| Ordem de grandeza | O que muda na arquitetura | Onde quebra primeiro se nada mudar |
|---|---|---|
| 10 correções por mês (hoje) | um UPDATE em lote de cada vez, execução supervisionada pelo CLI, tá suficiente | nada quebra; o overhead é de processo humano, não de infraestrutura |
| 10 mil correções por mês (ex.: reprocessamento de imposto de um trimestre inteiro) | correção precisa ir em lotes maiores e compaction precisa rodar com mais frequência | UPDATE linha a linha sobrecarrega o número de arquivos de delete gerados por hora |
| 1 milhão de linhas na tabela pedidos | manifest lists crescem; localizar o arquivo relevante custa mais tempo de planejamento | consulta de time travel para snapshot antigo fica mais lenta se a compaction atrasar |
| Pico (fechamento fiscal trimestral, rajada de correção) | workgroup do Athena dedicado à correção, separado do workgroup do BI do L66 | consultas de correção competem por slot de execução com o dashboard que os lojistas olham no mesmo horário |
| Falha de AZ | S3 Tables e Glue Data Catalog já replicam entre AZ por padrão — não há decisão de Multi-AZ a tomar aqui, diferente de um banco relacional | nada quebra por causa de AZ; o risco real desta arquitetura é temporal (snapshot expirado), não espacial |
Uma tabela pedidos em Parquet, particionada por dia, precisa corrigir 3 linhas de um dia com 40 mil pedidos. Por que o job existente reescreve o dia inteiro, e não só as 3 linhas?
Custos e FinOps
| Cenário | Perfil de uso | Onde o custo concentra |
|---|---|---|
| Protótipo (este laboratório) | poucas dezenas de correções testadas, tabela pequena | armazenamento de snapshot extra e execução de consulta do Athena — as duas pequenas em valor absoluto |
| Produção pequena (Cadência hoje, 40 lojas) | correções mensais de conciliação fiscal, algumas dezenas de linhas por vez | compaction e expiração agendadas rodam sempre, mesmo em mês sem correção — é o custo fixo de manter Iceberg saudável |
| Alta escala (rede de 400+ lojas, correção diária) | volume de arquivo de delete cresce rápido sem compaction frequente | leitura fica cara primeiro (merge-on-read aplicando muitos deltas), armazenamento cresce depois |
Well-Architected
| Pilar | Situação | Risco | Melhoria | Prioridade |
|---|---|---|---|---|
| Excelência operacional | a correção é gerada por um CLI a partir de um CSV revisado, não SQL digitado à mão | erro de digitação num predicado corrige o pedido errado | CSV validado por schema antes de qualquer execução, com confirmação explícita do operador | Alta |
| Segurança | papel de correção fiscal isolado do papel de leitura herdado do L62 | escrita não intencional numa tabela fiscal por acúmulo de policy | papel sem herança, revisado a cada trimestre junto com a retenção de snapshot | Alta |
| Confiabilidade | commit atômico do Iceberg já protege contra leitura de estado parcial | risco herdado: snapshot expira antes do prazo que a auditoria exige | calibrar retenção mínima com o time fiscal antes de ligar a expiração automática | Média |
| Eficiência de performance | merge-on-read acumula arquivo de delete sem compaction | leitura degrada aos poucos, sem alarme até virar reclamação do time de BI | rotina de compaction nativa do S3 Tables, medida por contagem de delete por partição | Média |
| Otimização de custo | sem expiração, cada correção é armazenamento que nunca some | fatura de S3 Tables cresce mesmo sem novo pedido de correção entrando | expiração de snapshot vencido configurada junto com a compaction, não separada | Média |
| Sustentabilidade | tabela pequena hoje; não é o gargalo de energia da Cadência | nenhum risco imediato | reavaliar só se o volume de correção crescer uma ordem de grandeza | Baixa |
Evolução em níveis: do protótipo até dados e IA
A tabela pedidos como o L62 deixou: Parquet particionado simples, sem Iceberg. Correção de linha histórica exige reprocessar o dia inteiro via job do Glue.Tabela pedidos migrada para Iceberg em S3 Tables, com UPDATE/DELETE pontual e time travel calibrado com o time fiscal.Papel de IAM isolado, CloudTrail correlacionando cada alteração ao snapshot resultante, retenção documentada por escrito com o time fiscal.Correção em lote maior, não uma linha de cada vez; compaction e monitoramento de arquivo de delete mais frequentes para acompanhar o volume.Outras tabelas do lake (estoque, clientes) também precisam de correção pontual — padronizar a migração para Iceberg como decisão de plataforma, não caso a caso.Um modelo aprende o padrão esperado de alíquota por estado e categoria, e sinaliza anomalia ANTES da conciliação manual encontrar o erro duas semanas depois — é o tema da banda 9 de laboratórios de IA aplicada sobre dado tabular, e não deste laboratório.Extensão com IA
Onde IA agregaria, e onde ela não entra
Neste laboratório, IA não decide O QUE corrigir — a lista de 189 pedidos veio de uma investigação humana da equipe fiscal, cruzando a alíquota aplicada com a tabela vigente na época. Onde IA agregaria de verdade é ANTES desse ponto: um modelo que aprende o padrão esperado de imposto por estado e categoria, treinado sobre o histórico de `cadencia_ouro`, e sinaliza desvio para revisão humana antes do fechamento mensal — não depois. Uma regra tradicional (limiar fixo de variação percentual) já pega os casos óbvios; o que o modelo acrescenta é aprender que alíquota "normal" muda por estado e por categoria, sem alguém escrever uma regra por combinação. Quando o modelo erra — falso positivo sinalizando uma variação legítima de preço promocional como se fosse erro de imposto — o UPDATE nunca acontece sozinho: o modelo só abre um item de revisão, e quem aperta o botão de correção continua sendo humano, pelo mesmo CLI deste laboratório. É o tema da banda 9 de laboratórios de IA aplicada, não deste módulo.
Anti-patterns
| Erro | Por que alguém faz | Sintoma em produção | Forma correta |
|---|---|---|---|
| Continuar em Parquet/Hive e reescrever o job do dia inteiro a cada correção | é o caminho que já existe, sem aprender ferramenta nova | job de reprocessamento do dia inteiro rodando minutos para mudar poucas dezenas de bytes úteis | migrar a tabela para Iceberg e escrever só a linha que muda |
| Deletar e reinserir a linha inteira em vez de UPDATE parcial | parece mais simples que montar a cláusula SET certa | perde campo que o INSERT novo não replicou exatamente, incluindo coluna que ninguém lembrou de copiar | UPDATE só nas colunas que de fato mudaram |
| Rodar a correção com o mesmo papel de leitura ampla do resto do time | criar um papel novo por tarefa parece burocracia extra | qualquer pessoa com SELECT em pedidos também consegue corrigir pedido, sem trilha de quem devia poder | papel de escrita isolado, só para a rotina de correção fiscal |
| Nunca configurar expiração de snapshot | "funciona sem, por que mexer" | armazenamento cresce mês após mês, mesmo sem nenhuma correção nova acontecendo, e ninguém percebe até a fatura | rotina de expiração calibrada com o prazo mínimo que a auditoria exige — nem zero, nem infinito |
| Achar que time travel substitui backup | os dois "voltam no tempo", parece a mesma coisa | perder a tabela inteira (bucket apagado, política de acesso alterada) e descobrir que time travel não protege disso | time travel resolve correção de dado; backup e replicação resolvem perda do bucket — são garantias diferentes |
Troubleshooting
| Sintoma | Causa provável | Como investigar | Log/métrica | Correção |
|---|---|---|---|---|
| UPDATE reporta sucesso mas o painel de BI ainda mostra o valor antigo | ouro não foi recalculado depois da correção em prata — o job de propagação não rodou | conferir se a rotina de recompute de ouro rodou para a partição afetada | CloudWatch Logs do job de propagação prata → ouro | rodar o recompute manualmente para aquela partição e conferir de novo antes de fechar o chamado |
| Consulta de time travel devolve "snapshot not found" | o snapshot pedido já expirou pela rotina de manutenção | checar a política de retenção configurada contra a data pedida | configuração de maintenance da tabela (min_snapshots_to_keep, max_snapshot_age_hours) | não tem correção retroativa — é o motivo de calibrar retenção ANTES, não depois de precisar |
| UPDATE demora muito mais do que o esperado | a partição acumulou dezenas de arquivos de delete sem compaction | contar arquivos de delete por partição via metadado da tabela | métrica de arquivo de delete por partição, exposta pela rotina de manutenção | rodar compaction fora de hora e reagendar com frequência maior |
| O papel de leitura conseguiu rodar DELETE | a policy de leitura herdou uma permissão de escrita por engano numa revisão | revisar a policy anexada ao papel linha por linha contra o desenho original | CloudTrail: chamada de DELETE atribuída ao papel de leitura | remover a permissão herdada e recriar o papel de leitura do zero, sem copiar de outro |
| Duas correções concorrentes na mesma linha geram conflito | dois processos tentaram UPDATE na mesma linha antes de o primeiro commitar | ver o erro de concorrência otimista devolvido pelo Athena/Iceberg | mensagem de erro da query, correlacionada por query-execution-id | reexecutar a segunda automaticamente depois que a primeira commitar — não é bug, é o controle funcionando |
Perguntas frequentes
❓ Iceberg dá ao lake o mesmo ACID de um banco relacional?
❓ Por que não editar o arquivo Parquet direto no S3?
❓ Quanto tempo um snapshot antigo fica disponível para time travel?
❓ UPDATE no Iceberg reescreve o arquivo Parquet inteiro?
❓ A tabela ouro precisa virar Iceberg também?
❓ Time travel substitui backup do bucket?
❓ Por que a correção usa um papel de IAM separado do de leitura?
❓ Iceberg funciona com qualquer motor de consulta, não só Athena?
Fixando
No modo merge-on-read do Iceberg, o que exatamente uma consulta "UPDATE pedidos SET valor_imposto = X WHERE pedido_id = Y" grava fisicamente no S3?
Um painel de BI aponta uma consulta Athena fixada com "FOR SYSTEM_TIME AS OF '2026-07-20 00:00:00'". Três correções fiscais são commitadas nas semanas seguintes. O que acontece com o painel?
Checklist de produção
- Papel de correção fiscal criado, sem herdar de nenhum papel de leitura existente
- Chave KMS do bucket S3 Tables com rotação habilitada
- CloudTrail com trilha dedicada, alertando UPDATE/DELETE fora do papel esperado
- Retenção de snapshot calibrada com o time fiscal, documentada por escrito, não o padrão genérico
- Compaction e expiração de snapshot habilitadas na configuração de manutenção da tabela
- Recompute de ouro disparado após correção em prata, não só na janela agendada
- CLI de correção validando o CSV de pedido_id contra um schema, nunca aceitando SQL livre
- Alarme de contagem de linhas afetadas por UPDATE, comparado ao esperado no lote
Implantar, e provar: 189 pedidos corrigidos e o snapshot de antes intacto
#!/usr/bin/env bash
set -euo pipefail
terraform apply -auto-approve
# Migra o dado existente de prata (Parquet) para a tabela Iceberg nova. Roda
# UMA vez, fora de qualquer correção — é bootstrap, não parte do fluxo de
# correção fiscal.
aws athena start-query-execution \
--query-string "CREATE TABLE cadencia_prata.pedidos_iceberg
WITH (table_type='ICEBERG', location='s3://$(terraform output -raw bucket_iceberg)/pedidos/',
is_external=false, format='PARQUET')
AS SELECT * FROM cadencia_prata.pedidos" \
--work-group cadencia-correcao \
--query-execution-context Database=cadencia_prata
echo "tabela iceberg no ar; publique o CLI e rode provas.sh"
PROJETO=cadencia; REGIAO=us-east-1
# ── Prova 1: a tabela é Iceberg de verdade, não Parquet com nome diferente ──
aws glue get-table --database-name cadencia_prata --name pedidos \
--query "Table.Parameters.table_type" --output text
# Esperado: ICEBERG
# ── Prova 2: 189 pedidos corrigidos, e SÓ eles ─────────────────────────────
ID1=$(aws athena start-query-execution --work-group "${PROJETO}-correcao" \
--query-execution-context Database=cadencia_prata \
--query-string "SELECT count(*) FROM pedidos WHERE data_particao IN ('2026-07-22','2026-07-23','2026-07-24') AND valor_imposto_corrigido = true" \
--output text --query QueryExecutionId)
aws athena get-query-results --query-execution-id "$ID1" \
--query "ResultSet.Rows[1].Data[0].VarCharValue" --output text
# Esperado: 189
# ── Prova 3: o snapshot ANTERIOR à correção continua consultável ──────────
SNAPSHOT_ANTES=$(aws athena start-query-execution --work-group "${PROJETO}-correcao" \
--query-execution-context Database=cadencia_prata \
--query-string "SELECT snapshot_id FROM \"pedidos\$snapshots\" ORDER BY committed_at ASC LIMIT 1" \
--output text --query QueryExecutionId)
# Consultando o passado: soma de imposto ANTES da correção
aws athena start-query-execution --work-group "${PROJETO}-correcao" \
--query-execution-context Database=cadencia_prata \
--query-string "SELECT sum(valor_imposto) FROM pedidos FOR SYSTEM_TIME AS OF TIMESTAMP '2026-08-01 00:00:00' WHERE data_particao IN ('2026-07-22','2026-07-23','2026-07-24')" \
--result-configuration OutputLocation=s3://cadencia-resultados/correcao/
# Esperado: valor menor que a soma atual — a diferença é exatamente o que a
# auditoria fiscal pediu para justificar o ajuste retroativo
# ── Prova 4: reescrita é pequena — só 2 arquivos por lote, não a partição ──
aws athena get-query-execution --query-execution-id "$ID1" \
--query "QueryExecution.Statistics.DataScannedInBytes"
# Esperado: poucos MB, não os GB que reprocessar 3 dias inteiros exigiria
# ── Prova 5: o papel de leitura NÃO consegue rodar DELETE ─────────────────
aws sts assume-role --role-arn "$(terraform output -raw papel_leitura_l62)" \
--role-session-name teste-negativa --query "Credentials" --output json > /tmp/cred.json
# com essas credenciais, tente DELETE FROM pedidos WHERE pedido_id = 'qualquer'
# Esperado: erro de permissão — o papel de leitura não tem s3tables:PutTableData
Quebrar de propósito: quatro falhas do caminho de correção
A correção dos 189 pedidos rodou. Agora vale quebrá-la de propósito, porque as quatro falhas abaixo são as que aparecem quando a segunda correção acontece — e sempre acontece uma segunda. Duas delas destroem justamente a prova que este laboratório existe para produzir.
| Falha injetada | Como injetar | Sintoma enganoso | Diagnóstico correto |
|---|---|---|---|
| Dois UPDATE concorrentes na mesma partição | Rodar o CLI duas vezes em paralelo sobre o mesmo CSV, em dois terminais | Um dos dois falha com erro de commit conflitante. Parece throttling do Athena, e o retry com backoff que o L36 ensinou vai tentar de novo sozinho | Não é throttling: é a concorrência otimista do Iceberg fazendo o trabalho dela. Dois commits partiram do mesmo snapshot-base e o segundo foi rejeitado na validação. Retry cego aqui é perigoso quando o UPDATE não é idempotente — reaplicar 6,5%→12% sobre uma linha já corrigida não erra, mas um UPDATE escrito como incremento erraria. A correção é serializar a escrita pelo papel de correção fiscal, não aumentar o retry |
| Snapshot anterior à correção expirado | `ALTER TABLE cadencia_prata.pedidos EXECUTE expire_snapshots(retention_threshold => '0d')` — ou simplesmente esperar a política padrão de retenção agir | Nada quebra hoje. As consultas do dia a dia continuam idênticas, e mais rápidas, porque há menos metadado para varrer | O time fiscal perdeu a capacidade de responder "como estava antes da correção?" — que é a pergunta da auditoria, feita meses depois. Time travel não é um recurso que existe por padrão para sempre: é uma retenção que alguém configura e que a manutenção apaga em silêncio. Antes de qualquer correção fiscal, marque o snapshot com uma tag nomeada, que sobrevive ao expire |
| Athena estrangulado no meio do lote | Baixar o limite de consultas concorrentes do workgroup para 1 e disparar a correção completa | O CLI termina, reporta os lotes que passaram, e o total corrigido parece próximo o bastante de 189 para ninguém conferir | Correção fiscal parcial é pior que correção nenhuma: a tabela fica num estado que não é nem o errado conhecido nem o certo. Por isso a prova conta 189 e compara com o esperado, em vez de checar código de saída. "Terminou sem exceção" nunca foi sinônimo de "aplicou tudo" |
| Muitos upserts pequenos, sem compactação | Rodar 50 correções de uma linha cada, uma por vez, e medir a consulta do agregado diário antes e depois | A consulta do painel fica lenta progressivamente. A leitura natural é "o volume de dados cresceu" | O volume não mudou — mudou a CONTAGEM DE ARQUIVOS. Cada upsert do Iceberg escreve arquivos de delete e de dados novos, e a leitura passa a abrir centenas de objetos minúsculos. É o problema de arquivo pequeno do L64, reaparecendo por uma porta que o L64 não previa: não pela ingestão, pela correção. `OPTIMIZE ... REWRITE DATA USING BIN_PACK` resolve, e precisa estar agendado, não lembrado |
Duas destas falhas apagam a prova, não o dado
A segunda e a quarta não corrompem nenhum pedido — e é isso que as torna fáceis de deixar passar numa revisão. A segunda apaga a capacidade de PROVAR o que foi corrigido; a quarta apaga o desempenho que tornava a tabela usável. Nos dois casos o dado está certo e o laboratório falhou mesmo assim, porque a promessa aqui nunca foi só "corrigir a linha": foi corrigir podendo mostrar depois o antes, o depois e quem mandou.
Limpeza do laboratório
PROJETO=cadencia; REGIAO=us-east-1
# 1. A tabela Iceberg NÃO é removida pelo destroy do bucket S3 Tables sozinho
# se ainda houver snapshot retido dentro da janela de retenção — apague a
# tabela explicitamente primeiro.
aws s3tables delete-table --table-bucket-arn "$(terraform output -raw bucket_iceberg_arn)" \
--namespace cadencia_prata --name pedidos
# 2. O bucket de tabelas em si.
aws s3tables delete-table-bucket --table-bucket-arn "$(terraform output -raw bucket_iceberg_arn)"
# 3. Terraform cuida do resto: IAM, CloudTrail, KMS.
terraform destroy -auto-approve
# 4. CHAVE KMS entra em exclusão PENDENTE, não some na hora — continua
# existindo (sem cobrar por uso, mas ocupando o limite de chaves da conta)
# pelo período de espera configurado.
aws kms list-keys --query "Keys[?KeyId=='$(terraform output -raw kms_key_id 2>/dev/null)']"
# 5. O BUCKET do trail de CloudTrail não é apagado pelo destroy se tiver
# objeto — os logs de auditoria continuam existindo de propósito, mesmo
# depois do laboratório desmontado. Esvazie manualmente só se tiver
# certeza de que a retenção de auditoria já não é mais exigida.
aws s3api list-objects-v2 --bucket "$(terraform output -raw bucket_trilha_auditoria)" \
--query "length(Contents)" --output text
# 6. Prova final: nada com o nome do projeto de pé.
aws resourcegroupstaggingapi get-resources \
--tag-filters Key=Projeto,Values="$PROJETO-correcao-fiscal" \
--query "ResourceTagMappingList[].ResourceARN" --output table
O que continua cobrando, e por quê
Snapshot retido para auditoria continua ocupando armazenamento no S3 Tables mesmo depois de "terminar" o laboratório, até a tabela ser apagada explicitamente — diferente de um recurso que para de cobrar assim que você para de usar. E o bucket do trail de CloudTrail guarda log de auditoria de propósito: apagar sem confirmar que a retenção fiscal já não é mais exigida é perder exatamente a prova que este laboratório existiu para criar.
Resumo visual
| Problema | Serviço | Motivo |
|---|---|---|
| Corrigir 189 linhas sem reescrever 14 mil por dia | S3 Tables + Iceberg (merge-on-read) | grava só o delta, nunca reescreve a partição inteira |
| Provar o que foi declarado antes da correção | Time travel (FOR SYSTEM_TIME AS OF) | consulta um snapshot antigo sem precisar de backup separado |
| Impedir escrita por acidente do papel de leitura | Papel de IAM isolado | raio de erro de política limitado à tabela de correção |
| Rastrear quem corrigiu o quê e quando | CloudTrail | evento de API correlacionado ao snapshot resultante |
| Impedir que o custo cresça sem limite | Compaction e expiração nativas do S3 Tables | consolida arquivo de delta e descarta snapshot vencido, calibrado com o fiscal |
Próximo laboratório
A seguir: L68 — Redshift quando o BI dói
A tabela pedidos agora responde rápido a correção pontual — mas o painel que os 40 gerentes de loja abrem toda manhã ainda roda sobre Athena, e uma agregação mais pesada (comparar as 40 lojas lado a lado, em janelas móveis) já leva 40 segundos para carregar. O L68 troca o motor de consulta pesada para o Redshift — sem trocar a tabela Iceberg por baixo, porque formato de tabela e motor de consulta são decisões independentes, como a última pergunta frequente deste módulo já antecipou.
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…