From Akka Cookbook, example from chapter Persistent Actors. In SamplePersistentActor.scala there is line of code that I don't quite understand. Here's the full code of 2 files.
SamplePersistentActor.scala:
class SamplePersistenceActor extends PersistentActor {
override val persistenceId = "unique-id-1"
var state = ActiveUsers()
def updateState(event:Event) = state = state.update(event)
val receiveRecover: Receive = {
case evt: Event => updateState(evt)
case SnapshotOffer(_, snapshot: ActiveUsers) => state = snapshot
}
override val receiveCommand: Receive = {
case UserUpdate(userId, Add) =>
persist(AddUserEvent(userId))(updateState)
case UserUpdate(userId, Remove) =>
persist(RemoveUserEvent(userId))(updateState)
case "snap" => saveSnapshot(state)
case "print" => println(state)
}
}
SamplePersistentModel.scala:
sealed trait UserAction
case object Add extends UserAction
case object Remove extends UserAction
case class UserUpdate(userId: String, action: UserAction)
sealed trait Event
case class AddUserEvent(userId: String) extends Event
case class RemoveUserEvent(userId: String) extends Event
case class ActiveUsers(users: Set[String] = Set.empty[String])
{
def update(evt: Event)= evt match {
case AddUserEvent(userId) => copy(users + userId)
case RemoveUserEvent(userId) => copy(users.filterNot(_ == userId))
}
override def toString = s"$users"
}
My question
What is the purpose of = state (or = this.state if I'm correct) in line def updateState(event:Event) = state = state.update(event). Why can't we just use def updateState(event:Event) = state.update(event)?
Found similar in documentation example.
In your sample code:
def updateState(event: Event) = state = state.update(event)
is equivalent to:
def updateState(event: Event) = { state = state.update(event) }
So, updateState is a function of Event => Unit, which is exactly what method persist expects as its second parameter:
persist(AddUserEvent(userId))(updateState)
Below is the signature of method persist in Akka PersistentActor:
trait PersistentActor extends Eventsourced with PersistenceIdentity {
// ...
def persist[A](event: A)(handler: A => Unit): Unit = {
internalPersist(event)(handler)
}
// ...
}
It expects a EventType => Unit handler code block as its second parameter to handle specific persistence business logic that generally involves updating internal states of the actor.
Why can't we just use def updateState(event:Event) = state.update(event)?
The reason for the reassignment to the state variable is that update creates a new object. In other words, calling state.update doesn't mutate state; it makes a copy of state with the updated information.
This is the case with the example that you referenced:
var state = ExampleState()
def updateState(event: Evt): Unit =
state = state.updated(event)
Looking at the code for ExampleState, we see that the updated method actually creates a new ExampleState object:
def updated(evt: Evt): ExampleState = copy(evt.data :: events)
Related
I have a actor like this:
class MyActor[T] extends Actor {
def receive = {
case Option1(t: T) =>
doWork(t) onComplete .....
case Option2 =>
}
def doWork(T): Future[T]{
}
}
Then I have an actor that inherits from the above:
class OtherActor extends MyActor {
val m = mutable.Map.empty[Int, User]
override def doWork(..) = {
//
}
}
Now in my OtherActor actor I want to add some methods to the receive method, how can I do this?
You can define the additional behavior in a Receive block inside OuterActor and chain that behavior to its parent's behavior with orElse:
class OtherActor extends MyActor {
val m = mutable.Map.empty[Int, User]
override def doWork(...) = ???
val otherBehavior: Receive = {
case ...
}
override def receive = otherBehavior.orElse(super.receive)
}
This is possible because Receive is just a type alias for PartialFunction[Any, Unit]. More information on composing actor behaviors is found here.
As a side note, you should prefer var m = immutable.Map.empty[Int, User] instead of val m = mutable.Map.empty[Int, User] in order to help avoid exposing the actor's state, as described in this answer.
Good day. I'm making a simple program which check's some server state and faced the issue with pattern matching.
Here is the code:
Entry point:
object Run extends App with StateActor.Api{
private implicit val system = ActorSystem()
implicit val blockingDispatcher: MessageDispatcher = system.dispatchers.lookup("blocking-dispatcher")
protected val log: LoggingAdapter = Logging(system, getClass)
protected implicit val materializer: ActorMaterializer = ActorMaterializer()
import scala.concurrent.duration._
implicit val timeout = Timeout(17 seconds)
val listener = system.actorOf(StateActor.props)
system.scheduler.schedule(
0 milliseconds,
5 minutes,
listener,
Ping
)
}
Actor:
class StateActor(implicit val blockingDispatcher: MessageDispatcher) extends Actor with StateActor.Api with ActorLogging {
import akka.pattern.pipe
private val formatter = JSONFormat.defaultFormatter
private val mHookUrl = ...
var mState: State = UNDEFINED
override def receive: Receive = {
case Ping =>
log.debug("Ping")
Future(Http("http://...").timeout(15000, 15000).asString)
.map {
case HttpResponse(_, 200, _) => UpResponse
case HttpResponse(body, code, _) => DownResponse(s"Code: $code, body:\n $body")
case rest => DownResponse(s"Undefined object: ${rest.toString}")
} recover { case e => DownResponse(e.getMessage) } pipeTo self
case UpResponse =>
if (mState == DOWN || mState == UNDEFINED) {
mState == UP
reportToSlack("Client Up")
}
case DownResponse(reason) =>
if (mState == UP || mState == UNDEFINED) {
mState == DOWN
reportToSlack(s"Client DOWN!\n Reason: $reason")
}
case other =>
println(other)
println(other.getClass)
}
def reportToSlack(message: String): Unit = {
...
}
}
object StateActor {
trait Api {
case object Ping
sealed trait State
case object UP extends State
case object DOWN extends State
case object UNDEFINED extends State
sealed trait StateMessage
case object UpResponse extends StateMessage
case class DownResponse(reason: String) extends StateMessage
}
def props(implicit blockingDispatcher: MessageDispatcher) = Props(new StateActor())
}
As you can see, I put all messages and other stuff intoto trait "API" inside "StateActor" companion object. But when scheduler sends "Ping" to actor, it matches 'case other', not 'case Ping'. Problem can be solved just by moving 'case object Ping' out from trait and companion object and making it 'stand alone' object. Like this:
case object Ping
object StateActor {
trait Api {
...
}
...
}
But why it doesn't work when it's inside trait? All other case classes and objects in trait pattern match just fine.
Run and StateActor both extend the trait separately, so each has its own Ping object and they shouldn't match. The only reason other messages match is because the StateActor is sending them to itself! It wouldn't even work with two different StateActor instances.
Instead of
moving 'case object Ping' out from trait and companion object
you should make Api an object and make the messages accessible by importing them: import StateActor.Api._ instead of extends StateActor.Api (or put them directly into object StateActor).
case class State(id: Long, remain: Int) {
def take(t: Take) = copy(remain = remain - t.amount)
}
object StateService {
def getInitState(id: Long): Future[State]
}
sealed trait Evt
case class Init(id: Long) extends Evt
case class Take(id: Long, amount: Int) extends Evt
class FooActor extends PersistentActor {
var state: State
def receiveCommand = {
case Init(id) => ??? // how to
case t: Take => persistAsync(t) {case e => state = state.take(t)}
}
}
object FooActor {
}
As example described
How could I init the actor state before accepting any other commands ?
You can use different behaviors:
case class State(id: Long, remain: Int)
object StateService {
def getInitState(id: Long): Future[State]
}
sealed trait Evt
case class Init(id: Long) extends Evt
class FooActor extends PersistentActor {
var state: State
import akka.pattern.pipe
def notInitialized: Receive = {
case Init(id) =>
// for simplicity, failure is not handled
StateService.getInitState(id) pipeTo self
case st: State =>
state = st
context become initialized
}
def initialized: Receive = {
case _ => // meh
}
def receiveCommand = notInitialized
}
object FooActor {
}
You can even remove the mutable state altogether by passing it as parameters to the initialized behavior (e.g. initialized(state)).
And regarding the recovery, from official Akka docs:
It's also possible to switch between different command handlers during
normal processing and recovery with context.become() and
context.unbecome(). To get the actor into the same state after
recovery you need to take special care to perform the same state
transitions with become and unbecome in the receiveRecover method as
you would have done in the command handler. Note that when using
become from receiveRecover it will still only use the receiveRecover
behavior when replaying the events. When replay is completed it will
use the new behavior.
I have to actor classes which looks similar to this form:
class ActorSupervisorOne(prop: Prop) extends Actor {
val dbSuper = context.actorOf(prop)
val subActor = context.actorOf(Props(new SubActorClass(dbSuper) with **SomeHandlersOne**))
def receive = {
case msg =>
subActor forward msg
}
}
class ActorSupervisorTwo(prop: Prop) extends Actor {
val dbSuper = context.actorOf(prop)
val subActor = context.actorOf(Props(new SubActorClass(dbSuper) with **SomeHandlersTwo**))
def receive = {
case msg =>
subActor forward msg
}
}
The only difference between them in mixing trait. Abstract it with type parameter or abstract type member won't work. I've tried the following solution, but it looks ugly and still have code duplication:
abstract class Super extends Actor {
_: {
val handler: Props
} =>
lazy val actor = context.actorOf(handler)
def receive = {
case msg =>
actor forward msg
}
}
class ActorSupervisorOne(val dbSuper: ActorRef) extends Super {
val handler = Props(new SubActorClass(dbSuper) with SomeHandlersOne)
actor
}
class ActorSupervisorTwo(val dbSuper: ActorRef) extends Super {
val handler = Props(new SubActorClass(dbSuper) with SomeHandlersTwo)
actor
}
But in this case i have to call actor to initialize it correctly or it won't work. Are there any other solution how this can be reduced?
You could probably use reflection to choose the SomeHandlersXYZ at runtime, but if you don't want to resort to reflection then I don't think that there is a way of achieving what you want without at least duplicating the subactor instantiation code (see this answer of mine for an explanation). You could do it the following way (sketched), where you basically pass in a factory function:
class ActorSupervisor(prop: Prop, getSubActor: Actor => SubActorClass) extends Actor {
val dbSuper = context.actorOf(prop)
val subActor = context.actorOf(Props(getSubActor(dbSuper)))
def receive = {
case msg =>
subActor forward msg
}
}
val asOne = new ActorSupervisor(..., a => new SubActorClass(a) with SomeHandlersOne)
val asTwo = new ActorSupervisor(..., a => new SubActorClass(a) with SomeHandlersTwo)
What about this solution:
class ActorSupervisor(subActor: => Actor) extends Actor {
val actor = context.actorOf(Props(statsProps))
def receive = {
case msg =>
actor forward msg
}
}
and then, like in Malte Schwerhoff you can create new actor like this:
val first = new ActorSupervisor(new SubActorClass(...) with SomeHandlersOne)
I think that more elegant colution can be achived with macros, but i'm not good at them
You can pass the puck on onReceive.
class ClassMixed(params: Options*)
extends BaseClass(params: _*)
with Mixin {
override def receive =
mixinReceive orElse receive
}
where Mixin has a method called mixinReceive and BaseClass overrides receive
Consider these two traits:
trait Poked extends Actor {
override def receive = {
case Poke(port, x) => ReceivePoke(port, x)
}
def ReceivePoke(port: String, x: Any)
}
trait Peeked extends Actor {
override def receive = {
case Peek(port) => ReceivePeek(port)
}
def ReceivePeek(port: String)
}
Now consider I can create a new Actor that implements both traits:
val peekedpoked = actorRef(new Actor extends Poked with Peeked)
How do I compose the receive handlers? i.e., the receiver should be something like the following code, though "automatically generated" (i.e., all traits should compose):
def receive = (Poked.receive: Receive) orElse (Peeked.receive: Receive) orElse ...
You can use super[T] to reference members of particular super classes/traits.
For example:
trait IntActor extends Actor {
def receive = {
case i: Int => println("Int!")
}
}
trait StringActor extends Actor {
def receive = {
case s: String => println("String!")
}
}
class IntOrString extends Actor with IntActor with StringActor {
override def receive = super[IntActor].receive orElse super[StringActor].receive
}
val a = actorOf[IntOrString].start
a ! 5 //prints Int!
a ! "Hello" //prints String!
Edit:
In response to Hugo's comment, here's a solution that allows you to compose the mixins without having to manually wire their receives together. Essentially it involves a base trait with a mutable List[Receive], and each mixed-in trait calls a method to add its own receive to the list.
trait ComposableActor extends Actor {
private var receives: List[Receive] = List()
protected def registerReceive(receive: Receive) {
receives = receive :: receives
}
def receive = receives reduce {_ orElse _}
}
trait IntActor extends ComposableActor {
registerReceive {
case i: Int => println("Int!")
}
}
trait StringActor extends ComposableActor {
registerReceive {
case s: String => println("String!")
}
}
val a = actorOf(new ComposableActor with IntActor with StringActor).start
a ! 5 //prints Int!
a ! "test" //prints String!
The only thing to keep in mind is that the order of the receives should not be important, since you won't be able to easily predict which one is first in the chain, though you could solve that by using a mutable hashmap instead of a list.
You can use empty Receive in base actor class and chain receives in their definitions.
Sample for Akka 2.0-M2:
import akka.actor.Actor
import akka.actor.Props
import akka.event.Logging
import akka.actor.ActorSystem
class Logger extends Actor {
val log = Logging(context.system, this)
override def receive = new Receive {
def apply(any: Any) = {}
def isDefinedAt(any: Any) = false
}
}
trait Errors extends Logger {
override def receive = super.receive orElse {
case "error" => log.info("received error")
}
}
trait Warns extends Logger {
override def receive = super.receive orElse {
case "warn" => log.info("received warn")
}
}
object Main extends App {
val system = ActorSystem("mysystem")
val actor = system.actorOf(Props(new Logger with Errors with Warns), name = "logger")
actor ! "error"
actor ! "warn"
}