Skip to content

Instantly share code, notes, and snippets.

@YuriFontella
Created August 11, 2026 14:49
Show Gist options
  • Select an option

  • Save YuriFontella/77540aef4b8db23a2355eb7444d21955 to your computer and use it in GitHub Desktop.

Select an option

Save YuriFontella/77540aef4b8db23a2355eb7444d21955 to your computer and use it in GitHub Desktop.

Review do core do OpenLake

Este documento descreve como revisar o OpenLake. A unidade de análise é a funcionalidade atravessando todas as camadas, não o arquivo que está aberto.

Uma review serve para entender um fluxo real de ponta a ponta e resolver o que estiver errado ou frágil nele — defeito concreto, risco demonstrável, barreira à evolução. Não para produzir lista de boas práticas.

Escopo

O inventário de funcionalidades, com camadas, stores e ligações, está em FEATURES.md. Toda review parte de um item de lá.

Aplicações ativas: apps/api (API principal e Data API), apps/web (interface operacional e principal consumidora dos contratos), apps/mcp (servidor e agente MCP de RAG). O core está em openlake/core e o código enviado ao sandbox em runtime/openlake_runtime. site/ é a aplicação de marketing e fica fora.

O que se olha

Quatro lentes são aprofundadas em toda review, mesmo sem suspeita prévia — elas são o motivo pelo qual o ciclo existe:

Segurança e isolamento. Qual autoridade cada operação exige, até onde ela alcança, e o que sobra quando algo escapa.

Performance e uso de recursos. Se tempo, memória, conexões, storage e custo crescem junto com o volume real ou junto com algo que ninguém limita.

Complexidade que não paga por si. Duplicação, indireção e trabalho cognitivo que cobram manutenção sem devolver nada.

Camadas e responsabilidades. Se cada decisão está no componente que tem autoridade e contexto para tomá-la.

As outras áreas — correção, integridade, concorrência, confiabilidade, contratos, testes, observabilidade — entram conforme o fluxo as toca. E um problema de correção ou de integridade tem poder de bloquear uma mudança que parecia boa sob as quatro lentes acima.

Área sem evidência de impacto no fluxo não gera achado.

Como conduzir

Começar lendo. A primeira passagem é somente leitura: entender o que a funcionalidade faz, por que faz assim, e o que já a protege. Suspeita levantada no primeiro contato costuma morrer na segunda leitura, e é mais barato que morra antes da correção.

Seguir a ordem real de execução — entrada, autenticação, autorização, regra, persistência, efeitos externos, eventos, resposta e recuperação. Rota, service, worker e adapter revisados como universos independentes deixam passar exatamente os defeitos que moram entre eles.

As listas mais adiante são perguntas, não prova. Um item aponta onde olhar; o achado nasce do código. Antes de relatar, tentar refutar: procurar o guard, a constraint, o lock, o retry ou a compensação que já resolve o caso. Não declarar ausência sem procurar as implementações alternativas, e não descrever caminho de código que não foi lido.

A correção. A correção certa é a que remove a causa no lugar onde a responsabilidade existe, incluindo os componentes que precisam mudar junto para o fluxo ficar coerente e testável. Se a causa é estrutural, é a estrutura que muda; contornar no ponto de sintoma para manter a alteração pequena deixa o defeito vivo com uma camada em cima. E a causa raiz nem sempre mora dentro da funcionalidade escolhida — quando mora fora, é ela o alvo, junto com os outros caminhos que ela atinge. Defeito sem relação com o fluxo percorrido vira registro para outro ciclo.

Preservar comportamento não significa preservar defeito. Se o comportamento atual causa vulnerabilidade, corrupção, resultado incorreto ou falha operacional, ele é o alvo — com o impacto documentado, e com aprovação quando a correção alterar contrato ou semântica pública.

