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:
- Learn about the other available combinators
- Learn about the MTL combinators
- Learn more about retry policies