/** * Copyright (C) 2015-2016 Typesafe Inc. */ package akka.pattern import scala.concurrent.Promise import scala.concurrent.duration._ import java.util.concurrent.ThreadLocalRandom import akka.actor.Actor import akka.actor.ActorLogging import akka.actor.Props import akka.actor.Status.Failure import akka.testkit.AkkaSpec import akka.testkit.ImplicitSender import java.util.concurrent.TimeoutException object CircuitBreakerStressSpec { case object JobDone case object GetResult case class Result(doneCount: Int, timeoutCount: Int, failCount: Int, circCount: Int) class StressActor(breaker: CircuitBreaker) extends Actor with ActorLogging with PipeToSupport { import context.dispatcher private var doneCount = 0 private var timeoutCount = 0 private var failCount = 0 private var circCount = 0 private def job = { val promise = Promise[JobDone.type]() context.system.scheduler.scheduleOnce(ThreadLocalRandom.current.nextInt(300).millisecond) { promise.success(JobDone) } promise.future } override def receive = { case JobDone ⇒ doneCount += 1 breaker.withCircuitBreaker(job).pipeTo(self) case Failure(ex: CircuitBreakerOpenException) ⇒ circCount += 1 breaker.withCircuitBreaker(job).pipeTo(self) case Failure(_: TimeoutException) ⇒ timeoutCount += 1 breaker.withCircuitBreaker(job).pipeTo(self) case Failure(_) ⇒ failCount += 1 breaker.withCircuitBreaker(job).pipeTo(self) case GetResult ⇒ sender() ! Result(doneCount, timeoutCount, failCount, circCount) } } } // reproducer for issue #17415 class CircuitBreakerStressSpec extends AkkaSpec with ImplicitSender { import CircuitBreakerStressSpec._ muteDeadLetters(classOf[AnyRef])(system) "A CircuitBreaker" in { val breaker = CircuitBreaker(system.scheduler, 5, 200.millisecond, 200.seconds) val stressActors = Vector.fill(3) { system.actorOf(Props(classOf[StressActor], breaker)) } for (_ ← 0 to 1000; a ← stressActors) { a ! JobDone } // let them work for a while Thread.sleep(3000) stressActors.foreach { a ⇒ a ! GetResult val result = expectMsgType[Result] result.failCount should be(0) } } }