Padrões. Antes de criar estrutura nova, identificar o padrão que o core já usa para a mesma regra e reutilizá-lo: service, repository, adapter, helper ou modelo. Padrão compartilhado é o que tem as mesmas invariantes e responsabilidades, não a mesma aparência — diferença justificada pelo domínio continua explícita. Um padrão existente só é reutilizado se for correto para produção; replicar estrutura inadequada por uniformidade troca um problema por vários. Sem padrão válido disponível, a estrutura mais simples que preserve as responsabilidades.

Quando nada precisa mudar. Terminar sem achado material é um resultado, não uma falha do ciclo. Quando o fluxo foi percorrido e as decisões se sustentam, dizer isso — com o que foi verificado e o que ficou fora — vale mais que preencher a entrega com observação de estilo. Isso não significa sair sem tocar em nada: se o caminho revisado se afastou de um padrão já estabelecido no core, ou deixou uma ponta que confunde quem vier depois, alinhar ali é parte do trabalho. Todo código pode melhorar; o que a review não faz é transformar "poderia ser diferente" em achado.

Limites. Pedir aprovação antes de alterar contrato público, schema, migration, formato persistido ou comportamento observável de forma intencional. Fora desses quatro casos, seguir. Nunca expor secret, token, credencial ou conteúdo de .env em nenhuma saída da review, inclusive ao examinar histórico do Git.

Ciclo

1. Delimitar a funcionalidade

Registrar o item correspondente em FEATURES.md, o objetivo do usuário ou processo, as entradas públicas e internas, o principal envolvido (humano, service account ou sistema), o environment, workspace e owner em jogo, os stores e serviços externos, os workers, schedules e eventos ligados, e quais estados finais são válidos.

2. Estabelecer o baseline

Descrever o comportamento atual observável e localizar os contratos consumidos pela interface web, pela Data API e pelo MCP. Identificar os testes e smokes que já cobrem o fluxo e rodar a validação mínima que demonstra o estado atual, quando o ambiente permitir. Registrar o que deve continuar igual depois da mudança, e explicitar o que não pôde ser confirmado.

3. Percorrer o caminho de execução

Ir da entrada até o último efeito observável, anotando validações e normalizações, decisões de autorização, transações, locks e limites de concorrência, mudanças de estado, escritas em Postgres, DuckLake, MinIO e Qdrant, publicação de eventos e auditoria, chamadas externas, retry, timeout, cancelamento e compensação, e a resposta entregue ao consumidor.

As lentes e os sinais deste documento se aplicam sobre esse mapa, não sobre arquivos avulsos.

4. Provar o achado

Para cada candidato: localizar o caminho exato no código, montar um cenário reproduzível ou deterministicamente demonstrável, confirmar pré-condições e alcance, procurar o controle que o refuta, consultar os testes e smokes existentes, e executar a validação mais estreita e segura quando ela decidir a questão. Classificar só depois disso.

5. Definir a correção

Identificar a causa raiz e todos os caminhos atingidos por ela, escolher onde a responsabilidade fica, desenhar a estrutura a partir dos padrões válidos existentes, listar contratos e estados preservados, declarar mudança funcional intencional e indicar os testes novos ou ajustados.

Achados independentes são corrigidos e validados independentemente.

6. Implementar e validar

Implementar as correções confirmadas preservando os contratos e o baseline não relacionado ao defeito. Ajustar os testes no nível mais próximo da regra e rodar testes, smokes do fluxo e as validações transversais afetadas. Revisar o diff antes de fechar, para que ele conte só a história da correção e dos ajustes que ela justifica. Comparar o resultado ao baseline: mudança que passa nos testes mas altera comportamento fora do achado não está concluída.

uv run python -m unittest discover -s tests -p "test_*.py"
uv run ruff check --select E,F,I --ignore E501 openlake apps
docker compose --env-file .env -f infra/compose.yaml up -d postgres minio rabbitmq

Smokes do fluxo ficam em smoke/; os executados pelo CI estão em .github/workflows/reliability.yml. Mudança na interface web também roda npm run lint, npm run typecheck e npm run build em apps/web.

7. Relatar

