Akka: Test scheduled actors using Testkit - scala

I have a parent actor which creates a child actor and the child actor greets parent every minute as below
class MasterActor extends Actor with ActorLogging {
override def receive: Receive = {
case "Greet" =>
print("Hey child!!")
case "CreateChild" =>
context.actorOf(Props[ChildActor])
}
}
class ChildActor extends Actor with ActorLogging {
import context.dispatcher
override def preStart(): Unit = {
super.preStart()
context.system.scheduler.schedule(Duration("1 minutes").asInstanceOf[FiniteDuration],
Duration("1 minutes").asInstanceOf[FiniteDuration], context.parent, "Greet")
}
override def receive: Receive = {
case _ =>
print("child receives something")
}
}
I am new to actor system, How can I test schedule scenario using TestKit?
I tried something like below in my test but that is not working
"Master actor" should {
"receive a Greet message every minute" in {
val probe = TestProbe
val actor = system.actorOf(Props(new Child() {
import context.dispatcher
override def preStart() =
context.system.scheduler.scheduleOnce(Duration("1 seconds").asInstanceOf[FiniteDuration], probe.ref, "Greet")
}))
probe.expectMsg("Greet")
}
}

You can read about it at the timing-assertions section in the testing of akka docs. There is the within function that should help you.
For example you can try:
"Master actor" should {
"receive a Greet message every minute" in {
within(62 seconds) {
val probe = TestProbe
val actor = system.actorOf(Props[Child])
probe.expectMsg("Greet")
}
}
}
What I would do, not to wait so long, is putting the delay timeout in the configuration, and change it to a few millis in the test scenario.

Related

How do you shut down an Actor if it has not received a message within a period of time?

Current code is following:
case object LatestMessageSignal
class MessageCheckpoint(implicit ec: ExecutionContext) extends Actor with ActorLogging with Timers {
override def receive: Receive = {
case LatestMessageSignal => awaitLatestMessageSignal()
}
private def awaitLatestMessageSignal(): Unit = {
import scala.concurrent.duration._
context.system.scheduler.scheduleOnce(30.seconds) {
context.stop(self)
}
}
}
When the actor receives a LatestMessageSignal message, it will call awaitLatestMessageSignal() method that will wait for 30 seconds, and then stop the actor.
It seems like you want to stop the actor after 30 seconds of inactivity? If so you can use ActorContext#setReceiveTimeout(Duration)
For instance:
case object LatestMessageSignal
class MessageCheckpoint(implicit ec: ExecutionContext) extends Actor with ActorLogging with Timers {
context.setReceiveTimeout(30.seconds)
override def receive: Receive = {
case ReceivedTimeout => context.stop(self)
}
}
As far as I understand, you'd like to keep MessageCheckpoint alive and stop it if there are no new messages coming to it for 30 seconds.
This actor will stay alive till you send messages to it and will stop after 30 seconds of inactivity.
case object LatestMessageSignal
class MessageCheckpoint extends Actor with ActorLogging {
override def postStop(): Unit = {
super.postStop()
log.info("Stopping")
}
override def receive: Receive = receiveWithTimer(None)
private def receiveWithTimer(timer: Option[Cancellable]): Receive = {
case LatestMessageSignal =>
timer.foreach(_.cancel())
context.become(receiveWithTimer(Option(initiateTimer())))
}
private def initiateTimer(): Cancellable = {
import context.dispatcher
log.info("Initiating new poison pill timer")
context.system.scheduler.scheduleOnce(30.seconds, self, PoisonPill)
}
}
I would like the actor can discard current processing message when another new message comes, and will process the latest message instead
This is not possible. I think you are assuming that method awaitLatestMessageSignal is blocking the actor. This method is non blocking, it will create a timer and return immediately. The message will be processed quite fast and actor be ready for the next message. Actor is processing messages one at a time and there is no way to cancel message processing.

Data push using akka scheduler

