Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Recursive Function with Future Return Type

I am writing a recursive retry function in scala where I want to know if there was a runtime error with the future creation. If there is then future instantiation is retried.

Imagine I have a database query function:

dbLookup(userId : UserId) : Option[UserName] = ???

The retry would look something of the form:

retry[T](f : () => Future[Option[T]],  
         notifyFailure : (t : Throwable) => Unit,
         n : Int) : Future[Option[T]] = {
  if(n <= 0) { Future{None} }
  else {
    val fut = f()
    if(f.resultsInException) {      //I don't know how to write this
      notifyFailure(f.exception)
      retry(f, notifyFailure, n-1) //try again
    }
    else {
      f.originalValueAsFuture
    }
  }
}

How can this future functionality be implemented and allow for tail recursion optimization?

This function could be used to retry the database 10 times for a user if the execution context keeps throwing an exception when I try to create a Future:

val userNameAfterRetries = 
  retry(() => Future{dbLookup("1234")},
        (t) => system error (s"future creation error : $t"),
        10)

Note: this is sort of possible with Future.fallbackTo, but unfortunately fallbackTo takes in a Future[T] rather than a () => Future[T]. This is important because using fallbackTo would result in retrying at least 1 extra times even if the first attempt was successful.

Thank you in advance for your consideration and response.

like image 412
Ramón J Romero y Vigil Avatar asked Mar 16 '26 06:03

Ramón J Romero y Vigil


1 Answers

How about this?

  def retry[T](f: () => Future[Option[T]],
               notifyFailure: Throwable => Unit,
               n: Int)(implicit ec : ExecutionContext): Future[Option[T]] = {
    if (n <= 0) Future.failed(new RuntimeException("Exceeded number of allowed retries"))
    else f().recoverWith { case originalError => notifyFailure(originalError); retry(f, notifyFailure, n - 1) }
  }

Update on tail recursion: The nature of Future is asynchronous so unless you want to await for result, I do not quite see it being possible to make it @tailrec because you will have to use recursion in callback.

Also practical note: if you know it is always ~10 retries, I would not be afraid of recursion.

like image 94
tkachuko Avatar answered Mar 18 '26 21:03

tkachuko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!