Separar comportamento preservado, defeito corrigido, mudança intencional aprovada e melhoria não implementada. Declarar o que não foi verificado, a dependência que faltou e o teste que não pôde rodar, no formato do final deste documento.

Onde os defeitos se concentram

Neste sistema os defeitos aparecem nas conexões, não no meio dos componentes. Ao percorrer um fluxo, dar atenção extra às travessias que ele usa:

  • Web → proxy Nuxt → API → service → repository/adapter.
  • Sessão/token → permission → environment → owner/grant/share.
  • Pipeline → run worker → runner → step → logs/events/lineage.
  • Schedule → tick → advisory lock de início de run → limites de concorrência.
  • Source → ingestion/consumer → DuckLake → dataset event/lineage.
  • PostgreSQL CDC → Debezium → RabbitMQ → worker → apply por primary key.
  • Python/notebook → sandbox → capability proxy → SQL/workspace/secrets/log.
  • SQL → análise de privilégio → grants/policies → DuckLake → history/lineage/audit.
  • Catálogo DuckLake ↔ Postgres metadata ↔ MinIO data files.
  • RAG → catálogo/governança → filtro de sensibilidade → embeddings → Qdrant → LLM → SQL answer.
  • Evento do core → Postgres NOTIFY/store → WebSocket → reconciliação no web.
  • API/core RAG → servidor MCP → cliente/agente externo.
  • Core → egress externo: webhook, repositório Git, provider de embedding/chat.

Sinais por área

Segurança, isolamento e governança de dados

O objetivo é impedir acesso, execução ou alteração fora da autoridade concedida, e limitar o prejuízo quando algo escapa. Em cada ponto do fluxo: quem é o principal, qual permission a operação exige, o que delimita o alcance dela, e o que sobra se essa decisão estiver errada.

  • autenticação incompleta, sessão ou token expirado, revogação ignorada;
  • rota, operação em lote ou WebSocket sem a permission equivalente à do caminho síncrono;
  • lookup por ID sem environment, workspace ou owner (IDOR), e autorização diferente entre listar, detalhar, executar e excluir;
  • confiança no filtro da interface web — ela é consumidora do contrato, não fronteira de segurança;
  • mass assignment de owner, role, environment ou principal;
  • SQL injection, SSRF, path traversal, upload inseguro, XSS, desserialização;
  • escape do runtime Python/notebook: código de usuário é hostil, recebe apenas as capacidades declaradas e nunca adapter administrativo, credencial de infraestrutura ou secret não declarado;
  • secret em log, erro, evento, payload, argumento de processo ou resposta — inclusive na saída do sandbox, onde valores sensíveis precisam sair redigidos;
  • CORS, CSRF, rate limit, teto de volume e timeout ausentes na borda;
  • token de service account, worker ou script com alcance maior que o necessário;
  • credencial administrativa entregue a worker ou integração sem necessidade;
  • delete, resync, cleanup, revoke ou lote sem escopo visível, e filtro vazio degenerando em operação global;
  • um único erro alcançando todos os environments, workspaces ou datasets;
  • offboarding incompleto de usuário, grupo, token, share ou integração, sem snapshot ou trilha para reconstruir o que se perdeu;
  • classificação, tags ou policies respeitadas em um caminho e ignoradas em preview, SQL, exportação ou RAG;
  • row filter ou column mask aplicado em um caminho e ausente em outro;
  • amostra sensível copiada para Qdrant, log, trace ou evento;
  • exportação ou listagem em massa sem limite e sem registro;
  • dado pessoal além da retenção, em store não mapeado, ou exclusão que não alcança índices, caches, logs e artefatos derivados;
  • auditoria carregando SQL, conteúdo de arquivo, token ou payload sensível;
  • acesso direto a storage ou banco contornando a governança do core.

Passar sempre por environment isolation, workspace root, grants, shares, Data API, service accounts, capability proxy, SQL compartilhado, RAG e egress de rede (webhook, Git, provider, sandbox).

Performance e uso de recursos