I am looking for some example on akka scheduler usage. I have one actor (lets call it - dataProducer) implementation for retrieving data from some database. I would like to write one scheduler actor which will pole the dataProducer actor in 5 seconds interval. Also how to handle the case if data retrieval takes more time than scheduler interval. Will scheduleOnce method in Scheduler actor handle this?
Here is my scheduler actor
import java.util.concurrent.{Executors, TimeUnit}
import akka.actor.{Actor, Cancellable, Props}
import scala.concurrent.ExecutionContext
class SchedulerActor(interval: Long) extends Actor with LogF{
implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(100))
private var scheduler: Cancellable = _
override def preStart(): Unit = {
import scala.concurrent.duration._
scheduler = context.system.scheduler.schedule(
initialDelay = 0 seconds,
interval = FiniteDuration(interval, TimeUnit.SECONDS),
receiver = self,
message = FetchData
)
}
override def postStop(): Unit = {
scheduler.cancel()
}
def receive = {
case FetchData =>
logger.debug( "Fetch Data" )
sender() ! "Data Fetched!!!" //here I'll call dataProducer API
true
case unknown =>
throw new RuntimeException( "ERROR: Received unknown message [" + unknown + "], can't handle it" )
}
}
object SchedulerActor {
def props(interval: Long): Props = Props(new SchedulerActor(interval))
}
sealed trait FetchDataMessage
case object FetchData extends FetchDataMessage
Scheduler's scheduleOnce helps to execute a piece of after some delay.
Have different states and switch between states to accept different kind of messages and act accordingly. But when timeout happens scheduleOnce will take you to timeoutState.
ScheduleOnce will help the actor know that timeout has happened.
how to handle the case if data retrieval takes more time than scheduler interval ?
If data fetch takes more than specified time actor state changes timeoutState and in the timeout state say what should be done to the actor. Either you can retry or try different source.
I would like to write one scheduler actor which will pole the dataProducer actor in 5 seconds interval
In the result state wait for scheduleOnce with 5 seconds delay to request the dataProducer and whole thing repeats again.
Check this code to understand how it can be done.
import akka.actor.{Actor, Cancellable}
import stackoverflow.DBUtils.Entity
import scala.concurrent.Future
import scala.concurrent.duration._
import akka.pattern.pipe
object DBPollActor {
case class Result(results: List[Entity])
case object Schedule
case object Timeup
case object FetchData
}
object DBUtils {
case class Entity(name: String)
def doDBOperation: Future[List[Entity]] = {
Future.successful(List(Entity(name = "foo")))
}
}
class DBPollActor(timeout: Int) extends Actor {
import DBPollActor._
implicit val ex = context.system.dispatcher
var schedulerOpt: Option[Cancellable] = None
#scala.throws[Exception](classOf[Exception])
override def preStart(): Unit = {
super.preStart()
self ! FetchData
schedulerOpt = Some(context.system.scheduler.scheduleOnce(timeout seconds) {
context become timeoutState
self ! Timeup
})
}
override def receive: Receive = {
case msg#FetchData =>
context become startState
self forward msg
}
def startState: Receive = {
case FetchData =>
schedulerOpt.map(_.cancel())
context become resultState
DBUtils.doDBOperation.map(Result) pipeTo self
schedulerOpt = Some(context.system.scheduler.scheduleOnce(timeout seconds) {
context become timeoutState
self ! Timeup
})
}
def timeoutState: Receive = {
case Timeup =>
schedulerOpt.map(_.cancel())
//Timeout happened do something or repeat
}
def resultState: Receive = {
case result#Result(list) =>
schedulerOpt.map(_.cancel())
//Result available consume the result and repeat or doSomething different
context become resultState
DBUtils.doDBOperation.map(Result) pipeTo self
schedulerOpt = Some(context.system.scheduler.scheduleOnce(timeout seconds) {
context become timeoutState
self ! Timeup
})
case ex: Exception =>
schedulerOpt.map(_.cancel())
//future failed exit or retry
}
}

Akka - test supervision strategy

I have the following scenario: the parent supervisor actor creates a child for each message using a factory (function) passed in the constructor.
class supervisorActor(childActorMaker: ActorRefFactory => ActorRef)
extends Actor with ActorLogging{
def receive: Receive = {
case "testThis" =>
val childActor = childActorMaker(context)
childActor!"messageForChild"
}
override val supervisorStrategy =
OneForOneStrategy() {
case _ => Stop
}
}
class childActor extends Actor {
def receive:Receive = {
case _ => /** whatever **/
}
}
In the test I override the child Receive to force an exception. My expectation is the child actor to be stopped every time because of the Supervision Strategy i set.
"When the child Actor throws an exception the Supervisor Actor " should " " +
" stop it" in {
val childActorRef = TestActorRef(new childActor() {
override def receive = {
case msg: String => throw new IllegalArgumentException("kaboom")
}
})
watch(childActorRef)
val maker = (_: ActorRefFactory) => childActorRef
val supervisorActorRef = system.actorOf(Props(new supervisorActor(maker)))
supervisorActorRef!"testThis"
expectTerminated(childActorRef, 1.second)
}
I would expect the child actor to be stoped because of the supervisorStrategy Stop.
Instead I get this error:
java.lang.AssertionError: assertion failed: timeout (1 second) during expectMsg: Terminated
Any idea of why is this happening? Thank you
It seems that the childActorRef is not created with supervisorActorRef's context (I mean ActorContext of supervisorActor you created in the test code).
So childActor(childActorRef) is not a child of supervisorActor(supervisorActorRef) in your test code. That's why supervisor strategy of supervisorActor doesn't serve the purpose.

How to wait actors stop in scala

