Single task typed actors - scala

I often use actors that perform a single computationally expensive task. After
completing its task this type of actors sends the result to its creator und
terminates itself afterwards.
Im am not quit sure what's the best practice for implementing such actors. I
could imagine the following possibilities:
Variant 1: start task in "constructor"
object SingleTaskBehavior:
sealed trait Reply
final case class Result(value: Int) extends Reply
def variant1(arg: Int, replyTo: ActorRef[Reply]): Behavior[Nothing] =
Behaviors.setup[Nothing] { context =>
val result = performLongRunningTask(arg)
replyTo ! Result(result)
Behaviors.stopped
}
end variant1
Variant 2: send message to actor itself
object SingleTaskBehavior:
sealed trait Command
private case object Init extends Command
sealed trait Reply
final case class Result(value: Int) extends Reply
def variant2(arg: Int, replyTo: ActorRef[Reply]): Behavior[Command] =
Behaviors.setup[Command] { context =>
context.self ! Init
Behaviors
.receiveMessage[Command] {
case Init =>
val result = performLongRunningTask(arg)
replyTo ! Result(result)
Behaviors.stopped
}
}
end variant2
Variant 3: pipe pattern
object SingleTaskBehavior:
sealed trait Command
private case object Init extends Command
private final case class AdaptedResult(result: Result) extends Command
private final case class AdaptedFailure(ex: Throwable) extends Command
sealed trait Reply
final case class Result(value: Int) extends Reply
def variant3(arg: Int, replyTo: ActorRef[Reply]): Behavior[Command] =
Behaviors.setup[Command] { context =>
given ExecutionContext = context.system.executionContext
val futureResult = Future { performLongRunningTask(arg) }
context.pipeToSelf(futureResult) {
case Success(r) => AdaptedResult(Result(r))
case Failure(ex) => AdaptedFailure(ex)
}
Behaviors
.receiveMessage[Command] {
case AdaptedResult(result) =>
replyTo ! result
Behaviors.stopped
case AdaptedFailure(ex) => throw ex
}
}
end variant3
Variant 1 is of course the simplest one. But is it also correct? Is it okay to
execute a long-running task in the setup method (so to say in the constructor of
the actor) even if unhandled exceptions may be thrown in the long-running task?
Or is it better to perform this type of operations only when messages are
dispatched by the actor system (variant 2 and variant 3)?

Assuming that you're running the single-task actor on a dispatcher which is well-suited to long-running tasks (e.g. one with a lot of threads for tasks which are long-running because they do blocking I/O and don't consume CPU or one with very few threads relative to the default dispatcher for tasks which are long-running because they do a lot of computation), any of the approaches is viable. Note that the default Akka dispatcher is not particularly well-suited to either of these.
Note that Behaviors.setup isn't quite like a constructor: it's more akin to registering a callback that will execute in the actor's "cell" after the actor has started but before it processes any message. In a context.spawn(SingleTaskBehavior.variant1(42, context.self)), the evaluation in the caller is effectively:
wrap the closure { context => ... } in a WrappedBehavior
spawn an actor with that WrappedBehavior
The ActorSystem then runs that WrappedBehavior (almost certainly in a different thread and only in the same thread if the spawning actor has completed processing the message), passing in the ActorContext.
Because Init is private in variant2, there isn't really a practical difference between variant1 and variant2 (especially since we're not introducing supervision).
That said, an actor which doesn't receive messages is a bit of a smell: the whole benefit of having an actor is to receive messages and change behavior based on the messages received. Such an actor is probably better off as a Future, in which case you'd just, in the spawning actor:
context.pipeToSelf(Future { performLongRunningTask(42) }(appropriateDispatcher)) {
case Success(r) => Result(r)
case Failure(ex) => NoOp // a command which this actor ignores
}
NB: If your long-running task is long-running because it both does blocking I/O and consumes CPU, you're going to be best off treating it as subtasks that basically only do one or the other and scheduling those subtasks on the respective appropriate dispatcher.

Related

How to supervise actors in Akka and handle exceptions

