Esse artigo faz parte da revista Java Magazine edição 50. Clique aqui para ler todos os artigos desta edição

FONT-FAMILY: Verdana">"> 

Neste artigo, vamos explorar um tema importante para o sucesso de projetos de missão crítica: como criar aplicações tolerantes a faltas (faults). O desafio de escrever este artigo é que o tema é considerado árduo, talvez por muitas vezes ser tratado de forma puramente teórica ou processual. Assim, já tranqüilizo o leitor que gosta de programação, avisando que procurei uma abordagem prática, direcionada à tecnologia Java (especialmente Java EE) e baseada em experiência de projetos reais. Teremos que começar com alguns conceitos inevitáveis, mas logo ilustraremos tudo com exemplos de código ou discussões situadas no contexto do Java (recursos de linguagens, runtimes, APIs etc.). Os artigos que falam do tema de tolerância a faltas são, de fato, na maioria artigos sobre clusters. Clusters são uma parte importante da solução, mas são um assunto já bem coberto, inclusive aqui na Java Magazine, por exemplo no artigo “Clusters Web com Tomcat” (Edição 29). Um cluster só protege sua aplicação de um tipo muito específico de falta: a pane total (onde um processo trava ou é abortado). Neste artigo vamos falar de muitas coisas, mas não de clusters. Aliás, uma aplicação realmente tolerante a faltas só se beneficia de clusters em casos raros (esperamos!) de pane externa à aplicação, como um HD pifado ou um crash do sistema operacional.

 

Conceitos

Uma aplicação tolerante a faltas não é a mesma coisa que uma aplicação sem bugs – por dois motivos. Primeiro, porque não existe aplicação sem bugs; pelo menos não acima de um grau de complexidade que hoje em dia caracteriza qualquer aplicação “séria”. Segundo, porque não ter bugs está longe de ser suficiente. A maioria das aplicações depende de diversos fatores externos – que estão sujeitos a toda sorte de comportamentos indesejáveis: conexões que caem, usuários que entram dados inválidos, SGBDs com concorrência no acesso às tabelas, e assim por diante. A terminologia resumida na Tabela 1 é comum na literatura de engenharia de software (e mesmo na engenharia em geral). Iremos utilizá-la de forma estrita neste artigo. Repare nos termos equivalentes em inglês. É comum se dizer, em português, “tolerância a falhas”, mas isso é impreciso. Existe uma relação de causalidade entre os três primeiros conceitos: Faltas produzem Falhas, e Falhas produzem Erros. Estamos habituados a ver softwares como sistemas em camadas, numa organização vertical que vai do mais baixo nível (ex.: hardware ou S.O.) ao mais alto (ex.: regras de negócio ou interface com o usuário). Pense, então, que estas criaturas indesejáveis – Faltas, Falhas e Erros – se originam em alguma camada do sistema, e se não tratadas, propagam-se para as camadas superiores. Por exemplo, uma Falta na camada de hardware “bloco ruim no HD” pode gerar a Falha na camada de S.O. “erro na leitura do arquivo EMAILS.DAT”, terminando num Erro na camada de aplicação, “notificações por e-mail não funcionam”. O mesmo é válido para as camadas internas de uma única aplicação, ex.: uma Falta na camada de acesso a dados como mas logo ilustraremos tudo com exemplos de código ou discussões situadas no acesso às tabelas, e assim por diante. A terminologia resumida na é comum na literatura de engenharia de software (e mesmo na engenharia em geral). Iremos utilizá-la de forma estrita neste artigo. Repare nos termos equivalentes em inglês. É comum se dizer, em português, “tolerância a é impreciso. Existe uma relação de causalidade entre os três primeiros conceitos: Faltas produzem Falhas, e Falhas produzem Erros. Estamos habituados a ver softwares como sistemas em camadas, numa organização vertical que vai do mais baixo nível (ex.: hardware ou S.O.) ao mais alto (ex.: regras de negócio ou interface com o usuário). Pense, então, que estas criaturas indesejáveis – Faltas, Falhas e Erros – se originam em alguma camada do sistema, e se não tratadas, propagam-se para as camadas superiores. uma Falta na camada de hardware “bloco ruim no HD na camada de S.O. “ arquivo EMAILS.DAT Erro na camada de aplicação, “ por e-mail não funcionam O mesmo é válido para as camadas internas de uma única aplicação, ex.: uma Falta na  camada de acesso a dados como “query incorreta em UsuarioDAO.update()” pode propagar-se até a camada de apresentação, onde resultará em vários Erros, como “Tela de alteração de senha não funciona”, ou “Após três tentativas de login com senha errada, usuário não é bloqueado¹ ” – e qualquer outra funcionalidade que exija alterar um registro de usuário. Esse exemplo ilustra que os relacionamentos Falta -> Falha e Falha -> Erro são muito freqüentemente do tipo 1-para-N: uma única Falta pode gerar várias Falhas diferentes e uma única Falha pode gerar vários Erros diferentes.

 

