neat way to test become unbecome swtichover in scala - scala

I am trying to test state switch over of receive method.
Found this stackoverflow post, but that also not clearly give a solution.
Please find below simplified code snippet :-
package become_unbecome_basics
import akka.actor.{Actor, ActorSystem}
import akka.testkit.{ImplicitSender, TestActorRef, TestKit}
import become_unbecome_basics.BasicBecomeUnbecomeActor.{SWITCH_TO_MASTER, SWITCH_TO_STANDBY}
import com.typesafe.scalalogging.LazyLogging
import org.scalatest.FlatSpecLike
import org.scalatest.Matchers._
class BecomUnbecomeSwitchoverTest extends TestKit(ActorSystem("testSystem")) with ImplicitSender with FlatSpecLike{
"initially receive" should "points to master" in {
val aRef = TestActorRef[BasicBecomeUnbecomeActor]
val actor = aRef.underlyingActor
//not sure, weather we have something like this to assert
//actor.receive should be(actor.master)
}
}
object BasicBecomeUnbecomeActor{
case object SWITCH_TO_MASTER
case object SWITCH_TO_STANDBY
}
class BasicBecomeUnbecomeActor extends Actor with LazyLogging{
override def receive: Receive = master
def master: Receive = {
case SWITCH_TO_STANDBY =>
context.become(standBy)
case msg => logger.debug(s"master : $msg received")
}
def standBy: Receive = {
case SWITCH_TO_MASTER =>
context.unbecome()
case msg => logger.debug(s"standBy : $msg received")
}
}