Um achado aqui declara escala ou frequência: "com X registros ou Y execuções, acontece Z". Sem isso, é hipótese de medição, não achado.

  • N+1 em handler, serialização, repository, worker ou montagem da resposta;
  • full scan, OFFSET crescente, índice incompatível com a query;
  • materialização integral onde streaming ou paginação era necessário;
  • resposta, log, evento ou payload sem teto imposto pelo servidor;
  • transação ou lock mantido durante I/O externo;
  • I/O síncrono bloqueando o event loop;
  • pool por réplica excedendo as conexões disponíveis;
  • subprocesso, thread, conexão, arquivo temporário ou container vazando;
  • cache sem invalidação, ou chave sem environment e permissão;
  • retenção ilimitada de snapshots, logs, traces, runs, ingestions ou vetores;
  • reindexação RAG, manutenção ou polling refazendo trabalho já feito;
  • limite de CPU ou memória declarado e não aplicado.

Complexidade e clareza

A pergunta não é se o código poderia ser mais elegante — é se a forma atual já produz divergência, esconde uma decisão ou trava uma mudança concreta. Relatar quando for possível apontar o defeito atual ou a mudança bloqueada.

Padrões de design entram aqui como ferramenta: reutilizar o que o core já estabeleceu para a mesma regra vale mais que introduzir um conceito novo, e aplicar um pattern sem demanda que o sustente é a própria complexidade que esta lente procura.

  • regra de negócio duplicada entre rota, service, worker, runtime e web;
  • duas formas ativas de executar a mesma operação;
  • variantes divergentes em que uma esquece validação, autorização, auditoria, timeout, cleanup ou teste que as outras têm;
  • god service, ou módulo misturando responsabilidades independentes;
  • flag booleana que permite combinações inválidas de estado;
  • condicionais profundas e tratamento de erro inconsistente no mesmo fluxo;
  • abstração para um único caso, sem fronteira real; camada genérica, framework ou indireção introduzida sem demanda;
  • dependência circular, e import tardio usado para escondê-la;
  • código morto em app ativo, ou referência ativa a código removido;
  • conhecimento concentrado a ponto de qualquer mudança segura depender de um único arquivo.

Diferença apenas de nome, ordem ou formatação não é achado. Inconsistência vira achado quando já produz comportamento diferente, aumenta o risco de divergência ou impede testar e evoluir o fluxo — e continua cabendo como ajuste quando o caminho revisado se afastou de um padrão do core sem motivo.

Camadas e responsabilidades

  • regra de negócio na rota, ou rota que apenas repassa sem validar entrada externa;
  • service dependendo de Request, response schema ou formato da UI;
  • adapter contendo regra de autorização ou de domínio;
  • normalização de path, identifier ou namespace duplicada fora do adapter;
  • worker ou MCP contando com validação que só a API executa;
  • decisão de autorização espalhada entre camadas em vez de resolvida na fronteira correta;
  • transação aberta em uma camada e commitada em outra;
  • dependência instanciada dentro da regra, impedindo teste ou substituição;
  • clock, UUID, random ou estado global usados direto na regra;
  • interface web reimplementando regra funcional da API em vez de consumir o contrato;
  • MCP reutilizando o core mas contornando autorização e escopo, tratando argumento de tool como entrada confiável, ou expondo além do contrato da tool;
  • services, repositories, stores ou workers equivalentes com padrões diferentes de transação, erro, dependência, retorno ou lifecycle.

Correção e invariantes

  • regra implementada diferente do contrato ou do que a interface web mostra;
  • estado válido rejeitado, ou estado inválido representável;
  • transição de estado ausente, impossível, ou sem estado terminal;
  • bordas: vazio, duplicado, parcial, limite, retry, cancelamento e timeout;
  • ordenação incorreta de steps, eventos ou mutações;
  • sucesso retornado antes do efeito necessário terminar;
  • contagem, métrica ou status que não representa o estado real;
  • invariantes entre pipeline, run, step, ingestion, source e dataset;
  • uso inconsistente de owner, principal solicitante e run_as.