Nota 1: Este seria um exemplo de erro “silencioso”, que a maioria dos usuários finais não perceberia. Mas software não é mecânica quântica: um erro é sempre um erro, mesmo que não seja observado como tal pelo usuário.

 

 

Bugs, ocorrências e suporte

Existem outros detalhamentos possíveis para cada conceito. Faltas, Falhas e Erros podem ser categorizados conforme o tipo, recorrência, severidade e outros critérios. Quem já usou softwares de acompanhamento de bugs (bug tracking, como Bugzilla ou JIRA) já tem familiaridade com isso. E qual é a relação entre Faltas/Falhas/Erros e Bugs? “Bug” é um conceito mais geral que engloba os outros – tanto usuários finais quanto desenvolvedores habituados a usar o bug tracking para organizar seu trabalho costumam chamar tudo de “bug”, inclusive coisas como requisições de mudanças e de novas funcionalidades, e até esclarecimentos de dúvidas ou equívocos de usuários². Com esse escopo mais amplo, muitos preferem substituir “bug” por um termo mais adequado, como issue (ocorrência). Vamos preferir esse termo, quando aplicável, também aqui.

 

Nota 2: Isso acaba sendo um abuso do bug tracking, pois em muitos projetos, especialmente quando o fornecedor é cobrado pelos tempos de resolução de bugs, os usuários logo percebem que a maneira mais fácil de ver qualquer tipo de solicitação atendida é registrá-la como bug.

 

 

 Tabela 1. Termos essenciais em Tolerância a Faltas

 

Usuários finais costumam reportar Erros, mas não Faltas ou Falhas, pois o usuário não costuma ter visibilidade das causas de um problema – só vê os sintomas. A equipe de suporte ou de desenvolvimento irá diagnosticar as causas de um Erro (determinar as Faltas e Falhas). Esse tipo de diagnóstico auxilia muito o processo de suporte e correção, pois resulta em tarefas de correção mais precisas e objetivas. Pode também resultar em nenhuma tarefa de correção, quando a ocorrência na verdade é apenas um erro ou dúvida do usuário. Um programador que recebe a tarefa de resolver uma ocorrência que contém apenas a descrição de um Erro, como “Tela de alteração de senha não funciona”,  terá que fazer um trabalho de investigação talvez bem maior do que a correção em si. O mesmo programador, recebendo  a descrição de uma Falta como “query incorreta em UsuarioDAO.update()”, resolverá o  problema rapidamente. Ou seja, o grosso do trabalho de suporte e manutenção não é um trabalho de desenvolvimento, e sim de Análise de Falhas. Um fornecedor de software pode definir um processo onde esta análise seja executada de uma maneira bem definida, por pessoal diferente. Talvez pelos mesmos profissionais responsáveis por testes,  que são quem mais entende de coisas que não funcionam. Pois na maioria das vezes os programadores são um gargalo, e sobrecarregá-los com todo o trabalho de suporte resulta em atrasos das próximas entregas de funcionalidade.

 

Análise de falhas

Por exemplo, numa aplicação que chamaremos A, a falta “Tabela VENDAS contém valor inválido num_itens = -17 produz a Falha “retorno de dados incorretos na query de vendas”, e esta produz o Erro “relatório de vendas incorreto apresentado na GUI”. Análise de Falhas (como indica o nome) é o processo de diagnóstico de falhas, com o objetivo de determinar sua causa – no caso a(s) falta(s) – e determinar como evitar novas falhas do mesmo tipo³. Nosso exemplo pode parecer óbvio, mas não se deixe enganar pelas aparências. Pode ser que a tabela que estamos lendo seja populada por uma outra aplicação B, para a qual um registro reportando -17 vendas num dia não é inválido: esta aplicação usa valores negativos para representar itens devolvidos. Para a aplicação A, que não tem interesse em devoluções, mas só no movimento de vendas mesmo, o correto seria filtrar os registros: WHERE num_itens >= 0.

 

Nota 3: Alguém poderia argumentar que o certo seria “Análise de Erros”, já que a maioria dos problemas (pelo menos os identificados por usuários finais) costuma ser reportada como Erros. Mas a que usei é a terminologia comum. Talvez porque, normalmente, deduzir a Falha a partir do Erro seja uma tarefa óbvia (para quem conhece o sistema por dentro), mas deduzir a Falta seja bem mais difícil; daí a necessidade de uma disciplina de análise.

 

 

Nesse caso, uma Falha só aconteceria se A esquecesse de fazer esse filtro. A Falta então estaria na aplicação A, não em B  que gerou os dados, e nem nos próprios dados. Mistério resolvido? Ainda não. Aqui considero que a verdadeira Falta foi ou de design ou de documentação. No lado do design, essa idéia de reusar a mesma tabela de vendas para registrar devoluções, jogando com o sinal da coluna num_itens, é um truque no mínimo confuso. Essa tabela única reusada para dois conceitos distintos – venda e devolução – não segue nem a Primeira Forma Normal. Essas teorias às vezes são encaradas como pouco práticas, mas no exemplo, segui-las ajudaria a evitar erros. Talvez essa modelagem não-normalizada seja justificada, por exemplo, como uma otimização (com ela, consultas que precisem ler tanto vendas quanto devoluções, digamos para estimar o estoque atual, não precisariam de um join). Mas se for este o caso, a Falta é de documentação. O dicionário de dados da tabela VENDAS, que foi criada para a aplicação A, deveria documentar o fato que a coluna num_itens pode conter valores negativos, além de  especificar o que tais valores significam, pois isso não é nada intuitivo. Se esta documentação tivesse sido feita, a outra equipe que posteriormente implementou a aplicação B acessando os mesmos dados, não teria deixado de fazer o filtro.

 

Validação de dados

 

Nota: Esta seção e a próxima ainda não tratam propriamente de tolerância a faltas, e sim, de prevenção de um tipo específico de faltas, os bugs de implementação. Mas como prevenir é sempre melhor do que consertar, é importante cobrir também este material. Outra técnica importante nesse mesmo contexto, que não repetiremos aqui  porque cobrimos há não muito tempo, é o uso de ferramentas de validação de código (como PMD, FindBugs e Checkstyle, ou as validações adicionais do compilador do Eclipse); para isso, veja o artigo “Qualidade Aplicada” na Edição 36.

 

 

Dizer que todos os dados devem se validados parece ser chover no molhado. Todos sabem disso, qualquer bom desenvolvedor valida seus dados de entrada, não? Por exemplo, numa GUI, todos os campos que aceitam digitação livre devem ser “protegidos” por restrições como valores máximo e mínimo, expressões regulares etc. Em interfaces web, por exemplo, é fácil fazer isso com o Jakarta Commons Validator 4 ...

Quer ler esse conteúdo completo? Tenha acesso completo