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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With