Todos que desenvolvem ou desenvolveram um processo de ETL para um DW sabem que existe um calcanhar-de-aquiles no processo: testes.

Quando escrevemos um pedaço de código em alguma linguagem, como C ou Java, podemos rodar um teste, debugar linha a linha, decidir se o código funciona ou não e ponto final. Existem até mesmo técnicas de desenvolvimento que pregam que os testes devem ser desenhados antes, e só depois deve ser escrito o programa (TDD.) Mas como ficam os testes em processo de ETL?

Começa que, em 2015 (praticamente 2016), pouca gente escreve ETL como um programa ou scripts. É difícil abandonar as facilidades, o poder e a versatilidade de uma ferramenta gráfica, como o PDI ou o PowerCenter, e escrever o ETL como um programa. Dá muito trabalho, muitas desvantagens e nenhuma vantagem. Logo, está fora de questão escrever programas para ETL só para poder usar testes automatizados. Mas mesmo que, masoquistamente, optássemos por um programa para ETL, o teste do programa não resolve o verdadeiro teste: os dados.

Veja, o problema de testar um processo de ETL para um DW é composto por duas partes:

  • Testar o processo em si;
  • Testar se o processo tratou os dados de maneira correta.

O Processo em Si

A primeira parte é a mais fácil. Basta verificar se o processo roda de ponta-a-ponta. Rodando, se ele resiste a erros comuns, como strings no lugar de números e campos com nulos. Para esses testes basta rodar o processo contra a massa de dados original e verificar se ele chega até o fim; injetar alguns erros na massa e repetir o teste, e assim por diante.

Não é difícil imaginar uma lista de erros, que possa ser forçada sobre uma massa de dados autêntica. Dados que contenham todo tipo de erro que pudermos pensar bastam para testar o processo de ETL contra erros de desenvolvimento mais ordinários, por assim dizer.

Tratando Dados Corretamente

É aqui que a porca torce o rabo. Conhece Calvin & Haroldo? Nesta tirinha (não posso colar a imagem, tem copyright), a professora pede ao Calvin para explicar a Primeira Lei de Newton com as próprias palavras. Eis a explicação dele:


Yakka foob mog. Grug pubbawup zink wattoom gazork. Chumble spuzz.

Calvin & Hobbes


Como dizer que ele está errado, se não conhecemos o significado das próprias palavras dele? É o mesmo problema nessa segunda categoria de testes: os dados estão como deveriam estar? Ora diabos, e como é que eles deveriam estar?

Não sei se consigo me fazer entender. Uma tabela dimensão “Clientes”, por exemplo, precisa ter a lista dos clientes da empresa. Agora, suponha que o processo de ETL força uma situação acidentalmente, mas não percebemos isso de cara. Sei lá, vamos dizer que um filtro no meio do processo sem querer removeu todo cliente do gênero feminino (um ETL machista, kkkk.) A transformação vai rodar, os dados serão carregados mas estarão errados! Que tipo de teste alguém desenha para detectar um erro desses, chamado de erro de domínio?

Poderíamos contar o domínio na origem, comparar com o destino e… e o quê? Dizer que se der diferente, o processo está errado? E como decidir quando um cliente não entrou na dimensão por uma questão legítima? Poderíamos estabelecer uma margem de erro, talvez?

Qual é sua opinião? Seus ETLs tratam erros de domínio? Como?

A solução que eu encontrei até agora é semi-automática:

  1. Um time formado por usuários e desenvolvedores revisam e validam cada resultado;
  2. Estatísticas são coletadas após essa validação e tomadas como parâmetros centrais;
  3. A cada nova carga, as estatísticas do DW atualizado são coletadas e comparadas com as estatísticas originais;
  4. Em caso de uma variação significativa, os desenvolvedores são automaticamente alertados, e um aviso é disparado para os usuários, informando que o processo de ETL “está passando por revalidação periódica e em breve os senhores serão notificados da normalidade do processo”.

Caímos de novo na mesma situação do quadrinho do Calvin: o que é uma variação significativa? E, novamente, a solução que eu vislumbro é empírica: começamos com uma tolerância zero, e a cada nova carga revisamos os dados e ajustamos a tolerância um degrau para cima. O processo acaba convergindo para uma “normalidade” e apenas acidentes passam a ser detectados. Porém, note que isso não isenta a equipe de um controle de qualidade periódico.