I am trying to improve the error handling with the actors in my system. Sometimes, when processing data, something goes wrong, and I need to stop and restart the actor, as well as log some information regarding this failure.
I have a Supervisor, which has 5 actors working for it. So I need to be able to supervise all of them. I found this link:
https://doc.akka.io/docs/akka/current/typed/fault-tolerance.html
regarding this, but I don't think it is very clear on where to implement the code:
Behaviors.supervise(behavior).onFailure[IllegalStateException](SupervisorStrategy.restart)
Where exactly is this code supposed to go?
Thanks
You can think of this supervisor as another behavioiur which wraps your behaviour inside of it.
Lets say you want to have following HelloWorld actor.
object HelloWorldActor {
sealed trait Command
case class HelloCommand(name: String) extends Command
def apply(): Behavior[Command] =
Behaviors.receiveMessage[Command] {
case msg: HelloCommand =>
println(s"Hello ${msg.name}")
Behaviors.same
}
}
Now, you can "wrap" this "behaviour" with a "supervisor"
object SupervisedHelloWorldActor {
sealed trait Command
case class HelloCommand(name: String) extends Command
def apply(): Behavior[Command] =
Behaviors.supervise(
Behaviors.receiveMessage[Command] {
case HelloCommand(name) =>
println(s"Hello ${name}")
Behaviors.same
}
).onFailure(onFailure[IllegalStateException](SupervisorStrategy.restart))
}

How to avoid mutating of a state variable in an Akka actor with inherited behavior?

