Skip to main content
Version: 2.x

Retrying

When we are building applications we want to be resilient in the face of a transient failure. This is where we need to retry to overcome these failures.

There are a number of useful methods on the ZIO data type for retrying failed effects:

ZIO#retryโ€‹

The most basic of these is ZIO#retry, which takes a Schedule and returns a new effect that will retry the first effect if it fails, according to the specified policy:

trait ZIO[-R, +E, +A] {
def retry[R1 <: R, S](policy: => Schedule[R1, E, S]): ZIO[R1, E, A]
}

In this example, we try to read from a file. If we fail to do that, it will try five more times:

import zio._

val retriedOpenFile: ZIO[Any, IOException, Array[Byte]] =
readFile("primary.data").retry(Schedule.recurs(5))

ZIO#retryNโ€‹

In case of failure, a ZIO effect can be retried as many times as specified:

import zio._

val file = readFile("primary.data").retryN(5)

ZIO#retryOrElseโ€‹

The next most powerful function is ZIO#retryOrElse, which allows specification of a fallback to use, if the effect does not succeed with the specified policy:

trait ZIO[-R, +E, +A] {
def retryOrElse[R1 <: R, A1 >: A, S, E1](
policy: => Schedule[R1, E, S],
orElse: (E, S) => ZIO[R1, E1, A1]
): ZIO[R1, E1, A1] =
}

The orElse is the recovery function that has two inputs:

  1. The last error message
  2. Schedule output

So based on these two values, we can decide what to do as the fallback operation. Let's try an example:

import zio._

object MainApp extends ZIOAppDefault {
def run =
Random
.nextIntBounded(11)
.flatMap { n =>
if (n < 9)
ZIO.fail(s"$n is less than 9!").debug("failed")
else
ZIO.succeed(n).debug("succeeded")
}
.retryOrElse(
policy = Schedule.recurs(5),
orElse = (lastError, scheduleOutput: Long) =>
ZIO.debug(s"after $scheduleOutput retries, we couldn't succeed!") *>
ZIO.debug(s"the last error message we received was: $lastError") *>
ZIO.succeed(-1)
)
.debug("the final result")
}

ZIO#retryOrElseEitherโ€‹

This operator is almost the same as the ZIO#retryOrElse except it will return either result of the original or the fallback operation:

import zio._

trait LocalConfig
trait RemoteConfig

def readLocalConfig: ZIO[Any, Throwable, LocalConfig] = ???
def readRemoteConfig: ZIO[Any, Throwable, RemoteConfig] = ???

val result: ZIO[Any, Throwable, Either[RemoteConfig, LocalConfig]] =
readLocalConfig.retryOrElseEither(
schedule0 = Schedule.fibonacci(1.seconds),
orElse = (_, _: Duration) => readRemoteConfig
)

ZIO#retryUntil/ZIO#retryUntilZIOโ€‹

We can retry an effect until a condition on the error channel is satisfied:

trait ZIO[-R, +E, +A] {
def retryUntil(f: E => Boolean): ZIO[R, E, A]
def retryUntilZIO[R1 <: R](f: E => URIO[R1, Boolean]): ZIO[R1, E, A]
}

Assume we have defined the following remote service call:

sealed trait ServiceError extends Exception
case object TemporarilyUnavailable extends ServiceError
case object DataCorrupted extends ServiceError

def remoteService: ZIO[Any, ServiceError, Unit] = ???

In the following example, we repeat the failed remote service call until we reach the DataCorrupted error:

remoteService.retryUntil(_ == DataCorrupted)

To provide an effectful predicate we use the ZIO#retryUntilZIO operator.

ZIO#retryUntilEqualsโ€‹

Like the previous operator, it tries until its error is equal to the specified error:

remoteService.retryUntilEquals(DataCorrupted)

ZIO#retryWhile/ZIO#retryWhileZIOโ€‹

Unlike the ZIO#retryUntil it will retry the effect while its error satisfies the specified predicate:

trait ZIO[-R, +E, +A] {
def retryWhile(f: E => Boolean): ZIO[R, E, A]
def retryWhileZIO[R1 <: R](f: E => URIO[R1, Boolean]): ZIO[R1, E, A]
}

In the following example, we repeat the failed remote service call while we have the TemporarilyUnavailable error:

remoteService.retryWhile(_ == TemporarilyUnavailable)

To provide an effectful predicate we use the ZIO#retryWhileZIO operator.

ZIO#retryWhileEqualsโ€‹

Like the previous operator, it tries while its error is equal to the specified error:

remoteService.retryWhileEquals(TemporarilyUnavailable)

ZIO Chat

๐ŸŒŸ Greetings from ZIO Chat! ๐ŸŒŸ Have questions about ZIO? Look no further! I'm here to be your ZIO guru. Whether you're a seasoned pro or just starting out, I've got answers tailored just for you. Fire away with your ZIO queries, and I'll hit you back with the best insights I've got. Let's dive into the world of ZIO together!