É claro que, uma vez que o processo atinja alguma estabilidade, as avaliações podem ficar mais espaçadas.

Arquitetura de Testes

Ok, se já entendemos que tipos de testes podemos/devemos fazer, resta a questão prática: que infraestrutura montar, em desenvolvimento, que colabore para manter o processo de ETL válido? Idealmente, que permita até avaliar sua performance (outro tipo de teste, aliás)?

Novamente recorrendo à analogia com o desenvolvimento em linguagens de programação, um ambiente de testes moderno é feito por um ambiente de integração contínua (ou CI, de Continuous Integration), um repositório de código e servidores de apoio (como os que o sistema teria se estivesse em produção.) Funciona assim:

  • Desenvolvedores puxam atualizações do repositório para suas estações;
  • Eles evoluem o código até o ponto desejado e comissionam as mudanças de volta ao repositório;
  • O servidor de CI, que monitora o repositório de desenvolvimento, entra em ação:
    • Puxa as atualizações assim que elas são dão entrada no repositório;
    • Compila e monta uma “build”;
    • Roda os testes unitários (regressão e novas funcionalidades;)
    • Roda os testes de integração;
    • Monta um pacote de artefatos “deployables”, para teste;
    • Atribui uma tag para a versão que acabou de tratar…
    • … e finalmente informa os resultados à equipe;
  • Se a compilação ou algum teste falha, o servidor de CI informa à equipe de desenvolvimento.

No caso de um erro, a equipe aplica-se em resolvê-lo o mais brevemente possível, submetendo essa revisão para o servidor de CI, via versionamento, para reiniciar o ciclo.


A lista acima foi traduzida livremente a partir do original aqui.


Não vejo porque não possamos montar uma solução igual para ETL. A única diferença, até onde eu posso notar, é que um processo de ETL construído com uma ferramenta visual não é compilado. Cada um dos passos da lista anterior possui um equivalente direto para um projeto de ETL e apenas os testes precisariam ser revistos.

Desenvolvimento

Todo projeto deve usar um repositório versionável (com o Git, meu favorito) para desenvolvimento. Mesmo que seja projeto de um membro só, composta pela famosa e ubíquia euquipe, um repositório é imprescindível. Logo, os dois primeiros itens da lista são pontos obrigatórios e (deveriam ser) correntes em todos os projetos de ETL.

Servidor de Integração Contínua

Um servidor de integração contínua periodicamente puxa o “código” atualizado e conduz o processo de integração e testes. Existem servidores de CI (CIS) já prontos, com muitas e muitas funcionalidades que talvez se adaptem a um projeto de ETL, como o Hudson. Em último caso, se nenhum deles servir, podemos construir um processo no PDI (a ferramenta de ETL da Pentaho, para os novatos) para emular um CIS.

Layout Genérico

A figura abaixo esquematiza um ambiente CIS para ETL, que por definição deve refletir a estrutura real:

Arquitetura genérica de um ambiente de testes para desenvolvimento de ETL.
Arquitetura genérica de um ambiente de testes para desenvolvimento de ETL.

Note os vários servidores de bancos de dados: um ambiente de testes digno deste nome precisa dispor de um cenário real para certos casos e para isso cópias completas dos bancos de origem são necessárias. Não falo de replicação contínua, em tempo real ou mesmo esporádico, mas apenas de uma infra-estrutura que replique a carga à qual o processo de ETL será submetido no ambiente real.


Se testes de performance são parte do conjunto de testes de um CIS, ou não, é um tópico aberto a debate. A eliminação de gargalos e melhora de rendimento é um processo de tentativa e erro, e sem dispor, em desenvolvimento, de uma fonte semelhante à real, acabamos usando o processo de ETL de produção para testar as melhorias de velocidade. No meu humilde entendimento, essa atitude é antiprofissional porque sujeita a operação da empresa a fatores variáveis e contraproducente, pois reduz a velocidade do desenvolvimento à velocidade das cargas do DW (ou seja, se ocorrer um refresh por dia, então os testes acontecem uma única vez por dia.) Eu, porém, tenho certeza que esse tipo de teste é dos mais importantes.


Para os testes que descreverei abaixo, cada banco de origem precisa ser backapeado em pelo menos dois dias diferentes e o banco de destino deve ser criado vazio.

Testes

Eis aqui uma lista dos testes que podemos fazer em um processo de ETL.