I have some Akka actors with a common behavior. This common behavior is defined in a trait:
trait CommonBehavior {
this: Actor =>
var history: List[String] = Nil
protected def commonActions: Receive = {
case Action1 => history = "action1" :: history.take(99)
case Action2 => history = "action2" :: history.take(99)
case GetHistory => sender() ! history
}
}
The actors override this trait and define additional behavior with orElse. This is one example of such actor:
class MyActor extends Actor with CommonBehavior {
var state: Int = 0
override def receive: Receive =
commonActions orElse {
case Increment => state += 1
case Decrement => state -= 1
}
}
I know that mutating the state is an antipattern and I want to refactor it with the usage of context.become. The problem is, when changing state in MyActor with context.become, I do not know the parameter for commonActions. Is it even possible to inherit behavior? Do I need a bigger refactor (e.g. creating a proxy actor)? This is how far I have got:
trait CommonBehavior {
this: Actor =>
protected def commonActions(history: List[String]): Receive = {
case Action1 => context.become(??? orElse commonActions("action1" :: history.take(99))
case Action2 => context.become(??? orElse commonActions("action2" :: history.take(99))
case GetHistory => sender() ! history
}
}
class MyActor extends Actor with CommonBehavior {
override def receive = ready(0)
def ready(state: Int): Receive = {
case Increment => context.become(ready(state + 1) orElse commonActions(???))
case Decrement => context.become(ready(state - 1) orElse commonActions(???))
} orElse commonActions(Nil)
}
Mutating state in an actor is not an antipattern, it is fine to do more OO style actors (and can in high througput cases be more performant) which mutate state in response to messages. Deciding to do a more FP-style is a personal preference so consider the pros and cons for your use case (your experience, team experience, project size, etc) rather than follow someones opinion dogmatically. Especially if you already have a class hierarchy that expects you to do mutations rather than switch behaviors.
If you decide you want to do a more FP-style actor, I'd recommend reconsidering the whole structure and not have a mixin with mutable state, requiring to be mixed in with an Actor to start with but design from that point of view. I'd also recommend going with the new typed APIs as the "FP:y" side of those gives a much nicer experience than using become with the classic Actor APIs.
As I understood, you want to avoid state mutation within actor by changing the context of the actor. So, there are 2 possibilities:
If you are afraid to mutate the state within the actor, you can go with that as the mutation within the actor is fine and does not lead to concurrency issues. Its because actors work synchronously.
If you still want to keep the state immutable (val) and allow changing the state on runtime, you can do that using become. If you don't know the parameters for commonActions, you can either initialize it with a default value or pick the value from storage depending on business logic and then update accordingly.

Why creating an actor within actor is dangerous

The akka documentation is clearly stated that it is dangerous to create an actor within an actor like this:
class ActorA extends Actor {
def receive = ???
}
final class ActorB extends Actor {
def receive = {
case _ =>
val act = context.actorOf(Props(new ActorA))
}}
I understand that the Actor's apply method is accepting this reference of the creating actor. yet I couldn't understand (nor couldn't find any example) why this is harmful and what issues it can cause?
Let's tweak your example a little bit
class ActorA(str:String) extends Actor {
def receive = ???
}
final class ActorB extends Actor {
def receive = {
case _ =>
val act = context.actorOf(Props(new ActorA("hidden")))
}}
Most of the common use case of using actors are to handle failover and supervision, shen an actor fails and needs to be restarted, the actor system needs to know how to do that. When you use Props(Props(new ActorA)), you've hidden the parameter value of "hidden" by handling it yourself.
Rather than doing that if instead, you declare how to create instances of the actor, the actor system will know exactly what it needs to do when recreating an actor -
i.e. create an instance of ActorA with a constructor argument of "hidden".
Even with your example of Actor without param
context.actorOf(Props(new ActorA))
this way of instantiating actors within another actor is not recommended because it encourages to close over the enclosing scope, resulting in non-serializable Props and possibly race conditions (breaking the actor encapsulation).
I believe we are confusing creation and declaration. The doc says that
Declaring one actor within another is very dangerous and breaks actor encapsulation. Never pass an actor’s this reference into Props!
So the problem is declaration, not creation!
Let's look at Java's:
public class MyActor extends AbstractActor {
#Override
public Receive createReceive() {
return ReceiveBuilder.create()
.match(String.class, handleString())
.matchAny(x -> unhandled(x))
.build();
}
private FI.UnitApply<String> handleString() {
return message -> sender().tell("OK", getSelf());
}
class MyOtherActor extends AbstractActor {
#Override
public Receive createReceive() {
return ReceiveBuilder.create()
.match(String.class, handleString())
.matchAny(x -> unhandled(x))
.build();
}
private FI.UnitApply<String> handleString() {
return message -> sender().tell("OK-Inner", getSelf());
}
}
}
Now, if MyOtherActor was a normal class, we'd be able to instantiate it only from an instance of MyActor:
MyActor actor = new MyActor();
MyActor.MyOtherActor otherActor = actor.new MyOtherActor();
Which means that the constructor for MyOtherActor depends on the instance of MyActor!
Now, if Props are supposed to contain the "factory" of the actor. They need a factory method. If our MyOtherActor is declared as we did here, then our props would look like this (ish):
MyActor actor = ??? // how did you even get a reference to the actor and not the actorRef in the first place!
Props otherActorProps = Props.create(MyActor.MyOtherActor.class, () -> actor.new MyOtherActor());
And bang, here comes the kicker! Now your otherActorProps contains a reference to actor, i.e. you have closed over mutable state! If for whatever reason actor "dies", your props will still be referencing it, causing all sort of weirdness.
There is also the issue of how you get a reference to the actor in the first place, and not it's actorRef
IMHO, that's what the documentation is referring to, and not the fact of "creating" (i.e. instantiating, spawning) an actor within another one: that's absolutely normal and it's a routine operation of akka (that's why you can do getContext().actorOf(..) as well as actorSystem.actorOf(...)
The warning is there in the documentation because it's easy to accidentally close over the creating actor's state, including its this pointer (which you should never use in actor-based code).
In my experience, I've usually seen a props method put into an actor's companion object:
object ActorA {
def props() = Props(new ActorA)
}
Doing it that way ensures the returned Props isn't closing over an actor's state.
class ActorB extends Actor {
def receive = {
case _ =>
val actorB = context.actorOf(ActorA.props)
...
}
}
It's not as big of a possibility for actors that don't take constructor parameters, but once parameters come into play you need to be careful about closing over internal state.

Strategy pattern in Akka

This is an continuation of my previous question How do I get around type erasure on Akka receive method
I have 10 type of events which extends from Event that I need to handle.
I want to implement business logic for each event in separate trait, because because mixing all 10 event handler functions will produce several hundreds(if not thousands) lines of code.
I don't want to create different Actor types for each event. For example:
class Event1Actor extend Actor{
def receive ={
case Event1(e) => //event1 Business Logic
}
}
class Event2Actor extend Actor{
def receive ={
case Event2(e) => //event2 Business Logic
}
}
and the same Event3Actor, Event4Actor,etc....
Such code seems ugly to me, because I need to implement business Logic inside each Actor.
Implementing 10 different traits and 10 different Actor classes seems also as bad design.
I'm seeking for some kind generic solution based on design pattern, for example strategy pattern.
case class EventOperation[T <: Event](eventType: T)
class OperationActor extends Actor {
def receive = {
case EventOperation(eventType) => eventType.execute
}
}
trait Event {
def execute //implement execute in specific event class
}
class Event1 extends Event {/*execute implemented with business logic*/}
class Event2 extends Event {/*execute implemented with business logic*/}
hope this is what you are looking for and helps, I have used this patternt to remove the redundant amount of actors wrapping all actions under a single actor executing different type of events.
You could try something like this, which involves auto-composing receive functionality via a base trait. First the code:
case class Event1(s:String)
case class Event2(i:Int)
case class Event3(f:Float)
trait EventHandlingActor extends Actor{
var handlers:List[Receive] = List.empty
override def preStart = {
val composedReceive = handlers.foldLeft(receive)((r,h) => r.orElse(h))
context.become(composedReceive)
}
def addHandler(r:Receive) {
handlers = r :: handlers
}
def receive = PartialFunction.empty[Any,Unit]
}
trait Event1Handling{ me:EventHandlingActor =>
addHandler{
case Event1(s) => println(s"${self.path.name} handling event1: $s")
}
}
trait Event2Handling{ me:EventHandlingActor =>
addHandler{
case Event2(i) => println(s"${self.path.name} handling event2: $i")
}
}
trait Event3Handling{ me:EventHandlingActor =>
addHandler{
case Event3(f) => println(s"${self.path.name} handling event3: $f")
}
}
So you can see in the EventHandlingActor trait we set up a List of type Receive that can be added to by each specific handling trait that we stack into a concrete actor. Then you can see the definitions of the handling functionality for each event defined in a separate trait that is calling addHandler to add another piece of handling functionality. In preStart for any kind of EventHandlingActor impl the receive functions will be composed together with receive being the starting point (empty by default) before hot-swapping out the receive impl with context.become.
Now for a couple of impl actors as an example:
class MyEventHandlingActor extends EventHandlingActor
with Event1Handling with Event2Handling with Event3Handling
case class SomeOtherMessage(s:String)
class MyOtherEventHandlingActor extends EventHandlingActor with Event1Handling{
override def receive = {
case SomeOtherMessage(s) => println(s"otherHandler handling some other message: $s")
}
}
The first one only handles events, so all it needs to do is define which ones it handles my mixing in the appropriate traits. The second one handles one type of event but also some other message that is not an event. This class overrides the default empty receive and provides functionality to handle the non-event message.
If we tested the code like so:
val system = ActorSystem("test")
val handler = system.actorOf(Props[MyEventHandlingActor], "handler")
handler ! Event1("foo")
handler ! Event2(123)
handler ! Event3(123.456f)
val otherHandler = system.actorOf(Props[MyOtherEventHandlingActor], "otherHandler")
otherHandler ! Event1("bar")
otherHandler ! SomeOtherMessage("baz")
Then we would see output similar to this (with the order changing due to asynch handling of messages):
otherHandler handling event1: bar
handler handling event1: foo
handler handling event2: 123
handler handling event3: 123.456

Puzzled by the spawned actor from a Spray route

I am doing some Http request processing using Spray. For a request I spin up an actor and send the payload to the actor for processing and after the actor is done working on the payload, I call context.stop(self) on the actor to wind the actor down.The idea is to prevent oversaturation of actors on the physical machine.
This is how I have things set up..
In httphandler.scala, I have the route set up as follows:
path("users"){
get{
requestContext => {
val userWorker = actorRefFactory.actorOf(Props(new UserWorker(userservice,requestContext)))
userWorker ! getusers //get user is a case object
}
}
} ~ path("users"){
post{
entity(as[UserInfo]){
requestContext => {
userInfo => {
val userWorker = actorRefFactory.actorOf(Props(new UserWorker(userservice,requestContext)))
userWorker ! userInfo
}
}
}
}
}
My UserWorker actor is defined as follows:
trait RouteServiceActor extends Actor{
implicit val system = context.system
import system.dispatcher
def processRequest[responseModel:ToResponseMarshaller](requestContex:RequestContext)(processFunc: => responseModel):Unit = {
Future{
processFunc
} onComplete {
case Success(result) => {
requestContext.complete(result)
}
case Failure(error) => requestContext.complete(error)
}
}
}
class UserWorker(userservice: UserServiceComponent#UserService,requestContext:RequestContext) extends RouteServiceActor{
def receive = {
case getusers => processRequest(requestContext){
userservice.getAllUsers
}
context.stop(self)
}
case userInfo:UserInfo => {
processRequest(requestContext){
userservice.createUser(userInfo)
}
context.stop(self)
}
}
My first question is, am I handling the request in a true asynchronous fashion? What are some of the pitfalls with my code?
My second question is how does the requestContext.complete work? Since the original request processing thread is no longer there, how does the requestContext send the result of the computation back to the client.
My third question is that since I am calling context.stop(self) after each of my partial methods, is it possible that I terminate the worker while it is in the midst of processing a different message.
What I mean is that while the Actor receives a message to process getusers, the same actor is done processing UserInfo and terminates the Actor before it can get to the "getusers" message. I am creating new actors upon every request, but is it possible that under the covers, the actorRefFactory provides a reference to a previously created actor, instead of a new one?
I am pretty confused by all the abstractions and it would be great if somebody could break it down for me.
Thanks
1) Is the request handled asynchronously? Yes, it is. However, you don't gain much with your per-request actors if you immediately delegate the actual processing to a future. In this simple case a cleaner way would be to write your route just as
path("users") {
get {
complete(getUsers())
}
}
def getUsers(): Future[Users] = // ... invoke userservice
Per-request-actors make more sense if you also want to make route-processing logic run in parallel or if handling the request has more complex requirements, e.g. if you need to query things from multiple service in parallel or need to keep per-request state while some background services are processing the request. See https://github.com/NET-A-PORTER/spray-actor-per-request for some information about this general topic.
2) How does requestContext.complete work? Behind the scenes it sends the HTTP response to the spray-can HTTP connection actor as a normal actor message "tell". So, basically the RequestContext just wraps an ActorRef to the HTTP connection which is safe to use concurrently.
3) Is it possible that "the worker" is terminated by context.stop(self)? I think there's some confusion about how things are scheduled behind the scenes. Of course, you are terminating the actor with context.stop but that just stops the actor but not any threads (as threads are managed completely independently from actor instances in Akka). As you didn't really make use of an actor's advantages, i.e. encapsulating and synchronizing access to mutable state, everything should work (but as said in 1) is needlessly complex for this use case). The akka documentation has lots of information about how actors, futures, dispatchers, and ExecutionContexts work together to make everything work.
In addition to jrudolph answer your spray routing structure shouldn't even compile, cause in your post branch you don't explicitly specify a requestContext. This structure can be simplified a bit to this:
def spawnWorker(implicit ctx: RequestContext): ActorRef = {
actorRefFactory actorOf Props(new UserWorker(userservice, ctx))
}
lazy val route: Route = {
path("users") { implicit ctx =>
get {
spawnWorker ! getUsers
} ~
(post & entity(as[UserInfo])) {
info => spawnWorker ! info
}
}
}
The line info => spawnWorker ! info can be also simplified to spawnWorker ! _.
Also there is an important point concerning explicit ctx declaration and complete directive. If you explicitly declared ctx in your route, you can't use complete directive, you have to explicitly write ctx.complete(...), link on this issue