Getting started

Let’s start with a realistic example.

In order to provide business value to our stakeholders, we need to download a textual description of a cat gif.

Unfortunately we have to do this over a flaky network connection, so there’s a high probability it will fail.

We’ll be working with the cats-effect IO monad, but any effect monad with an instance of Temporal will do.

import cats.effect.IO

val httpClient = util.FlakyHttpClient()

val flakyRequest: IO[String] = httpClient.getCatGif

To improve the chance of successfully downloading the file, let’s wrap this with some retry logic.

We’ll add a dependency on the core module:

val catsRetryVersion = "4.0.0"
libraryDependencies += "com.github.cb372" %% "cats-retry" % catsRetryVersion,

(Note: if you’re using Scala.js, you’ll need a %%% instead of %%.)

First we’ll need a retry policy. We’ll keep it simple: retry up to 5 times, with no delay between attempts. (See the retry policies page for information on more powerful policies).

import retry._

val retryFiveTimes = RetryPolicies.limitRetries[IO](5)

We can also provide an error handler to do some logging each time the operation raises an error.

Note how this also happens within whatever monad you’re working in, in this case the IO monad.

import scala.concurrent.duration.FiniteDuration
import retry.RetryDetails.NextStep._

val logMessages = collection.mutable.ArrayBuffer.empty[String]

def logError(err: Throwable, details: RetryDetails): IO[Unit] =
  details.nextStepIfUnsuccessful match
    case DelayAndRetry(nextDelay: FiniteDuration) =>
      IO(logMessages.append(s"Failed to download. So far we have retried ${details.retriesSoFar} times."))
    case GiveUp =>
      IO(logMessages.append(s"Giving up after ${details.retriesSoFar} retries"))

Now we have a retry policy and an error handler, we can wrap our IO in retries.

import retry.ResultHandler.retryOnAllErrors

val flakyRequestWithRetry: IO[String] =
  retryingOnErrors(flakyRequest)(
    policy = retryFiveTimes,
    errorHandler = retryOnAllErrors(logError)
  )

Let’s see it in action.

import cats.effect.unsafe.implicits.global

flakyRequestWithRetry.unsafeRunSync()
// res1: String = "cute cat gets sleepy and falls asleep"

logMessages.foreach(println)
// Failed to download. So far we have retried 0 times.
// Failed to download. So far we have retried 1 times.
// Failed to download. So far we have retried 2 times.
// Failed to download. So far we have retried 3 times.

Next steps: