Creating a scheduler/timer inside of akka typed actor - scala

I'm trying to create a scheduler in my akka typed just to test it out, like to run every x seconds.
def start(): Behavior[ClientMsg] = Behaviors.setup { ctx =>
ctx.log.info("start() called")
ctx.system.scheduler.scheduleAtFixedRate(30.seconds, 5000.millis) { () =>
ctx.self ! TestMessage(""""this is pretty cool"""")
}
}
I am getting an error saying an implicit execution context is not in scope.
Where should I get the execution context from when inside of an typed actor?
Also, is this how I should be setting up a scheduler/timer?

Note that using Behaviors.withTimers { timers => ... } should be preferred over directly using the system scheduler as it handles removing scheduled sends if the actors stops etc.

In non-typed Akka the default ExecutionContext is the dispatcher object in the system object:
implicit val executionContext: ExecutionContext = ctx.system.dispatcher
I put this in a base class that I use for all Actors, along with default implicits for Timeout and ActorMaterializer.

You can use just Behaviors.withTimers or both Behaviors.setup and Behaviors.withTimers:
https://doc.akka.io/docs/akka/2.7.0/typed/actor-lifecycle.html
object HelloWorldMain {
final case class SayHello(name: String)
def apply(): Behavior[SayHello] =
Behaviors.setup { context =>
val greeter = context.spawn(HelloWorld(), "greeter")
Behaviors.receiveMessage { message =>
val replyTo = context.spawn(HelloWorldBot(max = 3), message.name)
greeter ! HelloWorld.Greet(message.name, replyTo)
Behaviors.same
}
}
}

Related

How can I add a repeating schedule to send to the masterRegistryActor

def main(args: Array[String]): Unit = {
val rootBehavior = Behaviors.setup[Nothing] { context =>
val masterRegistryActor = context.spawn(Master(), "MasterActor")
context.watch(masterRegistryActor)
masterRegistryActor ! Master.Watchlist("TSLA")
masterRegistryActor ! Master.Watchlist("NVDA")
Behaviors.empty
}
implicit val system = ActorSystem[Nothing](rootBehavior, AppConfig.name)
implicit val executionContext = system.executionContext
}
I am using akka typed as you can see, and I want to understand how I can do the following:
How can I schedule on a repeated timeframe, currently I can only do this:
system.scheduler.scheduleOnce(50 milliseconds, masterRegistryActor, Master.WatchList("AAPLE"))
I did this inside of the Behaviors.Setup, for some reason the system.scheduler.schedule(...) API is not available, why and how do I get around this?
My other confusion is, since my masterRegistryActor is inside of my rootBehavior, how can I reference it from the outside? I wanted to make this actor available throughout my application kind of like a root/global actor but this is the only way I found how to spawn it.
Lets assume that Master is actually a Behavior[MasterMessage] and is defined like following
trait MasterMessage
case object RepeatMasterMessage extends MasterMessage
object Master {
def apply[MasterMessage]() =
Behaviors.setup { context =>
// master behaviour
}
}
Then you can just wrap the Master behaviour into a Behaviors.withTimer.
trait MasterMessage
case object RepeatMasterMessage extends MasterMessage
case object RepeatMasterMessageTimerKey
object Master {
def apply[MasterMessage]() =
Behaviors.withTimer[MasterMessage] { timerScheduler =>
timerScheduler.startTimerWithFixedDelay(
RepeatMasterMessageTimerKey,
RepeatMasterMessage,
Duration(1, TimeUnit.SECONDS),
Duration(5, TimeUnit.MINUTES)
)
Behaviours.setup[MasterMessage] { context =>
// master behaviour
}
}
}

Spray route get response from child actor

I am trying to figure out how I can setup a Master Actor that calls the appropriate children, in support of some spray routes where I am trying to emulate db calls. I am new to akka / spray, so just trying to gain a better understanding of how you would properly setup spray -> actors -> db calls (etc.). I can get the response back from the top level actor, but when I try to get it back from one actor level below the parent I can't seem to get anything to work.
When looking at the paths of the actors, it appears that from the way I am making the call from my spray route that I am passing from a temp actor. Below is what I have so far for stubbing this out. This has to be just user error / ignorance, just not sure how to proceed. Any suggestions would be appreciated.
The Demo Spray Service and Redis Actor code snippets below show where I am calling the actor from my route and the multiple actors where I am having the issue (want my route to get response from SummaryActor). Thanks!
Boot:
object Boot extends App {
// we need an ActorSystem to host our application in
implicit val system = ActorSystem("on-spray-can")
// create and start our service actor
val service = system.actorOf(Props[DemoServiceActor], "demo-service")
implicit val timeout = Timeout(5.seconds)
// start a new HTTP server on port 8080 with our service actor as the handler
IO(Http) ? Http.Bind(service, interface = "localhost", port = 8080)
}
Demo Service Actor (For Spray)
class DemoServiceActor extends Actor with Api {
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = handleTimeouts orElse runRoute(route)
//Used to watch for request timeouts
//http://spray.io/documentation/1.1.2/spray-routing/key-concepts/timeout-handling/
def handleTimeouts: Receive = {
case Timedout(x: HttpRequest) =>
sender ! HttpResponse(StatusCodes.InternalServerError, "Too late")
}
}
//Master trait for handling large APIs
//http://stackoverflow.com/questions/14653526/can-spray-io-routes-be-split-into-multiple-controllers
trait Api extends DemoService {
val route = {
messageApiRouting
}
}
Demo Spray Service (Route):
trait DemoService extends HttpService with Actor {
implicit val timeout = Timeout(5 seconds) // needed for `?` below
val redisActor = context.actorOf(Props[RedisActor], "redisactor")
val messageApiRouting =
path("summary" / Segment / Segment) { (dataset, timeslice) =>
onComplete(getSummary(redisActor, dataset, timeslice)) {
case Success(value) => complete(s"The result was $value")
case Failure(ex) => complete(s"An error occurred: ${ex.getMessage}")
}
}
def getSummary(redisActor: ActorRef, dataset: String, timeslice: String): Future[String] = Future {
val dbMessage = DbMessage("summary", dataset + timeslice)
val future = redisActor ? dbMessage
val result = Await.result(future, timeout.duration).asInstanceOf[String]
result
}
}
Redis Actor (Mock no actual redis client yet)
class RedisActor extends Actor with ActorLogging {
// val pool = REDIS
implicit val timeout = Timeout(5 seconds) // needed for `?` below
val summaryActor = context.actorOf(Props[SummaryActor], "summaryactor")
def receive = {
case msg: DbMessage => {
msg.query match {
case "summary" => {
log.debug("Summary Query Request")
log.debug(sender.path.toString)
summaryActor ! msg
}
}
}
//If not match log an error
case _ => log.error("Received unknown message: {} ")
}
}
class SummaryActor extends Actor with ActorLogging{
def receive = {
case msg: DbMessage =>{
log.debug("Summary Actor Received Message")
//Send back to Spray Route
}
}
}
The first problem with your code is that you need to forward from the master actor to the child so that the sender is properly propagated and available for the child to respond to. So change this (in RedisActor):
summaryActor ! msg
To:
summaryActor forward msg
That's the primary issue. Fix that and your code should start working. There is something else that needs attention though. Your getSummary method is currently defined as:
def getSummary(redisActor: ActorRef, dataset: String, timeslice: String): Future[String] =
Future {
val dbMessage = DbMessage("summary", dataset + timeslice)
val future = redisActor ? dbMessage
val result = Await.result(future, timeout.duration).asInstanceOf[String]
result
}
The issue here is that the ask operation (?) already returns a Future, so there and you are blocking on it to get the result, wrapping that in another Future so that you can return a Future for onComplete to work with. You should be able to simplify things by using the Future returned from ask directly like so:
def getSummary(redisActor: ActorRef, dataset: String, timeslice: String): Future[String] = {
val dbMessage = DbMessage("summary", dataset + timeslice)
(redisActor ? dbMessage).mapTo[String]
}
Just an important comment on the above approaches.
Since the getSummary(...) function returns a Future[String] object and you call it in onComplete(...) function you need to import:
import ExecutionContext.Implicits.global
That way you will have ExecutionContext in scope by letting Future
declare an implicit ExecutionContext parameter.
** If you don't, you will end up getting a mismatching error
since onComplete(...) expects an onComplete Future
magnet Object but you gave a Future[String] Object.

How to send iterables between actors or from an actor to a Future?

A future from the main method of a program sends a msg to its actor asking for an iterable object. The actor then creates another future that asks for the iterable object (say an ArrayBuffer) from a remote actor. After receiving the ArrayBuffer from the remote actor, how would the actor send it back to the first future in the main method? It seems creating a local alias of sender and creating a separate case class to represent the iterable does not prevent dead letters from being encountered.
Here is a sample code:
case class SequenceObject(sqnce:Seq[someArrayBuffer])
//...
implicit val timeout = Timeout(10 seconds)
val fut1: Future[Any] = myActor ? iNeedAnArrayBufferObject
fut1.onSuccess {
case listOfItems: SequenceObject => {
//do sth with listofItems.sqnce
}
class myActor extends Actor {
implicit val timeout = Timeout(1 seconds)
def receive = {
case a: iNeedAnArrayBufferObject => {
val originalSender = sender
val fut: Future[Any] = (remoteActor ? a)
fut.onSuccess {
case list: SequenceObject => {
originalSender ! SequenceObject(list.sqnce)
}
}
The remote actor code is:
class ServerActorClass extends Actor {
def receive = {
case a: iNeedAnArrayBufferObject => {
val closer = sender()
closer ! SequenceObject(ArrayBufferObject[information])
}
}
The above does not seem to work. The remote actor and the local actor can communicate and messages are received correctly. However, the iterable object is never send back to fut1. Why is that? Thanks in advance.
Check pipeTo pattern in Ask: Send-And-Receive-Future section

Akka TypedActor - how to correctly handle async responses with context

I have begun working with TypedActors in Scala and have run into a problem doing something pretty simple: I want Actor A to call a method on Actor B and process the result within an anonymous function on Actor A, but ensuring that:
My response-handling function is thread-safe, e.g. will not run concurrently with any other threads accessing Actor A’s state
My response-handling function can reference the context of Actor A
How can I (or can I) satisfy both of those requirements?
For example, this actor just wants to call an API on otherActor that returns a Future[Int], and update it's state with the result and then do something that requires it's actor context:
class MyActorImpl extends MyActor {
// my mutable state
var myNumber = 0
// method proxied by TypedActor ref:
def doStuff(otherActor: OtherActor): Unit = {
otherActor.doOtherStuff onSuccess {
// oops this is no longer running in MyActorImpl..
// this could be on a concurrent thread if we
case i => processResult(i)
}
}
private def processResult(i: Int): Unit = {
myNumber = 0 // oops, now we are possibly making a concurrent modification
println(s"Got $i")
// fails with java.lang.IllegalStateException: Calling TypedActor.context
// outside of a TypedActor implementation method!
println(s"My context is ${TypedActor.context}")
}
}
What am I missing here? Do I need to write my handler to call a method defined on the proxied interface to guarantee single-entry? That would seem ugly if I do not want to expose that particular “private” method (e.g. processResult) on an interface.
Here is a full version that will run in the Scala REPL:
import akka.actor._
import scala.concurrent._
val system = ActorSystem("mySystem")
import system.dispatcher
trait OtherActor {
def doOtherStuff(): Future[Int]
}
trait MyActor {
def doStuff(otherActor: OtherActor): Unit
}
class OtherActorImpl extends OtherActor {
var i = 0
def doOtherStuff(): Future[Int] = {
i += 1
Future {i}
}
}
class MyActorImpl extends MyActor {
// my mutable state
var myNumber = 0
// method proxied by TypedActor ref:
def doStuff(otherActor: OtherActor): Unit = {
otherActor.doOtherStuff onSuccess {
// oops this is no longer running in MyActorImpl..
// this could be on a concurrent thread if we
case i => processResult(i)
}
}
private def processResult(i: Int): Unit = {
myNumber = 0 // oops, now we are possibly making a concurrent modification
println(s"Got $i")
// fails with java.lang.IllegalStateException: Calling TypedActor.context
// outside of a TypedActor implementation method!
println(s"My context is ${TypedActor.context}")
}
}
val actor1: MyActor = TypedActor(system).typedActorOf(TypedProps[MyActorImpl])
val actor2: OtherActor = TypedActor(system).typedActorOf(TypedProps[OtherActorImpl])
actor1.doStuff(actor2)
You are exposing state of the Actor to outside world and that's a very bad thing. Look here: http://doc.akka.io/docs/akka/2.3.3/general/jmm.html section Actors and shared mutable state lines 9-10 describe your case.
#philwalk already described how you could fix this problem: Akka TypedActor - how to correctly handle async responses with context

Turning Scala Actors To Akka Actors: One Instance To Call Methods On

Recently I was switching from scala actors to akka actors, but noticed that akka actors use ActorRef instead of the instance object:
val actorRef: ActorRef = Actor.actorOf(new MyActor)
So I tried:
val myActor = new MyActor
val actorRef: ActorRef = Actor.actorOf(x)
... to have both: 1) ActorRef to send messages and 2) MyActor to call methods on.
But I got:
akka.actor.ActorInitializationException: ActorRef for instance of actor [MyActor] is not in scope.
So my question is: How can I obtain an instance (of some type) on which I can call ActorRef-like methods like ! AND also methods from the MyActor instance?
What you're doing is a terrible idea. So just stop right now, step away from the keyboard, and go to the Akka Documentation and read up on Actors.
Consider this:
class YourActor extends Actor {
var mutableStuff = ...
def receive = {
case _ =>
// mess with mutableStuff
}
def publicMethod = // mess with mutableStuff
}
Now, set up your system and start sending messages and calling that method from other threads. Boom!
You're doing precisely what Akka and the Actor model help you prevent. You're actually bending over backwards to break what they've already fixed :) They won't let you do it.
Now, you can unit test by accessing methods directly but you need a TestActorRef for that. While you're reading the docs, read through the section on Testing.
The best that I can up with is the following, quite dirty:
Is there a better way?
import akka.actor._
trait ActorCom {
var actorRefForInitialization: ActorRef = _
lazy val actorRef: ActorRef = actorRefForInitialization
def ?(message: Any)(implicit channel: UntypedChannel = NullChannel, timeout: Actor.Timeout = Actor.defaultTimeout) = actorRef ? message
def !(msg: Any)(implicit sender: UntypedChannel) = actorRef ! msg
def start = actorRef.start
}
object AkkaActorFactory {
def apply[A <: Actor](newInstance: => A with ActorCom): A with ActorCom = {
var instance: Option[A with ActorCom] = None
val actorRef = Actor.actorOf({
instance = Some(newInstance)
instance.get
})
instance.get.actorRefForInitialization = actorRef
instance.get.actorRef // touch lazy val in ActorCom, to make it equal to actorRef and then its fixed (immutable)
instance.get
}
}
class MyActor extends Actor {
def receive = {
case "test1" => println("good")
case "test2" => println("fine")
case _ => println("bad")
}
def sendTestMsg2Myself = self ! "test2"
}
val myActor = AkkaActorFactory(newInstance = new MyActor with ActorCom)
myActor.start
myActor ! "test1"
myActor.sendTestMsg2Myself // example for calling methods on MyActor-instance
myActor ! PoisonPill