diff --git a/akka-actor-tests/src/test/scala/akka/pattern/CircuitBreakerSpec.scala b/akka-actor-tests/src/test/scala/akka/pattern/CircuitBreakerSpec.scala index b3f26c05a5..ad6806904d 100644 --- a/akka-actor-tests/src/test/scala/akka/pattern/CircuitBreakerSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/pattern/CircuitBreakerSpec.scala @@ -33,8 +33,9 @@ object CircuitBreakerSpec { def longCallTimeoutCb()(implicit system: ActorSystem, ec: ExecutionContext): Breaker = new Breaker(new CircuitBreaker(system.scheduler, 1, 5 seconds, 500.millis.dilated)) + val longResetTimeout = 5.seconds def longResetTimeoutCb()(implicit system: ActorSystem, ec: ExecutionContext): Breaker = - new Breaker(new CircuitBreaker(system.scheduler, 1, 100.millis.dilated, 5 seconds)) + new Breaker(new CircuitBreaker(system.scheduler, 1, 100.millis.dilated, longResetTimeout)) def multiFailureCb()(implicit system: ActorSystem, ec: ExecutionContext): Breaker = new Breaker(new CircuitBreaker(system.scheduler, 5, 200.millis.dilated, 500.millis.dilated)) @@ -62,7 +63,9 @@ class CircuitBreakerSpec extends AkkaSpec with BeforeAndAfter { checkLatch(breaker.openLatch) - intercept[CircuitBreakerOpenException] { breaker().withSyncCircuitBreaker(sayHi) } + val e = intercept[CircuitBreakerOpenException] { breaker().withSyncCircuitBreaker(sayHi) } + e.remainingDuration should be > (Duration.Zero) + e.remainingDuration should be <= (CircuitBreakerSpec.longResetTimeout) } "transition to half-open on reset timeout" in { diff --git a/akka-actor/src/main/scala/akka/pattern/CircuitBreaker.scala b/akka-actor/src/main/scala/akka/pattern/CircuitBreaker.scala index c15f03f0b2..7614f08cb0 100644 --- a/akka-actor/src/main/scala/akka/pattern/CircuitBreaker.scala +++ b/akka-actor/src/main/scala/akka/pattern/CircuitBreaker.scala @@ -441,16 +441,17 @@ class CircuitBreaker(scheduler: Scheduler, maxFailures: Int, callTimeout: Finite * @return Future containing result of protected call */ override def invoke[T](body: ⇒ Future[T]): Future[T] = - Promise.failed[T](new CircuitBreakerOpenException(remainingTimeout().timeLeft)).future + Promise.failed[T](new CircuitBreakerOpenException(remainingDuration())).future /** - * Calculate remaining timeout to inform the caller in case a backoff algorithm is useful + * Calculate remaining duration until reset to inform the caller in case a backoff algorithm is useful * - * @return [[scala.concurrent.duration.Deadline]] to when the breaker will attempt a reset by transitioning to half-open + * @return duration to when the breaker will attempt a reset by transitioning to half-open */ - private def remainingTimeout(): Deadline = get match { - case 0L ⇒ Deadline.now - case t ⇒ (t.millis + resetTimeout).fromNow + private def remainingDuration(): FiniteDuration = { + val diff = System.nanoTime() - get + if (diff <= 0L) Duration.Zero + else diff.nanos } /** @@ -474,7 +475,7 @@ class CircuitBreaker(scheduler: Scheduler, maxFailures: Int, callTimeout: Finite * @return */ override def _enter(): Unit = { - set(System.currentTimeMillis) + set(System.nanoTime()) scheduler.scheduleOnce(resetTimeout) { attemptReset() }