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.
Related
Hi have below typesafe config in file application-typed.conf.
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = "DEBUG"
logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
actor {
provider = "local"
}
}
custom-thread-pool {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
fixed-pool-size = 40
}
throughput = 2
}
Below is the akka-typed actor code.
import akka.actor.typed.{ActorSystem, Behavior, DispatcherSelector, PostStop, Signal}
import akka.actor.typed.scaladsl.AbstractBehavior
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import com.typesafe.config.ConfigFactory
import scala.concurrent.ExecutionContext
trait PrintMessage
case class PrintMessageAny(x: Any) extends PrintMessage
object PrintMeActor {
def apply(): Behavior[PrintMessage] =
Behaviors.setup[PrintMessage](context => new PrintMeActor(context))
}
class PrintMeActor(context: ActorContext[PrintMessage]) extends AbstractBehavior[PrintMessage](context) {
val dispatcherSelector: DispatcherSelector = DispatcherSelector.fromConfig("custom-thread-pool")
implicit val executionContext: ExecutionContext = context.system.dispatchers.lookup(dispatcherSelector)
println(s"PrintMeActor Application started in Thread ${Thread.currentThread().getName}")
override def onMessage(msg: PrintMessage): Behavior[PrintMessage] = {
// No need to handle any messages
println(s"Got $msg in Thread ${Thread.currentThread().getName}")
Behaviors.same
}
override def onSignal: PartialFunction[Signal, Behavior[PrintMessage]] = {
case PostStop =>
context.log.info("PrintMeActor Application stopped")
this
}
}
object TestTypedActorApp extends App {
val config = ConfigFactory.load("application-typed.conf")
val as: ActorSystem[PrintMessage] = ActorSystem(PrintMeActor(), "PrintAnyTypeMessage", config)
as.tell(PrintMessageAny("test"))
Thread.sleep(2000)
}
When I run the code, I get the below output.
PrintMeActor Application started in Thread PrintAnyTypeMessage-akka.actor.default-dispatcher-6
Got PrintMessageAny(test) in Thread PrintAnyTypeMessage-akka.actor.default-dispatcher-6
I want this actor to run on the custom-thread-pool but it is not happening. How can I achieve the same?
You associate the dispatcher with the actor when you spawn it, by passing an akka.actor.typed.DispatcherSelector (which extends akka.actor.typed.Props) corresponding to the desired dispatcher.
When spawning the ActorSystem on a custom dispatcher, one can only pass Props through the overloads that take either a Config or an ActorSystemSetup.
If wanting to override the actor for the user guardian actor (the actor with the behavior you passed into the ActorSystem), it may make more sense to make that dispatcher the default dispatcher:
akka.actor.default-dispatcher {
executor = "thread-pool-executor"
thread-pool-executor {
fixed-pool-size = 40
}
throughput = 2
}
I'm trying to supervise an Akka Actor, more specifically a Cluster Singleton created using ClusterSingletonManager. I'm trying to achieve more control over exceptions, logs and Actor's life cycle.
Unfortunately, after implementing a solution, I made a Singleton Actor throw exceptions, but nothing was show in the logs, nor the Actor or Cluster was shutdown.
My implementation is as follows:
object SingletonSupervisor {
case class CreateSingleton(p: Props, name: String)
}
class SingletonSupervisor extends Actor with ActorLogging {
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 0, withinTimeRange = 1.minute) {
case x: ActorInitializationException =>
log.error(s"Actor=<${x.getActor}> trowed an exception=<${x.getCause}> with message=<${x.getMessage}>")
Stop
case x: ActorKilledException => Stop
case x: DeathPactException => Stop
case x: Exception =>
log.error(s"Some actor threw an exception=<${x.getCause}> with message=<${x.getMessage}>, trace=<${x.getStackTrace}>")
Escalate
}
def receive = {
case CreateSingleton(p: Props, name: String) =>
sender() ! context.actorOf(p)
context.actorOf(ClusterSingletonManager.props(
singletonProps = p,
terminationMessage = PoisonPill,
settings = ClusterSingletonManagerSettings(context.system)),
name = name)
}
}
So, is it even possible to supervisor a Cluster Singlegon? If possible, how should I attack this problem?
One possible solution is creating supervisor actor that spawns given child with given supervisorStrategy and forwards messages to its child.
Here is supervisor actor:
class SupervisorActor(childProps: Props, override val supervisorStrategy) extends Actor {
val child = context.actorOf(childProps, "supervised-child")
def receive: Receive = {
case msg => child forward msg
}
}
and here is how you create supervised actor as cluster singleton
context.actorOf(ClusterSingletonManager.props(
singletonProps = Props(classOf[SupervisorActor], p, supervisorStrategy),
terminationMessage = PoisonPill,
settings = ClusterSingletonManagerSettings(context.system)),
name = name)
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)
}
}
}
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))
}
}
}
I'd like to know if there are any mechanism in Akka that can have an actor executed periodically?
You don't really need an actor to do this in Akka 1.3.1 you can schedule a function to be called every 5 minutes like this:
Scheduler.schedule(() => println("Do something"), 0L, 5L, TimeUnit.MINUTES)
However, if you do want it to be an actor for other reasons you would call it like this
case class Message()
val actor = actorOf(new Actor {
def receive = {
case Message() => println("Do something in actor")
}
}).start()
Scheduler.schedule(actor, Message(), 0L, 5L, TimeUnit.MINUTES)
If you're using Akka 2.0 then it would be done like this
val system = ActorSystem("MySystem")
system.scheduler.schedule(0 seconds, 5 minutes)(println("do something"))
Or send a message to an actor every 5 minutes like this
case class Message()
class MyActor extends Actor {
def receive = { case Message() => println("Do something in actor") }
}
val system = ActorSystem("MySystem")
val actor = system.actorOf(Props(new MyActor), name = "actor")
system.scheduler.schedule(0 seconds, 5 minutes, actor, Message())
The approach using schedule is one good approach, although there is a potential for the messages to queue up if the work done on schedule is so great that it might take longer than the scheduled interval. If you want the interval to occur between the end of one iteration and the beginning of the next, then use scheduleOnce with the following pattern:
import akka.actor.Actor
import scala.concurrent.duration._
class SchedulingActor extends Actor {
override def preStart(): Unit = {
self ! "Do Some Work"
}
def receive = {
case "Do Some Work" =>
doWork
context.system.scheduler.scheduleOnce(5 minutes, self, "Do Some Work")
}
def doWork = ???
}
More complete Java example:
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import scala.concurrent.duration.FiniteDuration;
import java.util.concurrent.TimeUnit;
public class AnActor extends AbstractActor {
private final FiniteDuration SCHEDULED_WORK_DELAY = new FiniteDuration(5, TimeUnit.MINUTES);
#Override
public void preStart() {
getSelf().tell("Do Scheduled Work", ActorRef.noSender());
}
#Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("Do Scheduled Work", work -> {
doScheduledWork();
context().system().scheduler().scheduleOnce(SCHEDULED_WORK_DELAY, getSelf(),
"Do Scheduled Work", context().dispatcher(), ActorRef.noSender());
})
.build();
}
private void doScheduledWork() { ... }
}
If anyone want java code then they can do like this
Cancellable cancellable = system.scheduler().schedule(Duration.Zero(), Duration.create(5, TimeUnit.MINUTES), cronActor, "tick", system.dispatcher(), null);