Testing delayed messages with the help of akka-testkit - scala

There are two actors - ProducerActor and ConsumerActor. Producer has a scheduler which sends "Tick" message to itself each 2000 ms. After that the producer sends "Hello" message to consumer:
class ProducerActor(consumer: ActorRef) extends Actor {
override def receive: Receive = {
case "Tick" =>
...
// some code which takes < 1 ms
...
consumer ! "Hello"
}
override def preStart: Unit =
context.system
.scheduler
.schedule(2000 milliseconds, 2000 milliseconds, self, "Tick")
}
Is it possible to test the case when the consumer actor receives a "Hello" message each 2000 ms? So, if message is received in period < 2000 ms or > 2000 ms then test will assert error.
For example:
"Consumer test" should {
"receive message each 2000 ms" in {
...
val consumer = TestProbe()
val producer = system.actorOf(Props(new ProducerActor(consumer.ref))
...
consumer.howToExpectDelayedMessage(minDelay = 2000 millis, "Hello")
}
}
--- UPD ---
I have figured out the following solution which works fine for me:
"Consumer test" should {
"receive message each 2000 ms" in {
val consumer = TestProbe()
val producer = system.actorOf(Props(new ProducerActor(consumer.ref))
// check the first ten periods
(0 to 10) foreach { _ =>
consumer.expectNoMsg(2000 millis)
consumer.expectMsg("Hello")
}
}
}
But if there is a better solution for this task it would be great if you could share.

You can use Timing Assessions to do that.
certain events must not happen immediately (like a timer), others need to happen before a deadline. Therefore, all examination methods accept an upper time limit within the positive or negative result must be obtained.
import akka.actor.Props
import scala.concurrent.duration._
val worker = system.actorOf(Props[Worker]())
within(200 millis) {
worker ! "some work"
expectMsg("some result")
expectNoMessage() // will block for the rest of the 200ms
Thread.sleep(300) // will NOT make this block fail
}
The block given to within must complete after a duration which is between min and max, where the former defaults to zero.

Related

Akka scheduler not completing

I need to send a message to an actor at specific intervals. I am using the following code:
object SendToActor extends App {
import Sender._
val system: ActorSystem = ActorSystem("sender")
try {
val senderActor: ActorRef = system.actorOf(Sender.props, "sendActor")
val sendSchedule =
system.scheduler.schedule(0 milliseconds, 5 minutes, senderActor, doSomething())
} finally {
system.terminate()
}
}
Unfortunately, the scheduler doesn't seem to run unless I do one of the following:
Put a readLine() right after it:
val sendSchedule = system.scheduler.schedule(0 milliseconds, 5 minutes, senderActor, doSomething())
readLine()
Put a Thread.sleep() right after it:
val sendSchedule = system.scheduler.schedule(0 milliseconds, 5 minutes, senderActor, doSomething())
Thread.sleep(10000)
Is there a reason why the scheduler won't run as coded above? Why does it require the sleep in order to work?
Probably because you're terminating the actor system immediately after defining the scheduler.

Akka Thread Tuning

I have 100 threads, need to process only 12 threads at a time not more than that. After completion of these threads other 12 have to be processed and so on but it's processing only first 12 set threads then it terminates after that.
Here is my Logic :
class AkkaProcessing extends Actor {
def receive = {
case message: List[Any] =>
var meterName = message(0) // It Contains only 12 threads , it process them and terminates. Am unable to get remaining threads
val sqlContext = message(1).asInstanceOf[SQLContext]
val FlagDF = message(2).asInstanceOf[DataFrame]
{
All the business logic here
}
context.system.shutdown()
}
}
}
object Processing {
def main(args: Array[String]) = {
val rawBuff = new ArrayBuffer[Any]()
val actorSystem = ActorSystem("ActorSystem") // Creating ActorSystem
val actor = actorSystem.actorOf(Props[AkkaProcessing].withRouter(RoundRobinPool(200)), "my-Actor")
implicit val executionContext = actorSystem.dispatchers.lookup("akka.actor.my-dispatcher")
for (i <- 0 until meter_list.length) {
var meterName = meter_list(i) // All 100 Meters here
rawBuff.append(meterName, sqlContext, FlagDF)
actor ! rawBuff.toList
}
}
}
Any Inputs highly appreciated
I think you might be best to create 2 actor types : consumer (which run in parallel) and coordinator (which takes the 12 thread tasks and passes them to the consumers). The coordinator would wait for the consumers to finish and then run the next batch.
See this answer for a code example: Can Scala actors process multiple messages simultaneously?
Failing that, you could just use Futures in a similar manner.

Akka Actor Test: Automatic reply with a TestProbe

I am trying to get a test probe to reply with an acknowledgement, whenever it receive any message .
I wrote the following code in my test but it does not work:
val chgtWriter = new TestProbe(system) {
def receive: Receive = {
case m => println("receive messagereplying with ACK"); sender() ! ACK
}
}
Is there a way to do that. The actor that is actually sending the message to the test probe is definitely running on another thread than the TestThread. Below you can see the full test as currently crafted.
feature("The changeSetActor periodically fetch new change set following a schedule") {
scenario("A ChangeSetActor fetch new changeset from a Fetcher Actor that return a full and an empty ChangeSet"){
Given("a ChangeSetActor with a schedule of fetching a message every 10 seconds, a ChangeFetcher and a ChangeWriter")
val chgtFetcher = TestProbe()
val chgtWriter = new TestProbe(system) {
def receive: Receive = {
case m => println("receive message {} replying with ACK"); sender() ! ACK
}
}
val fromTime = Instant.now().truncatedTo(ChronoUnit.SECONDS)
val chgtActor = system.actorOf(ChangeSetActor.props(chgtWriter.ref, chgtFetcher.ref, fromTime))
When("all are started")
Then("The Change Fetcher should receive at least 3 messages from the ChangeSetActor within 40 seconds")
var changesetSNum = 1
val received = chgtFetcher.receiveWhile( 40 seconds) {
case FetchNewChangeSet(m) => {
println(s"received: FetchNewChangeSet(${m}")
if (changesetSNum == 1) {
chgtFetcher.reply(NewChangeSet(changeSet1))
changesetSNum += 1
}
else
chgtFetcher.reply(NoAvailableChangeSet)
}
}
received.size should be (3)
}
}
The changeSetActor is fully tested and works. The test hang with the ChangeWriter. It never receive a message in the receive method.
EDIT1(Following #Jakko anser)
The Auto Pilots is as follow:
val probe = TestProbe()
probe.setAutoPilot(new TestActor.AutoPilot {
def run(sender: ActorRef, msg: Any): TestActor.AutoPilot =
msg match {
case "stop" ⇒ TestActor.NoAutoPilot
case x ⇒ **testActor.tell(x, sender)**; TestActor.KeepRunning
}
})
Although all the explanation given so far is way clear, what is confusing in the official example is the reference "testActor". Who is testActor here ? there is no variable declaration of that name at that point.
You can script your test probes using Auto Pilots. For example:
import akka.testkit._
val probe = TestProbe()
probe.setAutoPilot(new TestActor.AutoPilot {
def run(sender: ActorRef, msg: Any): TestActor.AutoPilot = {
println("receive messagereplying with ACK")
sender ! ACK
TestActor.KeepRunning
}
})
In the above example, we set up a test probe with an automatic message handler, Auto Pilot. The auto pilot will be automatically triggered when the probe receives a message. In this example, the auto pilot will print a message and respond back to the sender.
After a message has been handled, the auto pilot can decide how the next incoming message will be handled. It can either set up a different auto pilot, reuse the existing auto pilot (TestActor.KeepRunning), or disable the auto pilot completely (TestActor.NoAutoPilot). In this example, the same auto pilot will be used for handling all the incoming messages.
You can still use test probe assertions as usual even with the auto pilot attached to the probe.
The testActor in the official documentation refers to the actor you are writing tests against. For example, in your case the actor could be the ChangeSetActor assigned to value chgtActor. Since all you really want to do is to respond back to the sender from probe, it is enough for the test probe auto pilot to respond back to the sender and not care about the testActor.

Akka priority mailbox and stash/unstash

I have this situation:
ActorA sends ActorB start/stop messages every 30-40 seconds
ActorA sends ActorB strings to print (always)
ActorB must print the strings he receives, but only if ActorA sent just a start message
My code :
case object Start
case object Stop
case object TriggerStateChange
case object SendMessage
class ActorB extends Actor with Stash {
override def receive: Receive = {
case Start =>
context.become(printingBehavior, false)
unstashAll()
case x => stash()
}
def printingBehavior: Receive = {
case msg: String => println(msg)
case Stop => context.unbecome()
}
}
class ActorA(val actorB: ActorRef) extends Actor {
var counter = 0
var started = false
override def preStart: Unit = {
import context.dispatcher
this.context.system.scheduler.schedule(0 seconds, 5 seconds, self, TriggerStateChange)
this.context.system.scheduler.schedule(0 seconds, 1 seconds, self, SendMessage)
}
override def receive: Actor.Receive = {
case SendMessage =>
actorB ! "Message: " + counter
counter += 1
case TriggerStateChange =>
actorB ! (if (started) {
started = false
Stop
} else {
started = true
Start
})
}
}
object Akka {
def main(args: Array[String]) = {
val system = ActorSystem.create("TestActorSystem")
val actorB = system.actorOf(Props(classOf[ActorB]), "ActorB")
val actorA = system.actorOf(Props(classOf[ActorA], actorB), "ActorA")
system.awaitTermination()
}
}
I used this code suggested by another user, but I have a problem.
ActorB should stop as soon as it receives a stop message (so if it receives M1-M2-M3 and a stop message while printing M1, it must not print M2 and M3 yet). I thought to use a priority mailbox, but stash() and unstash() don't work with priority mailboxes yet. Is there a way to do this just with a priority mailbox?
And if I have the sequence M1-M2-M3-Stop-M4-Start-M5 (and stop received while printing M1), I should get a first print "M1" and a second one "M2-M3-M4-M5" (so the old M2 and M3 must be printed before the new M4). Stash() and unstash() don't work with priority mailbox, so my idea was to create start/stop messages with max priority, M1, M2, etc... messages with min priority, and after I use become(), I though that ActorB could send again the saved messages, but this time with medium priority (so they'll be read before new messages, but after stop messages). Is it possible? Is there a better solution?
If it is possible to introduce new ActorC it could be solved like that:
ActorA sends messages to print and Start/Stop messages to ActorC
ActorB sends a message to ActorC when it is ready to print
If ActorC got Start message from ActorA earlier then it sends next message to print to ActorB. Else it just waits for Start message and sends nothing to ActorB.
This way if M1-M2-M3-Stop sequence comes while ActorB is still printing M1, no messages will be printed until next Start message.

Akka + WithinTimeRange

I've testing the fault tolerant system of akka and so far it's been good when talking about retrying to send a msg according the maxNrOfRetries specified.
However, it does not restart the actor within the given time range, it restarts all at once, ignoring the within time range.
I tried with AllForOneStrategy and OneForOneStrategy but does not change anything.
Trying to follow this blog post: http://letitcrash.com/post/23532935686/watch-the-routees, this is the code I've been working.
class Supervisor extends Actor with ActorLogging {
var replyTo: ActorRef = _
val child = context.actorOf(
Props(new Child)
.withRouter(
RoundRobinPool(
nrOfInstances = 5,
supervisorStrategy =
AllForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 10.second) {
case _: NullPointerException => Restart
case _: Exception => Escalate
})), name = "child-router")
child ! GetRoutees
def receive = {
case RouterRoutees(routees) =>
routees foreach context.watch
case "start" =>
replyTo = sender()
child ! "error"
case Terminated(actor) =>
replyTo ! -1
context.stop(self)
}
}
class Child extends Actor with ActorLogging {
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
log.info("***** RESTARTING *****")
message foreach{ self forward }
}
def receive = LoggingReceive {
case "error" =>
log.info("***** GOT ERROR *****")
throw new NullPointerException
}
}
object Boot extends App {
val system = ActorSystem()
val supervisor = system.actorOf(Props[Supervisor], "supervisor")
supervisor ! "start"
}
Am I doing anything wrong to accomplish that?
EDIT
Actually, I misunderstood the purpose of the withinTimeRange.
To schedule my retries in a time range, I'm doing the following:
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
log.info("***** RESTARTING *****")
message foreach { msg =>
context.system.scheduler.scheduleOnce(30.seconds, self, msg)
}
}
It seems to work ok.
I think you have misunderstood the purpose of the withinTimeRange arg. That value is supposed to be used in conjunction with maxNrOfRetries to provide a window in which to support the limiting of the number of retries. For example, as you have specified, the implication is that the supervisor will no longer restart an individual child if that child needs to be restarted more than 3 times in 10 seconds.
From docs:
maxNrOfRetries - the number of times a child actor is allowed to be
restarted, negative value means no limit, if the limit is exceeded the
child actor is stopped
withinTimeRange - duration of the time window
for maxNrOfRetries, Duration.Inf means no window
Your code means that when any child fails with NullPointerException more than 3 times within 10 seconds it will not be restarted again. Because of AllForOneStrategy after first Routee fails all routees are restarted. And because you've overridden preRestart to resend failed message this situation repeats again until reaches 3 failures within 10 seconds(which is achieved in less than a second).