Tente novamente um bloco com falha no Scala

Eu vi algumas implementações disso online, em particular Retrying with style , que é uma postagem de dois anos e tem algumas coisas que me incomodaram:

  1. está bloqueando, Futuros onde não estão em escala no momento,
  2. ele usa variáveis ​​mutáveis, que não são intrinsecamente ruins em tal contexto contido; mas meu editor os destaca em vermelho e eu não gosto de ter pedaços vermelhos em meu código: D

Portanto, criei uma nova implementação disponível nesta essência .

A ideia é que você poderia ter um bloco de código que falha por qualquer tipo de razão que não seja determinística. Por exemplo, uma chamada para o banco de dados pode falhar porque a fila de conexão está cheia, ou uma solicitação para um serviço da web pode falhar porque a “internet está ocupada”.

Em vez de escrever um loop e muitos códigos relevantes para a parte de nova tentativa, você pode escrever um wrapper de bloco em scala que cuidará de tudo em segundo plano para você:

val myResult = retry(10) {
...
makeWSCall
()
...
}

Isso é o que o pequeno código que coloquei nesta essência faz. Não vou copiar tudo aqui porque é muito longo, mas aqui estão alguns comentários sobre como funciona:

  • Eu uso a Promisepara eventualmente incorporar um valor em a Future. O futuro dessa promessa é retornado diretamente e as tentativas são executadas de forma assíncrona.
  • se uma exceção for detectada, ele tentará novamente, a menos que tenha esgotado o número fornecido de novas tentativas, caso em que falhará no futuro.
  • assim que o resultado puder ser calculado sem exceções, ele será retornado no futuro.

Como o bloco retorna a Future, as novas tentativas são executadas de forma assíncrona, com sorte não bloqueando o fluxo de dados. Você pode usar a FutureAPI para manipular o valor eventualmente retornado:

myResult.map(_ * 2)

Você também pode usar os métodos de recuperação integrados de Futurepara lidar com um resultado com falha (muitas tentativas):

val myResult = retry(10) {
...
makeWSCall
()
...
} recover {
case t: Throwable => 0
}

A retryfunção também fornece alguns parâmetros opcionais avançados:

  • além da contagem máxima obrigatória de novas tentativas, você pode definir um prazo opcional após o qual ele deve simplesmente desistir:
val myResult = retry(10, Some(10 seconds fromNow)) {
...
makeWSCall
()
...
}
  • por padrão, o bloco será repetido com um back-off exponencial , já que é freqüentemente usado para lidar com recursos que não gostam de ser sobrecarregados, você pode alterar este padrão se desejar
val myResult = retry(10, backoff = (r) => 100 milliseconds) {
...
makeWSCall
()
...
}
  • se você deseja que o bloco falhe sem nova tentativa para exceções específicas, você pode especificar uma função de filtragem com o ignoreThrowableparâmetro opcional