Elementares há dois testes:

  • De Processamento
    Neste teste ambos os bancos (origem e destino) estão vazios. O processo de ETL roda “em falso”, e o resultado é um vai/não vai para o processo como um todo. Seria o equivalente ao teste de compilação de uma linguagem tradicional: se o teste falhou, o dono do artefato que causou a falha deve ser avisado para consertá-lo. Não tenho certeza se ele é sempre possível, ou se um teste de sucesso poderia esconder uma falha ordinária – como um nome errado de tabela, coisa que deveria parar o processo.
  • De Carga Cheia
    Agora o banco de origem está cheio, e o de destino, vazio. O processo roda e realiza uma carga completa no destino. Em adição ao vai/não vai anterior, este teste dá a validação completa do ETL, ou seja, se o ETL faz alguma validação de domínio, ela é submetida a uma execução real, em condições normais. Incidentalmente, ele dá o perfil de rendimento/vazão do processo.

Esses testes são o mínimo que podemos fazer com o processo de ETL, pois ele nos mostra como o processo se comportaria no ambiente real.

Complementares

Extendendo os cenários anteriores, dois outros tipos complementam a lista dos testes mais básicos. São eles os testes:

  • De Carga Zero
    O teste de carga zero equivale à re-execução do Teste de Carga Cheia logo após seu fim. Nesta situação, em que o banco de origem não sofreu nenhuma alteração antes de ser reprocessado, e o de destino está com a carga inteira, o resultado deve ser uma carga zero, já que nada mudou. A exceção é a carga de estruturas que arquivam fotos, como fatos históricas. É um teste essencialmente de performance, já que se o processo passou pelo Teste de Carga Cheia, deve passar pelo Teste de Carga Zero.
  • De Carga Padrão
    O último dos testes é rodado com o banco de destino na situação após o Teste de Carga Cheia, mas com uma nova versão do banco de origem, capturado em momento diferente da versão usada nos testes de carga cheia e zero (que são a mesma.) Este teste fornece indicadores do rendimento do processo “em cruzeiro”, que dizer, nas condições que o processo enfrentaria refresh após refresh.

Esses dois testes, e especial o segundo da dupla, são os que permitem a criação de melhorias no processo de ETL. Podemos avaliar as mudanças feitas no processo de ETL em busca de mais performance rodando o cenário de Carga Padrão repetidas vezes. Claro que um preparo especial do ambiente é necessário, pois devemos resetar o DW à condição de carga inicial, descartando as inserções causadas pela carga das diferentes versões de bases de origem. Todas essas condições e configurações particulares podem render o processo de testes lento ao ponto de não permitir mais que um teste por dia, mas pelo menos não impactam o processamento operacional da organização.

Até agora estamos jogando o processo de ETL contra um mundo ideal, na qual as bases de origem não escondem surpresas.

Falhas

Só que testes de verdade, quando escritos para esgotar as possibilidades de um pedaço de código, alimentam condições que deveriam disparar um erro no processamento. Um teste de um código em Java, por exemplo, pode forçar situações de erro para descobrir se os tratamentos de exceções estão funcionando conforme esperado.

O último cenário é o mais complexo. Por falta de um nome melhor, temos o teste:

  • De Carga Podre
    Uma massa de dados igual ao Teste de Carga Padrão, mas eivada com todo tipo de erro possível.

Essa massa de dados dá um bocado de trabalho para ser produzida, já que precisa ter dados errados, mas não totalmente inúteis. Um campo com tipo errado aqui, uma linha nula ali… Pior: já vi casos em que o banco de dados corrompe algum conteúdo, e um teste contra essa situação precisaria forçar esse comportamento do servidor de bancos. E como fazer backup de uma banco com um erro destes?

Esses testes são, também IMHO, obrigatórios, já que são eles que atestarão a robustez do processo de ETL.

Conclusão

Os processos de ETL que eu construo passam por relativamente poucos testes, e isso me incomoda. Observo projetos que são acometidos por novos erros de carga às vezes até diariamente – é como se o DW nunca ficasse “saudável”. Ainda não tenho resposta para isso, mas acredito que o primeiro passo seja construir uma arquitetura de desenvolvimento equipada com testes automáticos. Melhor ainda, equipada com um framework que permita encaixar novos e variados testes. Com um pouco de tempo, essa infraestrutura e novas técnicas de teste podem melhorar a atualização do DW, dando mais um passo em direção à qualidade total também nos dados para BI.

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s