class A extends Actor{
def act = {
//do something
}
}
val a = new A
a.join // How Can I implement it
I have read How can I join started Actor?
however I still don't know how to write the code.
The standard way to achieve this would be to use the Ask Pattern
It goes something like this:
class MyActor extends Actor {
def receive = {
case "Ping" => sender ! "Pong"
}
}
val future = actor ? "Ping"
val result = Await.result(future, 10 seconds) //blocks until the response has been received, or the timeout reached
This is assuming that you want to block on a message from the actor. If you want to tell when an actor has died, you need to use DeathWatch like this:
case object TellMeWhenActorDies
case object ActorDied
class Watcher extends Actor {
val child = context.actorOf(Props[Watched], "watched")
context.watch(child)
override def receive: Receive = {
case TellMeWhenActorDies => context.become(waitingForDeath(sender))
}
def waitingForDeath(client: ActorRef): Receive = {
case Terminated(name) => client ! ActorDied
}
}
class Watched extends Actor {
override def receive: Receive = {
case _ => //do nothing
}
}
val finishedFuture = supervisor ? TellMeWhenActorDies
system.actorSelection("/user/$a/watched").tell(PoisonPill, supervisor)
Await.result(finishedFuture, 10 seconds)
Simply use the gracefulStop pattern. This is example is directly from the Akka docs:
try {
val stopped: Future[Boolean] = gracefulStop(actorRef, 5 seconds, Manager.Shutdown)
Await.result(stopped, 6 seconds)
// the actor has been stopped
} catch {
// the actor wasn't stopped within 5 seconds
case e: akka.pattern.AskTimeoutException =>
}

How to periodically execute an AKKA Actor's routine?

I need an actor to stop one of its children, so that I can possibly create a new actor with same name (UUID ?).
I've got an ActorSystem with one Actor child. And this child creates new actors with context.actorOf and context.watch. When I try to stop one of these using context.stop, I observe that its postStop method is called as expected, but no matter how long I wait (seconds... minutes...), it never sends back the Terminated message to its creator (and watching) actor.
I read this in the AKKA documentation:
Since stopping an actor is asynchronous, you cannot immediately reuse the name of the child you just stopped; this will result in an InvalidActorNameException. Instead, watch the terminating actor and create its replacement in response to the Terminated message which will eventually arrive.
I don't care waiting for normal termination, but I really need actors to eventually terminate when asked to. Am I missing something ? Should I create actors directly from the system instead of from an actor ?
EDIT:
Here is my code :
object MyApp extends App {
def start() = {
val system = ActorSystem("MySystem")
val supervisor = system.actorOf(Supervisor.props(), name = "Supervisor")
}
override def main(args: Array[String]) {
start()
}
}
object Supervisor {
def props(): Props = Props(new Supervisor())
}
case class Supervisor() extends Actor {
private var actor: ActorRef = null
start()
def newActor(name: String): ActorRef = {
try {
actor = context.actorOf(MyActor.props(name), name)
context.watch(actor)
} catch {
case iane: InvalidActorNameException =>
println(name + " not terminated yet.")
null
}
}
def terminateActor() {
if (actor != null) context.stop(actor)
actor = null
}
def start() {
while (true) {
// do something
terminateActor()
newActor("new name possibly same name as a previously terminated one")
Thread.sleep(5000)
}
}
override def receive = {
case Terminated(x) => println("Received termination confirmation: " + x)
case _ => println("Unexpected message.")
}
override def postStop = {
println("Supervisor called postStop().")
}
}
object MyActor {
def props(name: String): Props = Props(new MyActor(name))
}
case class MyActor(name: String) extends Actor {
run()
def run() = {
// do something
}
override def receive = {
case _ => ()
}
override def postStop {
println(name + " called postStop().")
}
}
EDIT²: As mentionned by #DanGetz, one shall not need to call Thread.sleep in an AKKA actor. Here what I needed was a periodical routine. This can be done using the AKKA context scheduler. See: http://doc.akka.io/docs/akka/2.3.3/scala/howto.html#scheduling-periodic-messages . Instead I was blocking the actor in an infinite loop, preventing it to use its asynchronous mecanisms (messages). I changed the title since the problem was actually not involving actor termination.
It's hard to gauge exactly what you want now that the question has changed a bit, but I'm going to take a stab anyway. Below you will find a modified version of your code that shows both periodic scheduling of a task (one that kicks off the child termination process) and also watching a child and only creating a new one with the same name when we are sure the previous one has stopped. If you run the code below, every 5 seconds you should see it kill the child and wait for the termination message before stating a new one with the exact same name. I hope this is what you were looking for:
object Supervisor {
val ChildName = "foo"
def props(): Props = Props(new Supervisor())
case class TerminateChild(name:String)
}
case class Supervisor() extends Actor {
import Supervisor._
import scala.concurrent.duration._
import context._
//Start child upon creation of this actor
newActor(ChildName)
override def preStart = {
//Schedule regular job to run every 5 seconds
context.system.scheduler.schedule(5 seconds, 5 seconds, self, TerminateChild(ChildName))
}
def newActor(name: String): ActorRef = {
val child = context.actorOf(MyActor.props(name), name)
watch(child)
println(s"created child for name $name")
child
}
def terminateActor(name:String) = context.child(ChildName).foreach{ ref =>
println(s"terminating child for name $name")
context stop ref
}
override def receive = {
case TerminateChild(name) =>
terminateActor(name)
case Terminated(x) =>
println("Received termination confirmation: " + x)
newActor(ChildName)
case _ => println("Unexpected message.")
}
override def postStop = {
println("Supervisor called postStop().")
}
}