The StackOverflow post you mentioned contains two suggested ways to test your actor.
Emit the state changes to some other actor.
Don't test state change, but the actor's behaviour.
In the first example, you would have some way of sending information out of your actor on every state change. In Akka, sending state change information as actor messages is natural way to implement this.
import akka.actor._
import akka.testkit._
class ExampleActor(notify: ActorRef) extends Actor with ActorLogging {
import ExampleActor.{Master, StandBy}
def receive: Receive = master
def master: Receive = {
case StandBy =>
notify ! StandBy
context.become(standby)
case msg =>
log.debug("received msg in master: {}", msg)
}
def standby: Receive = {
case Master =>
notify ! Master
context.become(master)
case msg =>
log.debug("received msg in standby: {}", msg)
}
}
object ExampleActor {
def props(notify: ActorRef): Props = Props(new ExampleActor(notify))
case object Master
case object StandBy
}
class ExampleActorTest extends TestKit(ActorSystem("testSystem")) with FlatSpecLike {
"ExampleActor" should "move to stand by state" in {
val probe = TestProbe()
val actor = system.actorOf(ExampleActor.props(probe.ref))
actor ! ExampleActor.StandBy
probe.expectMsg(ExampleActor.StandBy)
}
}
(I haven't run the code yet so apologies for any errors in the code)
In the code above, the ExampleActor is a stateful actor which notifies the given actor reference of any state changes. Note that this doesn't allow inspecting the current state, but instead a log of state transitions. Also, it is possible to introduce a bug in the state notification code because the notification code is manually added to the actor instead of the actor doing it automatically.
I changed the testing style to asynchronous testing style to get more realistic tests.
The state change notifications allows you to get information about what specific state the actor transitions to, but it doesn't tell you if it works the way it should. Instead of testing what state changes the actor goes through, how about testing what the actor itself does.
class Accumulator extends Actor with ActorLogging {
import Accumulator._
def receive: Receive = accumulatorReceive(0)
def accumulatorReceive(x: Int): Receive = {
case Add(i) => next(x + i)
case Remove(i) => next(x - i)
case Multiply(i) => next(x * i)
case Divide(i) => next(x / i)
case Get => sender() ! x
}
def next(x: Int) = context.become(accumulatorReceive(x))
}
object Accumulator {
def props: Props = Props(new Accumulator)
case class Add(i: Int)
case class Remove(i: Int)
case class Multiply(i: Int)
case class Divide(i: Int)
case object Get
}
class AccumulatorTest extends TestKit(ActorSystem("testSystem")) with FlatSpecLike {
import Accumulator._
"Accumulator" should "accumulate" in {
val probe = TestProbe()
val actor = system.actorOf(Accumulator.props)
actor ! Add(3)
actor ! Remove(1)
actor ! Multiply(4)
actor ! Divide(2)
probe.send(actor, Get)
probe.expectMsg(5)
}
}
In this example, Accumulator does state changes, but it doesn't notify when its state has changed. Instead, it has a specific get command for inspecting interesting parts about its state. In the test, we send multiple messages that cause state changes in the accumulator actor. Finally, we inspect the result of these messages by querying the accumulator.

Related

Actor supervised by BackoffSupervisor loses stashed messages after restart

I have an actor with stash usage. Sometimes, when it crashes, it loses all stashed messages. I found that it depends on what supervision logic I use.
I wrote a simple example.
An actor with the stash:
case object WrongMessage
case object TestMessage
case object InitialMessage
class TestActor extends Actor with Stash {
override def receive: Receive = uninitializedReceive
def uninitializedReceive: Receive = {
case TestMessage =>
println(s"stash test message")
stash()
case WrongMessage =>
println(s"wrong message")
throw new Throwable("wrong message")
case InitialMessage =>
println(s"initial message")
context.become(initializedReceive)
unstashAll()
}
def initializedReceive: Receive = {
case TestMessage =>
println(s"test message")
}
}
In the following code, TestActor never receives stashed TestMessage:
object Test1 extends App {
implicit val system: ActorSystem = ActorSystem()
val actorRef = system.actorOf(BackoffSupervisor.props(Backoff.onFailure(
Props[TestActor], "TestActor", 1 seconds, 1 seconds, 0
).withSupervisorStrategy(OneForOneStrategy()({
case _ => SupervisorStrategy.Restart
}))))
actorRef ! TestMessage
Thread.sleep(5000L)
actorRef ! WrongMessage
Thread.sleep(5000L)
actorRef ! InitialMessage
}
But this code works well:
class SupervisionActor extends Actor {
val testActorRef: ActorRef = context.actorOf(Props[TestActor])
override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy()({
case _ => SupervisorStrategy.Restart
})
override def receive: Receive = {
case message => testActorRef forward message
}
}
object Test2 extends App {
implicit val system: ActorSystem = ActorSystem()
val actorRef = system.actorOf(Props(classOf[SupervisionActor]))
actorRef ! TestMessage
Thread.sleep(5000L)
actorRef ! WrongMessage
Thread.sleep(5000L)
actorRef ! InitialMessage
}
I looked into sources and found that actor supervision uses
LocalActorRef.restart method which backed by system dispatcher logic, but BackoffSupervisor simply creates a new actor after termination of the old one. Is there any way to work around it?
I'm not sure one can make restart under BackoffSupervisor properly send stashed messages without some custom re-implementation effort.
As you've already pointed out that BackoffSupervisor does its own restart that bypasses the standard actor lifecycle. In fact, it's explicitly noted in the BackoffOnRestartSupervisor source code:
Whatever the final Directive is, we will translate all Restarts to our
own Restarts, which involves stopping the child.
In case you haven't read about this reported issue, it has a relevant discussion re: problem with Backoff.onFailure.
Backoff.onStop would also give the wanted BackoffSupervisor feature, but unfortunately it has its own use cases and won't be triggered by an exception.

Is there a way to know the context of an Akka actor?

I have an actor with some contexts, i.e.
def step2: Receive = {
case _ => log error "Unhandled message"
}
def step1: Receive = {
case _ => log error "Unhandled message"
}
Is there a way to know on which state the actor currently is (step1, step2)?
(I could stock the value on a string but I wonder if there is a better way.)
If it not possible, I would like to understand why since this state should be kept somewhere.
FSM
You can use FSM. stateName gives the name of the state.
Ping the Actor with some special message handled in every state to send the stateName.
sealed trait ExampleState
case object State1 extends ExampleState
case object State2 extends ExampleState
case object C extends ExampleState
import akka.actor.{Actor, FSM}
import akka.event.EventHandler
import akka.util.duration._
case object Move
class ABC extends Actor with FSM[ExampleState, Unit] {
import FSM._
startWith(State1, Unit)
when(State1) {
case Ev(Move) =>
EventHandler.info(this, "Go to B and move on after 5 seconds")
goto(state2) forMax (5 seconds)
}
when(State2) {
case Ev(StateTimeout) =>
EventHandler.info(this, "Moving to C")
goto(C)
}
when(C) {
case Ev(Move) =>
EventHandler.info(this, "Stopping")
stop
}
initialize // this checks validity of the initial state and sets up timeout if needed
}
Hacky Solution
Akka Actor does not store any specific information about the PartialFunction. So I don't think there will be a akka lib function readily available for this.
Have state inside the actor and then update the state when Actor tries to become something.
class FooBar extends Actor with ActorLogging {
var state: Option[String] = Some("receive")
override def receive: Receive = {
case _ => context become state1()
}
def state1: () => Receive = {
() => {
state = Some("state1")
{
case _ => log error "ignore"
}
}
}
def state2: () => Receive = {
() => {
state = Some("state2")
{
case _ => log error "ignore"
}
}
}
}

Test response from child on akka

I'm new to using scala on the akka system.
I have a parent actor (named "manager") which sends a message to a child, and receives an answer, which it forwards to next child.
The final actor saves the result of the whole process.
I want to create an end to end test which sends a message to the parent ("manager"), and expect a message from the last actor which recieves the result.
I'm looking for a simple way to direct the final actor to send a message back to the test. Since the test is not the sender, and it's not a simple actor, i don't know how to direct the message correctly.
Following is code of the test:
class EndToEndTest extends TestKit(ActorSystem("MyActorSystem"))
with FunSuiteLike with Matchers with BeforeAndAfterAll with ImplicitSender {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
test("should send result") {
val actor = system.actorOf(Props(new Manager(name = "ActorManager")))
actor ! tPoint(1000L)
actor ! tPoint(2000L)
expectMsg(ActorResult(1))
}
}
and of the last child actor:
class SensorHealthReportMySqlActor extends Actor with ActorLogging {
def receive = {
case Result(result: Long) =>
//this is where i would like to send the test a message with Result(result)
case _ =>
log.error("Unknown message type")
}
}
Any help would be appreciated.
I think the solution you want is to pass an ActorRef as a parameter in a message so the receiver knows where to send the reply when the message should be sent to some actor other than the sender of the current message.
So something along these lines:
import akka.actor.{Actor, Props, ActorRef, ActorLogging}
case class tPoint(n: Long)
case class Result(result: Long, replyTo: ActorRef)
case class ActorResult(result: Long)
class Manager(name: String) extends Actor {
val child = context.actorOf(Props(new SensorHealthReportMySqlActor))
override def receive: Receive = {
case msg: tPoint ⇒
child ! Result(msg.n, sender())
}
}
class SensorHealthReportMySqlActor extends Actor with ActorLogging {
def receive = {
case Result(result: Long, replyTo: ActorRef) =>
//this is where i would like to send the test a message with Result(result)
replyTo ! ActorResult(1)
case _ =>
log.error("Unknown message type")
}
}
In this case the sender() in the Manager receive method is the test itself, which is an ImplicitSender (as you declared in your test). This is an actor created behind the scenes which can receive messages.
In a non-test system, you can use the ask pattern or an Inbox for this same purpose. See the documentation at Actors.

Error with DeadLetters and ActorRef

I encounter an error while running my project I cannot solve.
Here is my code:
import akka.actor._
import akka.actor.Actor
import akka.actor.ActorSystem
import akka.actor.Props
import akka.actor.ScalaActorRef
import akka.pattern.gracefulStop
import akka.util._
import java.util.Calendar
import java.util.concurrent._
import java.text.SimpleDateFormat
import scala.Array._
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
sealed trait Message
case class ReturnInfluenceMessage(source: ActorRef) extends Message
case class SetInfluences(source: ActorRef) extends Message
case class GetInfluence() extends Message
class Listener extends Actor {
def receive = {
case ReturnInfluenceMessage(s0urce) => println ("Listener: received influence (" + s0urce + ")")
}
}
class Entity extends Actor {
val Influences = context.actorOf(Props[Influences], name = "Influences")
def receive = {
case SetInfluences(s0urce) => context.children foreach (_.forward(SetInfluences(s0urce)))
case GetInfluence => context.children foreach (_.forward(GetInfluence))
case ReturnInfluenceMessage(source) =>
source ! ReturnInfluenceMessage(source)
}
}
class Influences extends Actor {
private var source: ActorRef = _
def receive = {
case SetInfluences(s0urce) =>
source = s0urce
println ("Influences: received " + s0urce)
println ("Influences: Influence set to " + source)
case GetInfluence =>
println ("Influences: influence sent to " + source)
sender ! ReturnInfluenceMessage(source)
}
}
object main extends App {
val system = akka.actor.ActorSystem("mySystem")
val Abel = system.actorOf(Props[Listener], name = "Listener")
val Cain = system.actorOf(Props[Entity], name = "Entity")
system.scheduler.scheduleOnce(1500 milliseconds, Cain, SetInfluences(Abel))
system.scheduler.scheduleOnce(3000 milliseconds, Cain, GetInfluence)
}
Here the error:
[INFO] [08/29/2014 15:39:08.330] [mySystem-akka.actor.default-dispatcher-2] [akka://mySystem
/deadLetters] Message [ReturnInfluenceMessage] from Actor[akka://mySystem/user/Entity
/Shadow/Influences#1407138271] to Actor[akka://mySystem/deadLetters] was not
delivered. [1] dead letters encountered. This logging can be turned off or adjusted
with configuration settings
'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
I am trying to set the variable source of the Cain actor to have this last one send the ActorRef of Abel to which a message figuring the source variable and display it.
The error happens here:
source ! ReturnInfluenceMessage(source)
, and I do not know why it occurs.
#Martynas is correct in that your sender ref will be the DeadLetter ref when your code is setup the way it is. The main issue is that you send in a message from outside of the actor system (via the scheduler) and then when you use forward, you continue to propagate the fact that you don't have a sender down into the next actor. You can remedy this by using a tell (!) instead of forward. I have modified your code sample to show this (as well as some other changes described after the code sample):
import akka.actor._
import concurrent.duration._
sealed trait Message
case class ReturnInfluenceMessage(source: ActorRef) extends Message
case class SetInfluences(source: ActorRef) extends Message
case object GetInfluence extends Message
class Listener extends Actor {
def receive = {
case ReturnInfluenceMessage(s0urce) => println(s"Listener: received influence ($s0urce)")
}
}
class Entity extends Actor {
val influences = context.actorOf(Props[Influences], name = "Influences")
def receive = {
case si # SetInfluences(s0urce) =>
influences ! si
case GetInfluence =>
influences ! GetInfluence
case rim # ReturnInfluenceMessage(source) =>
source ! rim
}
}
class Influences extends Actor {
def receive = setInfluence
def setInfluence:Receive = {
case SetInfluences(s0urce) =>
println (s"Influences: received $s0urce")
println (s"Influences: Influence set to $s0urce")
context.become(withSource(s0urce) orElse setInfluence)
}
def withSource(source:ActorRef):Receive = {
case GetInfluence =>
println (s"Influences: influence sent to $source")
sender ! ReturnInfluenceMessage(source)
}
}
object Main extends App {
val system = akka.actor.ActorSystem("mySystem")
val abel = system.actorOf(Props[Listener], name = "Listener")
val cain = system.actorOf(Props[Entity], name = "Entity")
import system.dispatcher
system.scheduler.scheduleOnce(1500 milliseconds, cain, SetInfluences(abel))
system.scheduler.scheduleOnce(3000 milliseconds, cain, GetInfluence)
}
When I ran this I did not get any deadletters. Other changes include:
Formatting (2 spaces for indents, correct casing for var/vals)
Changed GetInfluence into a case object as it did not have any fields
Used variable binding in the case statements to capture a ref to the message (via the # symbol) when we needed to send that message along
Used a two state setup for the Influences actor where it is first waiting for the state to be set (the source ref) and then switches to a state where it is able to respond properly to a GetInfluences message. Probably want to explicitly handle a GetInfluences message when in the initial state as for now it's just an unhandled message.
Got rid of the use of children.foreach as you already had a ref to the only child of that actor, so it seemed unnecessary to use this construct. I would use that construct if you had a variable amount of children to send to and in this example you don't.
When you schedule a message with
system.scheduler.scheduleOnce(...)
a sender of that message is DeadLetters which is what you are trying to send message to in
sender ! ReturnInfluenceMessage(source)
and it is also what error message says.

Akka Actor - wait for some time to expect a message, otherwise send a message out

Is it possible to make an Actor wait for X amount of seconds to receive any message, and if a message is received, process it as usual, otherwise send a message to some other Actor (pre-determined in the constructor)?
It's possible, have a look at Akka Actor "ask" and "Await" with TimeoutException. But keep in mind that blocking inside an actor is a very bad idea since during that time actor can't handle any other messages. Moreover it blocks one Akka processing thread.
A better approach is to send a message (fire and forget) and schedule some timeout event using Akka scheduler. When the response arrives, cancel that event or set some flag so that it won't trigger if the reply actually came on time.
Yes, if you want to wait for any message, you simply set a receiveTimeout: http://doc.akka.io/docs/akka/current/scala/actors.html#receive-timeout
(The docs is slightly misleading here, you can set the receiveTimeout after every message also)
Might be an overkill, but you might check out the Finite State Machine (FSM) trait.
import akka._
import actor._
import util._
import duration._
import Impatient._
object Impatient {
sealed trait State
case object WaitingForMessage extends State
case object MessageReceived extends State
case object TimeoutExpired extends State
sealed trait Data
case object Unitialized extends Data
// In
case object Message
}
class Impatient(receiver: ActorRef) extends Actor with FSM[State, Data] {
startWith(WaitingForMessage, Unitialized)
when(WaitingForMessage, stateTimeout = 3 seconds) {
case Event(StateTimeout, data) => goto(TimeoutExpired) using data // data is usually modified here
case Event(Message, data) => goto(MessageReceived) using data // data is usually modified here
}
onTransition {
case WaitingForMessage -> MessageReceived => stateData match {
case data => log.info("Received message: " + data)
}
case WaitingForMessage -> TimeoutExpired => receiver ! TimeoutExpired
}
when(MessageReceived) {
case _ => stay
}
when(TimeoutExpired) {
case _ => stay
}
initialize
}
Here it is in action:
object Main extends App {
import akka._
import actor._
import Impatient._
val system = ActorSystem("System")
val receiver = system.actorOf(Props(new Actor with ActorLogging {
def receive = {
case TimeoutExpired => log.warning("Timeout expired")
}
}))
val impatient = system.actorOf(Props(new Impatient(receiver)), name = "Impatient")
impatient ! Message
val impatient2 = system.actorOf(Props(new Impatient(receiver)), name = "Impatient2")
Thread.sleep(4000)
impatient2 ! Message
system.shutdown()
}