score:204
Recursion + first class functions by-name parameters == awesome.
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e =>
if (n > 1) retry(n - 1)(fn)
else throw e
}
}
Usage is like this:
retry(3) {
// insert code that may fail here
}
Edit: slight variation inspired by @themel's answer. One fewer line of code :-)
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e if n > 1 =>
retry(n - 1)(fn)
}
}
Edit Again: The recursion bothered me in that it added several calls to the stack trace. For some reason, the compiler couldn't optimize tail recursion in the catch handler. Tail recursion not in the catch handler, though, optimizes just fine :-)
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
val r = try { Some(fn) } catch { case e: Exception if n > 1 => None }
r match {
case Some(x) => x
case None => retry(n - 1)(fn)
}
}
Edit yet again: Apparently I'm going to make it a hobby to keep coming back and adding alternatives to this answer. Here's a tail-recursive version that's a bit more straightforward than using Option
, but using return
to short-circuit a function isn't idiomatic Scala.
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
try {
return fn
} catch {
case e if n > 1 => // ignore
}
retry(n - 1)(fn)
}
Scala 2.10 update. As is my hobby, I revisit this answer occasionally. Scala 2.10 as introduced Try, which provides a clean way of implementing retry in a tail-recursive way.
// Returning T, throwing the exception on failure
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
util.Try { fn } match {
case util.Success(x) => x
case _ if n > 1 => retry(n - 1)(fn)
case util.Failure(e) => throw e
}
}
// Returning a Try[T] wrapper
@annotation.tailrec
def retry[T](n: Int)(fn: => T): util.Try[T] = {
util.Try { fn } match {
case util.Failure(_) if n > 1 => retry(n - 1)(fn)
case fn => fn
}
}
score:0
This project seems to provide some nice implementations for different retry mechanisms https://github.com/hipjim/scala-retry
// define the retry strategy
implicit val retryStrategy =
RetryStrategy.fixedBackOff(retryDuration = 1.seconds, maxAttempts = 2)
// pattern match the result
val r = Retry(1 / 1) match {
case Success(x) => x
case Failure(t) => log("I got 99 problems but you won't be one", t)
}
score:0
//Here is one using Play framework
def retry[T](times:Int)(block: => Future[T])(implicit ctx: ExecutionContext):Future[T] = {
type V = Either[Throwable,T]
val i:Iterator[Future[Option[V]]] =
Iterator.continually(block.map(t => Right(t)).recover { case e => Left(e) }.map(t => Some(t)))
def _retry:Iteratee[V,V] = {
def step(ctr:Int)(i:Input[V]):Iteratee[V,V] = i match {
case Input.El(e) if (e.isRight) => Done(e,Input.EOF)
case _ if (ctr < times) => Cont[V,V](i => step(ctr + 1)(i))
case Input.El(e) => Done(e,Input.EOF)
}
Cont[V,V](i => step(0)(i))
}
Enumerator.generateM(i.next).run(_retry).flatMap { _ match {
case Right(t) => future(t)
case Left(e) => Future.failed(e)
}}
}
score:0
Minor improvement to printout attempt x of N
// Returning T, throwing the exception on failure
@annotation.tailrec
final def retry[T](n: Int, name: String ="", attemptCount:Int = 1)(fn: => T): T = {
logger.info(s"retry count: attempt $attemptCount of $n ....... function: $name")
try {
val result = fn
logger.info(s"Succeeded: attempt $attemptCount of $n ....... function: $name")
result
} catch {
case e: Throwable =>
if (n < attemptCount) { Thread.sleep(5000 * attemptCount); retry(n, name, attemptCount+1)(fn) }
else throw e
}
}
score:1
If you want control of which exceptions you retry, you can use methods in scala.util.control.Exception
:
import java.io._
import scala.util.control.Exception._
def ioretry[T](n: Int)(t: => T) = (
Iterator.fill(n){ failing[T](classOf[IOException]){ Option(t) } } ++
Iterator(Some(t))
).dropWhile(_.isEmpty).next.get
(As written, it will also retry on null; that's the Option(t)
part. If you want nulls to be returned, use Some(t)
inside the iterator fill instead.)
Let's try this out with
class IoEx(var n: Int) {
def get = if (n>0) { n -= 1; throw new IOException } else 5
}
val ix = new IoEx(3)
Does it work?
scala> ioretry(4) { ix.get }
res0: Int = 5
scala> ix.n = 3
scala> ioretry(2) { ix.get }
java.io.IOException
at IoEx.get(<console>:20)
...
scala> ioretry(4) { throw new Exception }
java.lang.Exception
at $anonfun$1.apply(<console>:21)
...
Looks good!
score:1
I ended up adapting a previous answer to allow filtering on which exceptions to retry on:
/**
* Attempt 'fn' up to 'attempts' times, retrying only if 'forExceptions' returns true for retry-able exceptions.
*/
def retry[T](attempts: Int, forExceptions: (Throwable) => Boolean)(fn: => T): T =
{
// toStream creates a lazily evaluated list, which we map to a try/catch block resulting in an Either
val tries = (1 to attempts).toStream map
{
n =>
try
Left(fn)
catch
{
case e if forExceptions(e) => Right(e)
}
}
// find the first 'Either' where left is defined and return that, or if not found, return last
// exception thrown (stored as 'right'). The cool thing is that because of lazy evaluation, 'fn' is only
// evaluated until it success (e.g., until Left is found)
tries find (_ isLeft) match
{
case Some(Left(result)) => result
case _ => throw tries.reverse.head.right.get
}
}
You can call in two ways:
val result = retry(4, _.isInstanceOf[SomeBadException])
{
boom.doit()
}
or with partial functions (also showing version where don't care about return value)
def pf: PartialFunction[Throwable, Boolean] =
{
case x: SomeOtherException => true
case _ => false
}
retry(4, pf)
{
boom.doit()
}
score:1
This solution is not optimized by compiler to tail recursion for some reason (who knows why?), but in case of rare retries would be an option:
def retry[T](n: Int)(f: => T): T = {
Try { f } recover {
case _ if n > 1 => retry(n - 1)(f)
} get
}
Usage:
val words: String = retry(3) {
whatDoesTheFoxSay()
}
End of the answer. Stop reading here
Version with result as a Try:
def reTry[T](n: Int)(f: => T): Try[T] = {
Try { f } recoverWith {
case _ if n > 1 => reTry(n - 1)(f)
}
}
Usage:
// previous usage section will be identical to:
val words: String = reTry(3) {
whatDoesTheFoxSay()
} get
// Try as a result:
val words: Try[String] = reTry(3) {
whatDoesTheFoxSay()
}
Version with a function returning Try
def retry[T](n: Int)(f: => Try[T]): Try[T] = {
f recoverWith {
case _ if n > 1 => reTry(n - 1)(f)
}
}
Usage:
// the first usage section will be identical to:
val words: String = retry(3) {
Try(whatDoesTheFoxSay())
} get
// if your function returns Try:
def tryAskingFox(): Try = Failure(new IllegalStateException)
val words: Try[String] = retry(3) {
tryAskingFox()
}
score:1
A reusable object/method with a pause between attempts:
Retry(3, 2 seconds) { /* some code */ }
Code:
object Retry {
def apply[A](times: Int, pause: Duration)(code: ⇒ A): A = {
var result: Option[A] = None
var remaining = times
while (remaining > 0) {
remaining -= 1
try {
result = Some(code)
remaining = 0
} catch {
case _ if remaining > 0 ⇒ Thread.sleep(pause.toMillis)
}
}
result.get
}
}
score:2
I like the accepted solution, but suggest checking the exception is NonFatal:
// Returning T, throwing the exception on failure
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
Try { fn } match {
case Success(x) => x
case _ if n > 1 && NonFatal(e) => retry(n - 1)(fn)
case Failure(e) => throw e
}
}
You don't want to retry a control flow exception, and usually not for thread interrupts...
score:3
There is an existing library that can help with that, called retry, and there is a Java library too, called guava-retrying.
Here are some examples of using retry:
// retry 4 times
val future = retry.Directly(4) { () => doSomething }
// retry 3 times pausing 30 seconds in between attempts
val future = retry.Pause(3, 30.seconds) { () => doSomething }
// retry 4 times with a delay of 1 second which will be multipled
// by 2 on every attempt
val future = retry.Backoff(4, 1.second) { () => doSomething }
score:4
I'd suggest this -
def retry[T](n: Int)(code: => T) : T = {
var res : Option[T] = None
var left = n
while(!res.isDefined) {
left = left - 1
try {
res = Some(code)
} catch {
case t: Throwable if left > 0 =>
}
}
res.get
}
It does:
scala> retry(3) { println("foo"); }
foo
scala> retry(4) { throw new RuntimeException("nope"); }
java.lang.RuntimeException: nope
at $anonfun$1.apply(<console>:7)
at $anonfun$1.apply(<console>:7)
at .retry(<console>:11)
at .<init>(<console>:7)
at .<clinit>(<console>)
at RequestResult$.<init>(<console>:9)
at RequestResult$.<clinit>(<console>)
at RequestResult$scala_repl_result(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988)
at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter....
scala> var i = 0 ;
i: Int = 0
scala> retry(3) { i = i + 1; if(i < 3) throw new RuntimeException("meh");}
scala> i
res3: Int = 3
It can probably be improved to be more idiomatic Scala, but I am not a big fan of one-liners that require the reader to know the entire standard library by heart anyways.
score:4
You can express the idea in functional style using scala.util.control.Exception:
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T =
Exception.allCatch.either(fn) match {
case Right(v) => v;
case Left(e) if (n <= 1) => throw e;
case _ => retry(n - 1)(fn);
}
As we can see, tail recursion can be used here.
This approach gives you the additional benefit that you can parametrize the catch container, so you can only retry a certain subset of exceptions, add finalizers etc. So the final version of retry
might look like:
/** Retry on any exception, no finalizers. */
def retry[T](n: Int)(fn: => T): T =
retry(Exception.allCatch[T], n)(fn);
/** Parametrized retry. */
@annotation.tailrec
def retry[T](theCatch: Exception.Catch[T], n: Int)(fn: => T): T =
theCatch.either(fn) match {
case Right(v) => v;
case Left(e) if (n <= 1) => throw e;
case _ => retry(theCatch, n - 1)(fn);
}
With this, you can do complex stuff like:
retry(Exception.allCatch andFinally { print("Finished.") }, 3) {
// your scode
}
score:6
Here is one possible implementation:
def retry[T](times: Int)(fn: => T) =
(1 to times).view flatMap (n => try Some(fn) catch {case e: Exception => None}) headOption
You can use it like this:
retry(3) {
getClient.putObject(request)
}
retry
also returns Some[T]
if body was processed successfully and None
if body was only throwing exceptions.
Update
If you want to bobble up last exception, then you can take very similar approach but use Either
instead of Option
:
def retry[T](times: Int)(fn: => T) = {
val tries = (1 to times).toStream map (n => try Left(fn) catch {case e: Exception => Right(e)})
tries find (_ isLeft) match {
case Some(Left(result)) => result
case _ => throw tries.reverse.head.right.get
}
}
Also, as you can see, at the end, instead of having only last exception, I have them all. So you can also wrap them in some AggregatingException
if you want and then throw it. (for simplicity, I just throw last exception)
score:8
There is a method in scalaz.concurrent.Task[T]
: http://docs.typelevel.org/api/scalaz/nightly/#scalaz.concurrent.Task
def retry(delays: Seq[Duration], p: (Throwable) ⇒ Boolean = _.isInstanceOf[Exception]): Task[T]
Given a Task[T]
, you can create a new Task[T]
which will retry a certain number of times, where the delay between retries is defined by the delays
parameter. e.g.:
// Task.delay will lazily execute the supplied function when run
val myTask: Task[String] =
Task.delay(???)
// Retry four times if myTask throws java.lang.Exception when run
val retryTask: Task[String] =
myTask.retry(Seq(20.millis, 50.millis, 100.millis, 5.seconds))
// Run the Task on the current thread to get the result
val result: String = retryTask.run
Source: stackoverflow.com
Related Query
- What's the Scala way to implement a retry-able call like this one?
- Whats the scala way to do this
- What is/are the Scala way(s) to implement this Java "byte[] to Hex" class
- What is the easiest way to implement a Scala PartialFunction in Java?
- Is this really the way to pass void functions to Scala methods from Java?
- What is the correct way to implement Producer Consumer in scala
- Scala immutable vs mutable. What is the way one should go?
- The Scala way to use one actor per socket connection
- Is there a way to call a function defined using `val` in Scala with the whole curly brace block as an argument and not the final result of that block?
- Writing my APIs in Scala using Play framework. What's the easiest way to implement OAuth?
- Is there a way to tell the scala compiler not to do a tail call optimization?
- play framework - how can i call this function for the authenticated user in this code play2 scala zentasks
- Idiomatic scala way to implement this simple split-like function
- How to implement something like the strategy pattern in scala
- Which is the best way to write this scala method?
- Scala elegant way to convert a multiple optional objects to a different object if at least one of the objects defined
- What is the correct way to invoke this Scala method on main?
- How to write a One to Many Query with Scala Slick which returns something like this `(Model1, Option[Seq[Model2]])`
- Is there a way to append user input to an array in Scala without overriding my old input every time I call the function?
- why am i not able to return the Dataframe for this scala code
- What is the best possible way to call Hadoop FileSystem operations from Serializable Scala object
- What is the best way to store different type of objects in one data structure scala
- I'm not able to call classes from scala AST. The classes are not being found
- What is the correct way to implement a generic method in scala
- What's the standard way to work with dates and times in Scala? Should I use Java types or there are native Scala alternatives?
- What is an idiomatic Scala way to "remove" one element from an immutable List?
- What is the idiomatic scala way of finding, if a given string contains a given substring?
- How are Scala collections able to return the correct collection type from a map operation?
- Is this the proper way to initialize null references in Scala?
- Why couldn't twitter scale by adding servers the way sites like facebook have?
More Query from same tag
- Can I avoid redundantly casting a Throwable when using catching(...).either?
- Confused about seemingly superfluous respecification of a variable's type in a genericized scala function
- Save multiple csv files to PostgreSQL database using copy command through spark Scala at the same time opening multiple connections
- Where are the other results
- Serializing polymorphic types with µPickle
- Why is this Spring map not injecting into my Scala object?
- Change node in Scala case class tree
- IoC container that supports constructor injection with Scala named/default arguments?
- How to create an optional section in my template that will render near the bottom
- How to use scala class member name as variable
- Scala/scoverage: Is it necessary to make a clean rebuild for releasing a jar for publishing?
- Always getting ClassNotFoundException when trying to debug
- Best way to update a dataframe in Spark scala
- Specs2/Guice issue in Play 2.4.0 functional tests
- Multiply elements in the Spark RDD with each other
- Java: implicit type conversion, or implicit toString() invocation
- Scala: regex with string interpolation for match
- Deploy multi modules Play project
- Scope of Scala's implicit class conversion
- Will using Scala in a more functional way (scalaz) incur a performance/maintainability penalty?
- why does scala not match implicitly on tuples?
- What's the meaning of "value class space is flat"?
- Cassandra Timeouts with No CPU Usage
- Need to validate nested json arrays in play 2.1 in scala
- How can I group and sort columns in spark.rdd
- sbt stage : not a valid command
- Explanation for - No Reflection involved
- spark: SAXParseException while writing to parquet on s3
- Scala converters convert Java collections to Wrapper objects
- Simplify convoluted code with implicits