I've been trying the reaper pattern suggested by the Akka team. It does sound good in theory, but the provided unit test does not pass in either sbt or Eclipse's JUnit runner.
import scala.concurrent.duration._
import akka.util.Timeout
import akka.actor._
import akka.testkit.{TestKit, ImplicitSender, TestProbe}
import org.scalatest.{WordSpec, BeforeAndAfterAll}
import org.scalatest.matchers.MustMatchers
import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith
#RunWith(classOf[JUnitRunner])
class ReaperSpec extends TestKit(ActorSystem("ReaperSpec"))
with ImplicitSender with WordSpec with BeforeAndAfterAll with MustMatchers
{
import Reaper._
override def afterAll() {
system.shutdown()
}
// Our test reaper. Sends the snooper a message when all the souls have been reaped
private class TestReaper(snooper: ActorRef) extends Reaper {
def allSoulsReaped(): Unit = snooper ! "Dead"
}
"Reaper" should {
"work" in {
// Set up some dummy Actors
val a = TestProbe()
val b = TestProbe()
val c = TestProbe()
val d = TestProbe()
// Build our reaper
val reaper = system.actorOf(Props(new TestReaper(testActor)))
// Watch a couple
reaper ! WatchMe(a.ref)
reaper ! WatchMe(d.ref)
// Stop them
system.stop(a.ref)
system.stop(d.ref)
// Make sure we've been called
within (1 seconds, 5 seconds) {
expectMsg("Dead")
}
}
}
}
After adding logging to the actors, I noticed that the Terminated message ended up in the dead letter box. So the reaper actor never got the Terminated message.
[ERROR] [08/05/2014 11:10:27.244] [ReaperSpec-akka.actor.default-dispatcher-4]
[akka://ReaperSpec/user/$a] Monitored actor [Actor[akka://ReaperSpec/system/testActor2#-1060476766]] terminated (akka.actor.DeathPactException)
[INFO] [08/05/2014 11:10:27.248] [ReaperSpec-akka.actor.default-dispatcher-2]
[akka://ReaperSpec/user/$a] Message [akka.actor.Terminated] from Actor[akka://ReaperSpec/system/testActor5#2029818496] to Actor[akka://ReaperSpec/user/$a#1787786349] was not delivered.
[1] dead letters encountered, no more dead letters will be logged. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
I thought that system.stop(watchedActorRef) would guarantee delivery of a Terminated message to all the actors that are watching the watchedActorRef. What am I missing?
Related
I'm working on an application that has a couple of long-running streams going, where it subscribes to data about a certain entity and processes that data. These streams should be up 24/7, so we needed to handle failures (network issues etc).
For that purpose, we've wrapped our sources in RestartingSource.
I'm now trying to verify this behaviour, and while it looks like it functions, I'm struggling to create a test where I push in some data, verify that it processes correctly, then send an error, and verify that it reconnects after that and continues processing.
I've boiled that down to this minimal case:
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{RestartSource, Sink, Source}
import akka.stream.testkit.TestPublisher
import org.scalatest.concurrent.Eventually
import org.scalatest.{FlatSpec, Matchers}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
class MinimalSpec extends FlatSpec with Matchers with Eventually {
"restarting a failed source" should "be testable" in {
implicit val sys: ActorSystem = ActorSystem("akka-grpc-measurements-for-test")
implicit val mat: ActorMaterializer = ActorMaterializer()
implicit val ec: ExecutionContext = sys.dispatcher
val probe = TestPublisher.probe[Int]()
val restartingSource = RestartSource
.onFailuresWithBackoff(1 second, 1 minute, 0d) { () => Source.fromPublisher(probe) }
var last: Int = 0
val sink = Sink.foreach { l: Int => last = l }
restartingSource.runWith(sink)
probe.sendNext(1)
eventually {
last shouldBe 1
}
probe.sendNext(2)
eventually {
last shouldBe 2
}
probe.sendError(new RuntimeException("boom"))
probe.expectSubscription()
probe.sendNext(3)
eventually {
last shouldBe 3
}
}
}
This test consistently fails on the last eventually block with Last failure message: 2 was not equal to 3. What am I missing here?
Edit: akka version is 2.5.31
I figured it out after having had a look at the TestPublisher code. Its subscription is a lazy val. So when RestartSource detects the error, and executes the factory method () => Source.fromPublisher(probe) again, it gets a new Source, but the subscription of the probe is still pointing to the old Source. Changing the code to initialize both a new Source and TestPublisher works.
I have refactored a bunch of email sending code in a play application to do that asynchronously using an actor.
When I need to send an email, I now have an injection of an EmailActor and I call emailActor ? EmailRequest(from, to, ...) to send it.
My question is, how can I unit test that the actor is actually called ?
I read the documentation of Akka regarding tests, but it seems to me it focuses on testing the actor themselves, not their invocation, and it is not clear at all where I should start.
You can use a TestProbe and inject it into your email service. Check out this simple test case
import akka.actor.{ActorRef, ActorSystem}
import akka.pattern.ask
import akka.testkit.{TestKit, TestProbe}
import akka.util.Timeout
import org.scalatest.flatspec.AsyncFlatSpecLike
import scala.concurrent.Future
import scala.concurrent.duration._
class TestProbeActorExample
extends TestKit(ActorSystem("test"))
with AsyncFlatSpecLike {
class MyService(actorRef: ActorRef) {
def sendEmail(email: String): Future[Int] = {
implicit val timeout: Timeout = 1.second
(actorRef ? email).mapTo[Int]
}
}
it should "test an actor" in {
val testProbe = TestProbe()
val service = new MyService(testProbe.ref)
val statusCode = service.sendEmail("email")
testProbe.expectMsg("email")
testProbe.reply(10)
statusCode.map(r => assert(r == 10))
}
}
Please note if you use ask pattern you need to assert that a message has been received with expectMsg and then you have to send a reply back with reply
I have an actor whose sole responsibility is to forward messages it receives from external interfaces (command-line, user, etc.) to appropriate topics. I want to test that it correctly publishes these messages.
I will need to create some dummy subscriber who will expect messages published to a certain topic and make assertions about the messages it receives.
Here's my code that I attempted to realize that:
Messages.scala
case class Foo(foo: String)
InterfaceForwardingActor.scala
import akka.actor.{Actor, ActorLogging}
import akka.cluster.pubsub.{DistributedPubSub, DistributedPubSubMediator}
import com.typesafe.config.Config
/** Actor responsible for forwarding stimuli external to the system.
* For instance, messages from the command-line interface or from a UI.
*
*/
class InterfaceForwardingActor extends Actor with ActorLogging {
import DistributedPubSubMediator.Publish
protected val mediator = DistributedPubSub(context.system).mediator
log.info(s"Hello from interface forwarder.")
final val topic = "info"
def receive = {
case foo: Foo => {
log.info("Forwarding a Foo message")
mediator ! Publish(topic, foo)
}
}
}
and the test code
InterfaceForwardingActorTest.scala
import akka.actor.{ActorSystem, Props}
import akka.cluster.client.ClusterClient.Publish
import akka.cluster.pubsub.DistributedPubSub
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
import com.typesafe.config.ConfigFactory
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
class InterfaceForwardingActorTest extends
TestKit(ActorSystem("InterfaceForwardingActorSpec")) with ImplicitSender with
WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
"An InterfaceForwardingActor" must {
val interfaceForwardingActor = system.actorOf(Props[InterfaceForwardingActor])
val probe = TestProbe()
val mediator = DistributedPubSub(system).mediator
// subscribe the test probe to the "info" topic
mediator ! Publish("info", probe.ref)
"publish a Foo message" in {
val msg = Foo("test")
interfaceForwardingActor ! msg
probe.expectMsg(msg)
}
}
}
What I find is that the probe, which is subscribed to the info topic, doesn't receive the message within the default timeout period of 3 seconds and the assertion fails. The interesting part, however, is that I do see the log message stating that the interface forwarding actor is indeed forwarding a Foo message.
What am I doing wrong in my test?
The TestProbe should be subscribed to the topic in the the test code:
mediator ! Subscribe("info", probe.ref)
instead of
mediator ! Publish("info", probe.ref)
Documentation page of distributed pub-sub is here, for reference.
Requirement?
- There has to be a long running process(daemon) that should run forever
- In case of any exceptions, it should be restarted, but if it fails again twice, no restart efforts should be taken
Problem I face?
- The actor is restarted but no message sent again
What I have?
Main Class
package com.learner.ahka.runforever
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
object RaceEvent extends App {
val config = ConfigFactory.parseString( """
akka.loglevel = "DEBUG"
akka.actor.debug {
receive = on
lifecycle = on
}
""")
val system = ActorSystem.create("race", config)
val coach = system.actorOf(Coach.props(), "coach")
coach ! GetSetGo
}
Supervisor
package com.learner.ahka.runforever
import akka.actor.SupervisorStrategy.{Escalate, Restart}
import akka.actor._
import akka.event.LoggingReceive
import scala.concurrent.duration._
case object GetSetGo
object Coach {
def props(): Props = Props[Coach];
}
class Coach() extends Actor with ActorLogging {
val runner = context.actorOf(Runner.props(new Marathon), "runner")
override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy(maxNrOfRetries = 2, withinTimeRange = 5 seconds) {
case _: RuntimeException => Restart
}
override def receive = LoggingReceive {
case GetSetGo => runner ! GoForIt
}
}
Actor
package com.learner.ahka.runforever
import akka.actor.Status.Failure
import akka.actor.{Actor, ActorLogging, Props}
import akka.event.LoggingReceive
import akka.pattern.pipe
object Runner {
def props(race: Race) = Props(classOf[Runner], race)
}
class Runner(race: Race) extends Actor with ActorLogging {
import context.dispatcher
override def receive: Receive = LoggingReceive {
case GoForIt => race.start pipeTo self
case Failure(throwable) => throw throwable
}
}
Actual work
package com.learner.ahka.runforever
import scala.concurrent.Future
case object GoForIt
trait Race {
def start: Future[Any]
}
class Marathon extends Race {
import scala.concurrent.ExecutionContext.Implicits.global
override def start: Future[Any] = future
val future = Future {
for (i <- 1 to 3) {
println("I am a Marathon Runner!")
Thread.sleep(1000)
}
throw new RuntimeException("MarathonRunner is tired")
}
}
Logs
[DEBUG] [05/30/2015 16:03:35.696] [main] [EventStream(akka://race)] logger log1-Logging$DefaultLogger started
[DEBUG] [05/30/2015 16:03:35.698] [main] [EventStream(akka://race)] Default Loggers started
[DEBUG] [05/30/2015 16:03:35.704] [race-akka.actor.default-dispatcher-4] [akka://race/system] now supervising Actor[akka://race/system/deadLetterListener#-1391310385]
[DEBUG] [05/30/2015 16:03:35.706] [race-akka.actor.default-dispatcher-3] [akka://race/system/deadLetterListener] started (akka.event.DeadLetterListener#191ba186)
[DEBUG] [05/30/2015 16:03:35.710] [race-akka.actor.default-dispatcher-2] [akka://race/user] now supervising Actor[akka://race/user/coach#-1161587711]
I am a Marathon Runner!
[DEBUG] [05/30/2015 16:03:35.722] [race-akka.actor.default-dispatcher-3] [akka://race/user/coach] started (com.learner.ahka.runforever.Coach#66f0f319)
[DEBUG] [05/30/2015 16:03:35.722] [race-akka.actor.default-dispatcher-4] [akka://race/user/coach/runner] started (com.learner.ahka.runforever.Runner#72f67980)
[DEBUG] [05/30/2015 16:03:35.723] [race-akka.actor.default-dispatcher-3] [akka://race/user/coach] now supervising Actor[akka://race/user/coach/runner#755574648]
[DEBUG] [05/30/2015 16:03:35.723] [race-akka.actor.default-dispatcher-3] [akka://race/user/coach] received handled message GetSetGo
[DEBUG] [05/30/2015 16:03:35.725] [race-akka.actor.default-dispatcher-4] [akka://race/user/coach/runner] received handled message GoForIt
I am a Marathon Runner!
I am a Marathon Runner!
[DEBUG] [05/30/2015 16:03:38.739] [race-akka.actor.default-dispatcher-3] [akka://race/user/coach/runner] received handled message Failure(java.lang.RuntimeException: MarathonRunner is tired)
[ERROR] [05/30/2015 16:03:38.752] [race-akka.actor.default-dispatcher-4] [akka://race/user/coach/runner] MarathonRunner is tired
java.lang.RuntimeException: MarathonRunner is tired
at com.learner.ahka.runforever.Marathon$$anonfun$1.apply(Race.scala:22)
at com.learner.ahka.runforever.Marathon$$anonfun$1.apply(Race.scala:17)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
[DEBUG] [05/30/2015 16:03:38.753] [race-akka.actor.default-dispatcher-2] [akka://race/user/coach/runner] restarting
[DEBUG] [05/30/2015 16:03:38.755] [race-akka.actor.default-dispatcher-2] [akka://race/user/coach/runner] restarted
UPDATE
If I don't delegate to Future, everything works as expected, even in case of restart, but when delegated to Future, Future does not get executed in case of actor restart. See here
You could override the postRestart method to send a message back to the parent to notify it of the restart, then watch for that new message type in the parent and respond accordingly. If context.parent won't work happily for that purpose (I tend not to rely on it), then have the Coach actor pass it's self actor reference as a new constructor parameter when instantiating the Runner.
It is not well known, but you can access sender() from the supervisorStrategy. This means that you can easily identify the actor that you are going to restart (sender() will point to the ActorRef that is currently being decided on). This is completely safe.
I created the following Akka Actor code in Scala. Code works fine when a single workerActor is created. But code silently fails when I try to create a pool of worker actors using round robin logic. Any idea how to fix this? How do I get more debug info to be printed?
import scala.collection.immutable.Map
import scala.collection.mutable.ArrayBuffer
import akka.actor.actorRef2Scala
import akka.actor.ActorSystem
import akka.actor.Props
import scala.concurrent.Await
import scala.concurrent.duration._
import akka.pattern.ask
import akka.util.Timeout
import akka.actor._
import org.junit._
import org.junit.Assert._
import messaging.actors._
import akka.routing.RoundRobinRouter
import akka.routing._
class MainEngineActorTest {
#Test
def testMainActor () = {
val _system = ActorSystem("MainEngineActor")
val master = _system.actorOf(Props[MainEngineActor], name = "EngineActor")
println ("Created Main Engine Actor")
implicit val timeout = Timeout(5 seconds)
val userID = new UserID ("test1")
println ("Sending messages")
for (i <- ( 1 to 10)) {
master ! "Hello"
master ! "World"
}
}
}
class MainEngineActor extends Actor with ActorLogging{
// works if we create only a single workerActor
//val workerActors = context.actorOf(Props[WorkerActor], name = "WorkerActors")
// Doesn't work when we create a pool of worker actors - how do we fix this?
// why doesn't this work and why aren't any error messages printed?
val workerActors = context.actorOf(RoundRobinPool(5).props(Props[WorkerActor]), name = "WorkerActors")
def receive: Receive = {
case request => {
workerActors forward request
}
}
}
class WorkerActor extends Actor {
def receive: Receive = {
case request => {
println ("RequestReceived =" + request)
}
}
}
Try creating your pool like this instead:
val workerActors = context.actorOf(Props[WorkerActor].withRouter(RoundRobinPool(5)), name = "WorkerActors")
In addition, when running this as a Junit test, the program is terminating before the child actors have a chance to receive the message. I verified this by adding a Thread.sleep(5000) after the loop that is sending the Hello and World messages to master. I then tweaked your code a little bit to use Akka's TestActorRef from akka-testkit which will force everything to use the CallingThreadDispatcher to get synchronous execution throughout the test and everything works as expected. The two lines I changed are:
implicit val _system = ActorSystem("MainEngineActor")
val master = TestActorRef(new MainEngineActor())