2018-10-29 17:19:37 +08:00
|
|
|
/*
|
2020-01-02 07:24:59 -05:00
|
|
|
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
2018-03-26 14:56:20 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package akka.pattern
|
|
|
|
|
|
|
|
|
|
import akka.actor.Scheduler
|
|
|
|
|
|
|
|
|
|
import language.postfixOps
|
|
|
|
|
import akka.testkit.AkkaSpec
|
|
|
|
|
|
|
|
|
|
import scala.concurrent.{ Await, ExecutionContextExecutor, Future }
|
|
|
|
|
import scala.concurrent.duration._
|
|
|
|
|
|
|
|
|
|
class RetrySpec extends AkkaSpec with RetrySupport {
|
|
|
|
|
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
|
|
|
|
implicit val scheduler: Scheduler = system.scheduler
|
|
|
|
|
|
|
|
|
|
"pattern.retry" must {
|
|
|
|
|
"run a successful Future immediately" in {
|
2019-03-11 10:38:24 +01:00
|
|
|
val retried = retry(() => Future.successful(5), 5, 1 second)
|
2018-03-26 14:56:20 +03:00
|
|
|
|
|
|
|
|
within(3 seconds) {
|
|
|
|
|
Await.result(retried, remaining) should ===(5)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"run a successful Future only once" in {
|
|
|
|
|
@volatile var counter = 0
|
2019-03-13 10:56:20 +01:00
|
|
|
val retried = retry(
|
|
|
|
|
() =>
|
|
|
|
|
Future.successful({
|
|
|
|
|
counter += 1
|
|
|
|
|
counter
|
|
|
|
|
}),
|
|
|
|
|
5,
|
|
|
|
|
1 second)
|
2018-03-26 14:56:20 +03:00
|
|
|
|
|
|
|
|
within(3 seconds) {
|
|
|
|
|
Await.result(retried, remaining) should ===(1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"eventually return a failure for a Future that will never succeed" in {
|
2019-03-11 10:38:24 +01:00
|
|
|
val retried = retry(() => Future.failed(new IllegalStateException("Mexico")), 5, 100 milliseconds)
|
2018-03-26 14:56:20 +03:00
|
|
|
|
|
|
|
|
within(3 second) {
|
|
|
|
|
intercept[IllegalStateException] { Await.result(retried, remaining) }.getMessage should ===("Mexico")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"return a success for a Future that succeeds eventually" in {
|
|
|
|
|
@volatile var failCount = 0
|
|
|
|
|
|
|
|
|
|
def attempt() = {
|
|
|
|
|
if (failCount < 5) {
|
|
|
|
|
failCount += 1
|
|
|
|
|
Future.failed(new IllegalStateException(failCount.toString))
|
|
|
|
|
} else Future.successful(5)
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-27 17:31:16 +07:00
|
|
|
val retried = retry(() => attempt(), 10, 100 milliseconds)
|
2018-03-26 14:56:20 +03:00
|
|
|
|
|
|
|
|
within(3 seconds) {
|
|
|
|
|
Await.result(retried, remaining) should ===(5)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"return a failure for a Future that would have succeeded but retires were exhausted" in {
|
|
|
|
|
@volatile var failCount = 0
|
|
|
|
|
|
|
|
|
|
def attempt() = {
|
|
|
|
|
if (failCount < 10) {
|
|
|
|
|
failCount += 1
|
|
|
|
|
Future.failed(new IllegalStateException(failCount.toString))
|
|
|
|
|
} else Future.successful(5)
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-27 17:31:16 +07:00
|
|
|
val retried = retry(() => attempt(), 5, 100 milliseconds)
|
2018-03-26 14:56:20 +03:00
|
|
|
|
|
|
|
|
within(3 seconds) {
|
|
|
|
|
intercept[IllegalStateException] { Await.result(retried, remaining) }.getMessage should ===("6")
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-23 22:20:12 +08:00
|
|
|
|
|
|
|
|
"return a failure for a Future that would have succeeded but retires were exhausted with delay function" in {
|
|
|
|
|
@volatile var failCount = 0
|
|
|
|
|
@volatile var attemptedCount = 0;
|
|
|
|
|
|
|
|
|
|
def attempt() = {
|
|
|
|
|
if (failCount < 10) {
|
|
|
|
|
failCount += 1
|
|
|
|
|
Future.failed(new IllegalStateException(failCount.toString))
|
|
|
|
|
} else Future.successful(5)
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-27 17:31:16 +07:00
|
|
|
val retried = retry(() => attempt(), 5, attempted => {
|
2020-03-23 22:20:12 +08:00
|
|
|
attemptedCount = attempted
|
|
|
|
|
Some(100.milliseconds * attempted)
|
|
|
|
|
})
|
|
|
|
|
within(30000000 seconds) {
|
|
|
|
|
intercept[IllegalStateException] { Await.result(retried, remaining) }.getMessage should ===("6")
|
|
|
|
|
attemptedCount shouldBe 5
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"retry can be attempted without any delay" in {
|
|
|
|
|
@volatile var failCount = 0
|
|
|
|
|
|
|
|
|
|
def attempt() = {
|
|
|
|
|
if (failCount < 1000) {
|
|
|
|
|
failCount += 1
|
|
|
|
|
Future.failed(new IllegalStateException(failCount.toString))
|
|
|
|
|
} else Future.successful(1)
|
|
|
|
|
}
|
|
|
|
|
val start = System.currentTimeMillis()
|
2020-04-27 17:31:16 +07:00
|
|
|
val retried = retry(() => attempt(), 999)
|
2020-03-23 22:20:12 +08:00
|
|
|
|
|
|
|
|
within(1 seconds) {
|
|
|
|
|
intercept[IllegalStateException] {
|
|
|
|
|
Await.result(retried, remaining)
|
|
|
|
|
}.getMessage should ===("1000")
|
|
|
|
|
val elapse = System.currentTimeMillis() - start
|
|
|
|
|
elapse <= 100 shouldBe true
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-26 14:56:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|