1

I am trying to implement a code which makes request to external REST endpoint and when that endpoint returns a 404, it should retry for finite times.

The HttpRequest is something like

val responseFuture = Http().singleRequest(HttpRequest(method = requestMethod,
    uri = url,
    entity = HttpEntity(requestBody).withContentType(ContentTypes.`application/json`)
))

and the response is handled as

responseFuture.onComplete {
    case Success(r) =>
      if (r.status.isFailure()) Future.failed(new Exception("request failed with status 404"))
      else r
    case Failure(e) => throw e
}

My retry logic is:

def retryFuture[T](retries: Int, delay: FiniteDuration = 1.second)(fn: => Future[T])(implicit ec: ExecutionContext, s: Scheduler): Future[T] = {
    fn.recoverWith {
      case _ if retries > 0 => after(delay, s)(retryFuture(retries - 1, delay)(fn))
    }
}

The problem is when the endpoint returns 404, it comes as SUCCESS(HttpResponse(404,...) and so the retry is not working. Can anyone point out what can be done to resolve this?

Alexey Subach
  • 11,903
  • 7
  • 34
  • 60
nik266
  • 13
  • 5

2 Answers2

0

onComplete doesn't change the success or failure of the future; in fact, the returned value of the function is discarded. Instead, either do your retry in the onComplete, or use transform to get a new failed Future if it's a 404 and then recoverWith.

Vitruvie
  • 2,327
  • 18
  • 25
0

You should define responseFuture as a def rather than a val, to be able to run it several times.

Then you just need to do responseFuture.filter(_.status.isSuccess()) to have a Future.failed on 404 return code (actually on all error codes).

You can now use your recover strategy on this.

Cyrille Corpet
  • 5,265
  • 1
  • 14
  • 31