Reputation: 241
I am trying to learn Scala and it seems like a very powerful language so far, but some things seem hard to achieve, may be its just my learning curve. I have researched online for a few days but could not find a good solution to do what I want to do.
I have multiple methods (with different signatures, including return type) which I want to wrap around in a retry logic.
I want to keep calling a method for a predefined number of times until the method succeeds.
Here is an example:
def downloadLocal(memory: Boolean, userName: Name, version: Int): Int
def downloadRemote(
memory: Boolean, userName: Name, masterNodeId: String, masterNodeId: String
): Pair(Int, Int)
I want wrap these two method inside the retry logic. Here is my attempt at the retry logic:
trait WithRetry{
def withRetry(retry :Int){
try{
callBack
}catch{
case exception:Throwable =>
if(retry>0){
logger.warn(exec+" method failed with an exception. retry number is:" + retry);
logger.warn(exception);
withRetry(retry-1)
}
else{
throw exception
}
}
}
def callBack():Any
}
The problem I have is that I am not able to find a clean way of wrapping around my methods (downloadRemote
and downloadLocal
) inside the retry logic.
Any suggestions/thoughts?
Upvotes: 1
Views: 437
Reputation: 9820
You can just define a generic function :
import scala.util.{Try, Success, Failure}
def retry[T](times: Int)(block: => T): T = Try(block) match {
case Success(result) => result
case Failure(e) =>
if (times > 0) {
logger.warn(s"method failed with an exception, retry #$times")
logger.warn(exception)
retry(times - 1)(block)
}
else throw e
}
This is a tail recursive function, so it will be as efficient as calling the function in a for loop in Java.
However more idiomatic Scala would be to return a Try
:
def retry2[T](times: Int)(block: => T): Try[T] =
Try(block) match {
case Failure(e) if (times > 0) =>
logger.warn(s"method failed with an exception, retry #$times")
logger.warn(exception)
retry2(times - 1)(block)
case other => other
}
Both versions could be used as :
def failRandomly: Int =
if (scala.util.Random.nextInt < 0.80) throw new Exception("boom")
else 1
scala> retry(2)(failRandomly)
res0: Int = 1
scala> retry(2)(failRandomly)
java.lang.Exception: boom // ...
scala> retry2(2)(failRandomly)
res1: scala.util.Try[Int] = Success(1)
scala> retry2(2)(failRandomly)
res2: scala.util.Try[Int] = Failure(java.lang.Exception: boom)
Upvotes: 3