Skip to content

Commit

Permalink
A Guide to Retry Library (#470)
Browse files Browse the repository at this point in the history
* [SCALA-316] A Guide to the Retry Library

* [SCALA-316] Use AtomicInteger instead of mocks in tests

* [SCALA-316] Report known issue of retry library and fix tests

* Update PrimeNumberRetry.scala

* [SCALA-316] Move retry code to scala-libraries-3

* [SCALA-316] Fix dependencies

Co-authored-by: Stefanos Georgakis <[email protected]>
Co-authored-by: Grzegorz Piwowarek <[email protected]>
  • Loading branch information
3 people authored Aug 2, 2022
1 parent 0bd674e commit a132a74
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 1 deletion.
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@ lazy val scala_libraries_3 = (project in file("scala-libraries-3"))
"com.github.pureconfig" %% "pureconfig" % "0.17.1",
"com.github.pureconfig" %% "pureconfig-enumeratum" % "0.17.1"
),
libraryDependencies += "org.scalamock" %% "scalamock" % "5.1.0" % Test
libraryDependencies += "org.scalamock" %% "scalamock" % "5.1.0" % Test,
libraryDependencies += "com.softwaremill.retry" %% "retry" % "0.3.5"
)

lazy val scala_strings = (project in file("scala-strings"))
Expand Down
1 change: 1 addition & 0 deletions scala-libraries-3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
- [Introduction to http4s](https://www.baeldung.com/scala/http4s-intro)
- [Introduction to ScalaMock](https://www.baeldung.com/scala/scalamock)
- [Print the Content of an Apache Spark RDD](https://www.baeldung.com/scala/spark-rdd-content)
- [A Guide to the Retry Library](TBD)
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.baeldung.scala.retry

import retry.{Policy, Success}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Random

object PrimeNumberRetry {

implicit val success: Success[Int] = {
val successA = retry.Success[Int](_ == 0)
val successB = retry.Success[Int](isPrime)
successA.or(successB)
}

def generateRandomNumber: Future[Int] =
outerPolicy.apply(Future {
val number = Random.nextInt()
if (number < 0) {
throw new UnsupportedOperationException(
s"Got a negative number: $number"
)
} else if (number > 100) {
throw new IllegalArgumentException(
s"Expected number within the [0-100] range. Got $number"
)
}
number
})

def isPrime(number: Int): Boolean = {
var prime = true
for (i <- 2 until number if number % i == 0) {
prime = false
}
prime
}

val policy: Policy = retry
.When {
case _: UnsupportedOperationException =>
retry.Backoff.apply(5, 5.milliseconds)
case _: IllegalArgumentException =>
retry.Directly.apply(5)
case _ =>
retry.JitterBackoff.apply(5, 5.milliseconds)
}

val outerPolicy: Policy = retry.FailFast(policy) {
case _: NumberFormatException => true
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.baeldung.scala.retry

import com.baeldung.scala.retry.PrimeNumberRetry.success
import org.scalatest.matchers.must.Matchers
import org.scalatest.wordspec.AsyncWordSpec

import java.util.concurrent.atomic.AtomicInteger
import scala.concurrent.Future

class PrimeNumberRetryTest extends AsyncWordSpec with Matchers {
"PrimeNumberRetry" should {

"FailFast when an NumberFormatException is thrown" in {
val counter = new AtomicInteger(0)
val result = PrimeNumberRetry.outerPolicy.apply(Future {
counter.incrementAndGet()
throw new NumberFormatException
})
result
.map(_ => assert(false))
.recover {
case _: NumberFormatException =>
assert(counter.get() == 1)
}
}

/*
* This test is affected by <a href="https://github.com/softwaremill/retry/issues/31">this issue</a>
*/
"retry in any exception" in {
val counter = new AtomicInteger(0)
val result = PrimeNumberRetry.outerPolicy.apply(Future {
counter.incrementAndGet()
throw new IllegalArgumentException
})
result
.map(_ => assert(false))
.recover {
case _: IllegalArgumentException =>
assert(counter.get() == 7)
}
}

"succeed when a prime is returned" in {
val counter = new AtomicInteger(0)
val result = PrimeNumberRetry.outerPolicy.apply(Future {
counter.incrementAndGet()
7
})
result
.map(_ => {
assert(counter.get() == 1)
})
}

/*
* This test is affected by <a href="https://github.com/softwaremill/retry/issues/31">this issue</a>
*/
"fail when a non prime number is returned" in {
val counter = new AtomicInteger(0)
val result = PrimeNumberRetry.outerPolicy.apply(Future {
counter.incrementAndGet()
10
})
result
.flatMap(number => {
assert(counter.get() == 7)
assert(number == 10)
})
}
}
}

0 comments on commit a132a74

Please sign in to comment.