Prioridade para estados de runs, ingestions, consumers, schedules, pipelines, índices RAG e mutações DuckLake.

Concorrência e integridade entre stores

Postgres, DuckLake, MinIO, Qdrant e os stores operacionais precisam continuar coerentes sob execução simultânea e falha parcial. Onde dois efeitos precisam valer juntos, procurar o que garante isso — e o que acontece se só o primeiro valer.

  • constraint apenas no código onde a concorrência exige constraint no banco, e unicidade por "consulta e depois insere" sem índice único;
  • read-modify-write sem lock ou controle otimista, e limite de concorrência verificado fora do lock;
  • lock com granularidade errada ou ordem de aquisição inconsistente;
  • transação longa, fronteira de commit incorreta, chamada externa ou evento não compensável antes do commit;
  • dual write sem atomicidade, reconciliação ou estado intermediário explícito; arquivo gravado sem metadata, ou o inverso;
  • mutação DuckLake confirmada com run ou ingestion marcado como falha;
  • evento, lineage, auditoria ou índice RAG divergindo da fonte de verdade — ou não estar claro qual store é a fonte de verdade e como os derivados reconciliam;
  • exclusão parcial deixando referência órfã;
  • execução duplicada por retry HTTP, redelivery, restart ou dois schedulers, sem deduplicação por ID de mensagem ou de operação;
  • ACK antes da persistência necessária, ou depois de efeito não idempotente;
  • lease ou heartbeat menor que a duração real do trabalho;
  • corrida entre start, stop, resync, retry, cancel e delete;
  • evento antigo sobrescrevendo estado mais novo no store ou na interface web;
  • cron sobreposto, DST e catchup duplicado;
  • webhook ou integração externa repetida sem chave idempotente.

Confiabilidade e recuperação

  • timeout ausente em banco, HTTP, provider, subprocesso ou Docker;
  • retry de erro permanente, ausência de retry para falha transitória, backoff sem jitter;
  • poison message sem isolamento;
  • falha parcial sem compensação nem reconciliação;
  • processo encerrado sem graceful shutdown, e worker morto ainda representado como saudável;
  • worker que não distingue falha transitória, permanente, cancelamento e resultado desconhecido, ou que perde principal, environment e correlation ID;
  • run, ingestion, consumer ou job preso sem caminho de recuperação;
  • cancelamento que não interrompe a dependência, ou deixa efeito desconhecido sem sinalização;
  • indisponibilidade de RabbitMQ, Qdrant, Ollama, MinIO, Postgres ou Docker derrubando funcionalidade não relacionada;
  • ausência de caminho operacional para reprocessar ou reparar, e de dry-run quando o efeito não é facilmente reversível;
  • schedule sem lock distribuído, janela e catchup determinísticos, sem expor último progresso, último sucesso e motivo de deferral, ou limpando fora do namespace resolvido.

Contratos e evolução

  • divergência entre schema, rota, response helper, service e tipos da web;
  • campo renomeado, removido ou com semântica alterada sem compatibilidade;
  • status HTTP incompatível com o resultado real, e operação longa respondendo como concluída em vez de aceita;
  • erro interno ou entidade ORM vazando como contrato;
  • idempotência não documentada em escrita repetível;
  • paginação, filtros, defaults e limites diferentes entre API principal e Data API;
  • evento ou payload de worker sem versão nem tolerância a campo adicional;
  • MCP expondo semântica diferente da API e do core que ele reutiliza;
  • mudança de YAML de pipeline ou source invalidando definição existente;
  • enum ou estado fechado replicado em vários lugares, exigindo mudança coordenada;
  • metadata persistida sem versão nem caminho de migração;
  • coluna, tabela, evento, configuração ou formato sem estratégia de remoção; migração bloqueante ou sem expand/contract;
  • incompatibilidade entre versões simultâneas de API e workers.

Testes e observabilidade

As duas perguntas são a mesma: como se prova que o fluxo está correto antes de subir, e como se descobre que ele quebrou depois. Percentual de cobertura não é conclusão — o que importa é se o teste atravessa a costura onde está o prejuízo.

  • caminho crítico sem teste do sucesso e do principal modo de falha;
  • nenhum teste de idempotência, concorrência, cancelamento ou retry onde eles fazem parte do fluxo;
  • smoke que valida apenas status superficial;
  • mock que apaga o comportamento real de transação, lock, filesystem, DuckLake ou provider;
  • teste dependente de ordem, hora, rede ou estado compartilhado;
  • fixture com autoridade maior que a da execução real;
  • falta de teste de contrato entre API e web/MCP;
  • erro difícil de reproduzir por dependência rígida ou efeito global;
  • except silencioso, erro reduzido a debug, mensagem sem contexto;
  • correlation ID perdido entre API, worker, evento, query e dataset;
  • log sem run, step, source, index, environment ou principal — ou log com dado sensível;
  • métrica que mostra processo vivo mas não trabalho progredindo; falta de lag, idade, último sucesso, retries, deferrals ou backlog; ausência de sinal tratada como sucesso;
  • auditoria só no endpoint, sem cobrir worker, schedule, MCP e script; ação destrutiva sem ator, owner, escopo e resultado;
  • trace incompleto ou evento impossível de ordenar;
  • configuração obrigatória validada só na primeira utilização, default insegura, comportamento diferente entre local, Docker e produção, configuração duplicada entre API, worker, runtime e compose;
  • health ou readiness que não representa as dependências essenciais;
  • deploy interrompendo worker sem drenagem;
  • serviço opcional impedindo o startup de área não relacionada;
  • arquivo temporário, container, configuração Debezium ou cache não limpo.

Severidade e confiança

  • P0 — perda ou corrupção ampla de dados, isolamento rompido entre environments, execução remota não autorizada, exposição relevante de secret ou PII, operação irreversível de grande alcance. Ação imediata.
  • P1 — falha explorável ou operacional com impacto grave, recurso crítico indisponível, efeito importante duplicado ou perdido, recuperação manual arriscada. Bloqueia release do fluxo afetado.
  • P2 — comportamento incorreto ou risco relevante com escopo limitado, pré-condição específica ou workaround confiável.
  • P3 — dívida comprovada que aumenta custo ou chance de regressão, sem falha material imediata. P3 não é lugar para preferência de estilo.

Confiança: confirmado, quando reproduzido ou demonstrado por caminho determinístico completo; alta, quando o código demonstra o cenário mas uma dependência externa impediu reproduzir; média, quando a evidência é parcial e uma pré-condição segue não confirmada. Hipótese sem cenário demonstrável não tem severidade — vai para as questões de investigação.

Um achado relatável traz título, severidade e confiança; funcionalidade e área afetadas; localização em arquivo:linha; pré-condições e cenário na ordem de execução; comportamento atual e o invariante ou contrato violado; alcance do impacto (usuário, environment, workspace, dataset, execução); a tentativa de refutação e por que os controles existentes não bastam; e a correção — aplicada ou recomendada — com a forma de validá-la. Sem localização, cenário e impacto, é anotação, não achado.

Formato da entrega

  1. funcionalidade escolhida em FEATURES.md e fluxo percorrido;
  2. baseline preservado;
  3. padrões encontrados no core e qual foi aplicado;
  4. achados por severidade, com o que foi corrigido e o que ficou preservado depois da alteração;
  5. áreas avaliadas sem achado material;
  6. questões para investigação, separadas dos achados;
  7. testes e comandos executados, antes e depois;
  8. limitações da análise, dependências não verificadas e hipóteses restantes;
  9. melhorias futuras não implementadas, priorizadas por risco, impacto, esforço e dependências.

A conclusão vale para a funcionalidade e os cenários percorridos, não para o OpenLake inteiro. Risco residual entra declarado; conclusão genérica de aprovação não substitui isso. E quando o fluxo se sustenta, dizer que se sustenta, com o que foi verificado — é uma entrega completa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment