Múltiplas tentativas de execução em Delphi (Retry)

Existem momentos na execução do código que é previsível que ele falhe. Não estamos falando de um simples “try except”, mas de problemas que fogem ao controle do desenvolvedor, que vai além de sua capacidade de atuar dentro do fluxo de operação e que muitas vezes tem forte relação com questões de infra-estrutura.

Em um modelo tradicional de aplicações Enterprise Desktop – o que acredito que seja a massiva categoria dos programas desenvolvidos em Delphi – é muito difícil encontrar situações onde haja a necessidade de realizar múltiplas tentativas de execução. Com isso, refiro-me ao famoso “retry”, onde existe a tentativa de execução de um trecho de código e, caso haja qualquer tipo de problema, ela é refeita tantas vezes quanto necessário – ou parametrizado – como uma espécie de “tudo bem, mas vamos tentar de novo, na esperança que agora vai…”.

Exemplos desses casos são acessos a dispositivos periféricos, dependências de rede, e outras dependências que fogem ao cerne do trinômio cpu-memória-hd. Esses, por sua natureza e histórico de evolução, mostram-se tão maduros que pouco ou nada deixam a desejar hoje em dia quanto à possibilidade de falhas. Elas existem? Claro, sem sombra de dúvidas. Mas desenvolver sistemas pensando nisso pode ser, na atualidade, um gasto – não se engane, tudo custa alguma coisa – sendo que existem diversos outros pontos maiores de preocupação e que vão demandar muito mais tempo e atenção.

Por outro lado, ao analisarmos problemas como requests que trafegam pela Internet, qual seria a taxa disso? Ridiculamente comum. Com o crescimento de sistemas distribuídos e crescente adoção de micro serviços, a comunicação quase sempre é feita através de web-services REST. Isso tudo acaba trafegando via HTTP através da Internet, onde tudo pode ocorrer, desde um nó de computador desligado, quanto problemas atmosféricos prejudicando a qualidade do sinal – quem viveu a época dos discadores modem sabe bem o que é isso.

Existem diversos Frameworks em outras linguagens que fornecem facilidades para esses tipos de situação, contudo, em Delphi, não vejo isso tão disseminado.

Já há algum tempo transitou em nosso meio um artigo sobre uma classe helper que disporia facilmente a qualquer objeto a possibilidade de realizar esse retry: https://www.danielespinetti.it/2017/06/simple-retry-mechanism-in-delphi.html

Contudo, não sou adepto aos helpers do Delphi. Não vejo problema nenhum em helpers – em diversas outras linguagens isso funciona muito bem -, apenas não gosto como o Delphi gerencia isso. Isso basicamente porque se houver a utilização de mais de uma classe helper na mesma unit, uma delas já não vai funcionar. Ou seja, não há como criar uma estratégia sadia para isso.

Abaixo encontra-se a implementação de uma classe para realizar múltiplas tentativas de execução de código e que, a meu ver, funcionará tão satisfatoriamente quanto um helper – talvez não de forma tão elegante – mas com certeza muito flexível:

A classe TRetrier implementa a interface IRetrier. Assim, fica muito fácil utilizá-la até porque facilita o gerenciamento de memória.

Outro ponto é o tipo de dados record chamado TRetryExecutionResult. Ele é um tipo de dados preparado para receber informações e identificar:

Result – Resultado. Por se tratar de um tipo TValue (Para saber mais, acesse: http://edgarpavao.com/2017/07/01/um-pouco-sobre-o-tvalue/), ele tem flexibilidade suficiente para retornar diversos tipos de dados.

Error – Informa se houve algum tipo de erro.

ErrorCode – Código do erro, caso haja algum tipo de erro, o codigo de erro pode ser informado para identificar e dar uma resposta posterior.

StopExecution – Indica se deve impedir a “re-execução” do código. Existem alguns tipos de cenários onde, mesmo que haja a possibilidade de utilizar o “retry”, não é viável que isso seja feito. Assim, é possível evitar essa re-execução alimentando essa informação. Imagine que você está se comunicando com uma API e essa API retorna um erro de acesso negado. Não adianta ficar tentando vária vezes com as mesmas credenciais. Outra estratégia deve ser vista.

A interface IRetry nos fornece o método Retry, que possue 4 parâmetros:

AFunc – É a função que será executada. Ela retorna justamente o tipo TRetryExecutionResult, que orientam o funcionamento da classe de múltiplas execuções.

AQtyTimesToTry – É a quantidade de tentativas caso hajam falhas de execução. Um valor de 3, indica que o código deverá ser executado 3 vezes.

ADelay – É o tempo em milissegundos que serão aguardados até a próxima execução, caso hajam falhas.

AErrorOutputMessage – Código que será executado caso hajam problemas. Ele é executado quanto o retorno da função (AFunc) demonstra que ocorreu um erro e também caso a quantidade de tentativas já tenha sido atingida.

TRetryExecutionErrorCode é um valor string que retorna o código do erro, e TRetryExecutionTimesExceeded um valor booleano, que retorna quando a quantidade de vezes de tentativas de execução já foram alcançadas. Dessa forma, é possível criar uma saída personalizada para cada erro dentro da execução. Exception é a própria exceção caso ela ocorra na execução do código. Caso contrário será nil.

Assim, basicamente basta passar para a classe o código que será executado, a quantidade de vezes que deverá ser re-executado em ocorrência de erros, quanto tempo aguardar entre cada execução e a procedure de tratamento de erro caso necessário.

Veja abaixo o exemplo de consumo dessa classe de Retry:

O CodigoExecucao é um simples código de consumo de um webservice. Caso haja problemas (StatusCode <> 200) ele retorna essa informação.

SaidaMensagensErro é a procedure de tratamento de erros, caso ocorrem. Perceba que se o StatusCode for diferente de 200, CodigoExecucao vai pontuar isso e SaidaMensagemErro terá essa informação para utilizar em seu fluxo de tratamento.

No final, basta chamar Retrier.Retry e você terá a garantia de execução quantas vezes desejar.

A abordagem aqui é um pouco mais complexa comparada àquela dada por Daniele Spinetti, contudo, é mais flexível e íntegra porque prevê os tratamentos de erros provenientes de suas execuções, e pronta para ser utilizada em projetos profissionais.

Identificou onde poderia utilizar uma abordagem dessa em seus projetos? Avise-me nos comentários. Gosto muito de saber que essas informações foram úteis não só a mim, mas a todos vocês.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *