akka actor current receive function name - akka

i have a simple actor with 2 behavior
package com.hello
import akka.actor.{Actor, ActorLogging}
case object Ping
class Hello extends Actor with ActorLogging {
import context._
def receive: Receive = behaviorFoo
self ! Ping
def behaviorFoo: Receive = {
case _ =>
log.info(this.receive.getClass.getSimpleName)
become(behaviorBar)
self ! Ping
}
def behaviorBar: Receive = {
case _ =>
log.info(this.receive.getClass.getSimpleName)
}
}
this thing do those:
ping self
logging current receive function
change behavior to behaviorBar
ping self
logging current receive function
in both cases it logs "$anonfun$behaviorFoo$1"
why it is not "$anonfun$behaviorBar$1" in second log?
if i change code to
self ! Ping
def receive: Receive = {
case _ =>
log.info(this.receive.getClass.getSimpleName)
become(behaviorFoo)
self ! Ping
}
def behaviorFoo: Receive = {
case _ =>
log.info(this.receive.getClass.getSimpleName)
become(behaviorBar)
self ! Ping
}
def behaviorBar: Receive = {
case _ =>
log.info(this.receive.getClass.getSimpleName)
}
it logs 3 times "$anonfun$receive$1"
is exist any way to get current behavior (Receive) function name?
or i need to hardwrite like log.info("behaviorFoo") any time?
update:
for logging issues, i added
trait MyLogging extends ActorLogging {
this: Actor ⇒
private[this] val actorClassName = this.getClass.getSimpleName
private[this] var receiveName: String = {
val receiveClassName = s"${this.receive.getClass.getSimpleName}"
val left = receiveClassName.substring(0, receiveClassName.lastIndexOf("$"))
left.substring(left.lastIndexOf("$") + 1)
}
def become(behavior: Actor.Receive): Unit = {
val behaviorClassName = behavior.getClass.getSimpleName
val left = behaviorClassName.substring(0, behaviorClassName.lastIndexOf("$"))
receiveName = left.substring(left.lastIndexOf("$") + 1)
context.become(behavior)
}
def info(message: Any): Unit = log.info(s"$actorClassName : $receiveName got $message")
}
then, my actor code become
class Hello extends Actor with MyLogging {
def receive: Receive = behaviorFoo
self ! Ping
def behaviorFoo: Receive = {
case any =>
info(any)
become(behaviorBar)
self ! Ping
}
def behaviorBar: Receive = {
case any => info(any)
}
}
now logs looks like
... Hello : behaviorFoo got Ping
... Hello : behaviorBar got Ping

Maybe this is why those are called anonymous functions. The behaviorFoo, behaviorBar, ... names have no influence on those anonymous functions. To illustrate this point, consider this:
// this is a val!
val behaviorFoo: Receive = {
case _ =>
log.info(this.receive.getClass.getSimpleName)
become(behaviorBar)
self ! Ping
}
// this returns the same anonymous function
def behaviorFoo2 = behaviorFoo
With the above, you should see that the name under which you store the anonymous function has nothing to do with the anonymous function itself...
Now, if you realize what those anonymous functions are (they are partial functions, aliased with type Actor.Receive) you could do something like below:
// not an anonymous function anymore
class BehaviourFoo extends Actor.Receive {
val anonymousFun: Actor.Receive = {
case _ =>
log.info(this.receive.getClass.getSimpleName)
become(behaviorBar)
self ! Ping
}
override def isDefinedAt(x: Any) = anonymousFun.isDefinedAt(x)
override def apply(x: Any) = anonymousFun(x)
}
// again, the name behaviorFoo doesn't matter
def behaviorFoo: Receive = new BehaviourFoo
It is certainly NOT worth the hassle, but it should help you understand what is happening.

Related

call akka scheduler.scheduleOnce in actor did not work

I want to use akka system's scheduler to do some thing interval
system.scheduler.scheduleOnce(interval.milliseconds, dayActor, DaySwitch)
I works fine, dayActor will receive DaySwitch message.
in dayActor
def receive = {
case DaySwitch =>
log.info("begin day switch")
context.system.scheduler.scheduleOnce(1.day, self, DaySwitch)
after one day, the dayActor didn't receive DaySwitch message.
How to fix it. Thanks!
I suppose that you have troubles with starting of scheduler or in place which serve interaction with receive event (please provide the whole code).
case object PullCounter
case class PullResult(counter: Int)
case object PullFailed
class PullActor extends Actor {
val period = 2.seconds
var timerCancellable: Option[Cancellable] = None
def scheduleTimer() = {
timerCancellable = Some(
context.system.scheduler.scheduleOnce(
period, context.self, PullCounter
)
)
}
override def preStart() = scheduleTimer()
// so we don't call preStart and schedule a new message
// see http://doc.akka.io/docs/akka/2.2.4/scala/howto.html
override def postRestart(reason: Throwable) = {}
def receive = LoggingReceive {
case PullCounter =>
val fReq = Database.fakeRequest()
fReq.map(counter => PullResult(counter)) pipeTo self
fReq.onFailure{ case _ => self ! PullFailed }
case PullFailed =>
scheduleTimer()
case r: PullResult =>
if(r.counter >= 5) {
context.system.shutdown()
} else {
scheduleTimer()
}
}
}
I suppose, that example like above can help you (at least, it helped me with the similar situation). I took it from this

Time for create TestProbe in Akka TestKit

I have trait for override actorOf in tests:
trait ActorRefFactory {
this: Actor =>
def actorOf(props: Props) = context.actorOf(props)
}
And I have worker actor, which stop self when receive any message:
class WorkerActor extends Actor {
override def receive: Actor.Receive = {
case _ => { context.stop(self) }
}
}
Also I have master actor, who creates actors and hold them in queue:
class MasterActor extends Actor with ActorRefFactory {
var workers = Set.empty[ActorRef]
override val supervisorStrategy = SupervisorStrategy.stoppingStrategy
def createWorker() = {
val worker = context watch actorOf(Props(classOf[WorkerActor]))
workers += worker
worker
}
override def receive: Receive = {
case m: String =>
createWorker()
case Terminated(ref) =>
workers -= ref
createWorker()
}
}
And this test, which is failed:
class ActorTest(val _system: ActorSystem) extends akka.testkit.TestKit(_system)
with ImplicitSender
with Matchers
with FlatSpecLike {
def this() = this(ActorSystem("test"))
def fixture = new {
val master = TestActorRef(new MasterActor() {
override def actorOf(props: Props) = TestProbe().ref
})
}
it should "NOT FAILED" in {
val f = fixture
f.master ! "create"
f.master ! "create"
f.master.underlyingActor.workers.size shouldBe 2
val worker = f.master.underlyingActor.workers.head
system.stop(worker)
Thread.sleep(100)
f.master.underlyingActor.workers.size shouldBe 2
}
}
After Thread.sleep in the test, I give error by "1 was not equal to 2". I have not idea what happening. But, if guess I can assume that TestProbe() can't create in the time. What can I do?
This basically boils down to an asynchronicity issue that you want to try and avoid in unit tests for Akka. You are correctly using a TestActorRef to get hooked into the CallingThreadDispatcher for the master actor. But when you call system.stop(worker), the system still us using the default async dispatcher which will introduce this race condition on the stopping and then re-creating of a worker. The simplest way I can see to fix this issue consistently is to stop the worker like so:
master.underlyingActor.context.stop(worker)
Because you are using the context of master and that actor is using the CallingThreadDispatcher I believe this will remove the asnyc issue that you are seeing. It worked for me when I tried it.

message goes to dead letter instead of sender (akka router) [scala]

Actually I`m having trouble with getting my actor (router) system to work correctly.
My Setup:
I`m trying to use an akka router within an play controller. For dependency injection I use scaldi.
scaldi module:
class UserDAOModule extends Module {
binding to new ExampleRouter
binding toProvider new UserDAOWorker
}
akka router:
class UserDAORouter(implicit inj:Injector) extends Actor with AkkaInjectable {
val userDAOProps = injectActorProps[UserDAOWorker]
var router = {
val routees = Vector.fill(5) {
val r = context.actorOf(userDAOProps)
context watch r
ActorRefRoutee(r)
}
Router(RoundRobinRoutingLogic(), routees)
}
override def receive: Receive = {
case mm: MongoDBMessage =>
router.route(mm, sender)
case Terminated(a) =>
router = router.removeRoutee(a)
val r = context.actorOf(userDAOProps)
context watch r
router = router.addRoutee(r)
}
}
worker:
class UserDAOWorker(implicit inj:Injector) extends Actor with Injectable {
val db = inject[DefaultDB]
val collection:JSONCollection = db("users")
val currentSender = sender
override def receive: Receive = {
case InsertUser(user) => insertUser(user)
}
def insertUser(user:User) = {
collection.save(user).onComplete {
case Failure(e) => currentSender ! new UserDAOReturnMessage(Some(e), None)
case Success(lastError) => currentSender ! new UserDAOReturnMessage(None, lastError)
}
}
}
When I send a message (insertUser message) to the router, it is routed correctly and the worker receives the message, but when the worker sends a message back to the sender it cant be delivered, so it is send to dead letter office. I cant figure out how to fix this. Is there someone able to help me?
Thanks in advance
I guess the problem is that the currentSender is initialized with null (i.e. ActorRef.noSender) in the constructor on actor creation. 'sender' is only valid in context of receiving a message in receive(). Sending a message to ActorRef.noSender is an equivalent of sending to dead letter queue.
Something like this should work:
class UserDAOWorker(implicit inj:Injector) extends Actor with Injectable {
val db = inject[DefaultDB]
val collection:JSONCollection = db("users")
override def receive: Receive = {
case InsertUser(user) => {
insertUser(sender, user)
}
}
def insertUser(currentSender : ActorRef, user:User) = {
collection.save(user).onComplete {
case Failure(e) => currentSender ! new UserDAOReturnMessage(Some(e), None)
case Success(lastError) => currentSender ! new UserDAOReturnMessage(None, lastError)
}
}
}

Testing actor system

i have a next code
class MySystem(outerResourse: OuterResourse) extends Actor {
val firstActor = context.actorOf(Props(new FirstActor(outerResourse)), "first")
val secondActor = context.actorOf(Props(new SecondActor(firstActor)), "second")
secondActor ! Go
def receive = {
case x: AnotherMessage => printl(s"another message: $x")
case x => println(x)
}
}
class FirstActor(outerResourse: OuterResourse) extends Actor {
def receive = {
case Test =>
context.parent ! AnotherMessage
sender ! "ok"
}
}
class SecondActor(firstActor: ActorRef) extends Actor {
def receive = {
case Go => firstActor ! Test
case "ok" => println("ok")
}
}
Here OuterResourse is a any resourse - file, internet connection...
i would like to check a behavior, but i in embarrassment, i do not know how to check that second actor will be got a "ok", and mySystem actor will be got a AnotherMessage
class MyTest(_system: ActorSystem) extends TestKit(_system)
with ImplicitSender with FunSpecLike with Matchers {
def this() = this(ActorSystem("myTest"))
val outerResourse = new OuterResourse()
val mySystem = system.actorOf(Props(new MySytem(outerResourse)))
describe("Actors") {
it("should get AnotherMessage message and ok message") {
???
}
}
}
Hot to check, that a secondActor got a "ok" message?
You could set custom OutputStream for println usnig Console.withOut. It could be mock and "wait" for OK message from first actor. But it is not very nice....
// edit: please read documentation http://doc.akka.io/docs/akka/snapshot/scala/testing.html
Akka provide they owm testing "framework"

How to kill other routees after Akka ScatterGatherFirstCompletedRouter returns?

I'm using ScatterGatherFirstCompletedRouter which returns the answer of the first actor that finishes. This works fine, but some of the other actors time out. Even if I catch the timeouts, it's still a waste of time and resources. I could send a Kill to the actors, but this emits a different message and still doesn't do anything about time (for some reason). What's the best way to handle this?
Rather than have the direct children of ScatterGatherFirstCompletedRouter do the meat of the work, have them delegate to another actor. Be sure to keep track of the original requester so that the other actor can send the response to it.
Perhaps it can be cleaner, but here's what I got (where ValidBoardActor is used with ScatterGatherFirstCompletedRouter):
class ValidBoardActor extends Actor {
private implicit val timeout = Timeout(12.seconds)
private lazy val generateBoardActor = context.actorFor(s"/user/${GenerateBoardActor.name}")
private def stopProcessing: Receive = {
case _ =>
}
private def processBoards(requester: ActorRef, piecesConfigSpec: Configuration.PiecesConfigSpec*): Receive = {
case board: Board => {
if (board.check) {
requester ! board
context.become(stopProcessing)
} else {
self ! GenerateBoard(piecesConfigSpec: _*)
context.become(processRequests(requester))
}
}
}
private def processRequests(requester: ActorRef): Receive = {
case GenerateBoard(configuration # _*) => {
generateBoardActor ! GenerateBoard(configuration: _*)
context.become(processBoards(requester, configuration: _*))
}
}
def receive = {
case message => {
self forward message
context.become(processRequests(sender))
}
}
}