Consider that i have four actors(1,2,3,4) in a same actor system.Each actor can send message only with its neighbors who has not sent a message to it(i.e. 1 can send only to 2 and 4. Also 2 and 4 can only send to 3 because their neighbor 1 has already sent a message). when an actor receives message from both its neighbors, it prints its name and the system stops.I am able to partially implement.But issue here is in the same time two actors gets message from both their neighbors and stops.For example, if i start the process at 1, 1 sends message to 4 and 2,2 to 3 and 4 to 3 so theoretically 3 should get printed but i get 2 and 3 both printed.Please suggest what can be done.Below is my sample logic.
object Main extends App {
//creating a actor system
val actorSystem = ActorSystem("System")
//creating four actor instances with id as 1,2,3,4
for (i <- 1 to 4) {
actorSystem.actorOf(CircularActor.props(4), "" + i)
}
//initiating message to actor 1
actorSystem.actorSelection(s"/user/1") ! "hello from x"
}
class CircularActor(n: Int) extends Actor {
//variable to keep a track whether the actor received two meesages(i.e.from both neighbours)
var noOfMessagesReceived = 0
//generic method to send message using actorPath
def messageNeighbour(path:String){
context.actorSelection(path) ! "hello from x"
}
override def receive: Receive = {
case "hello from x" =>
noOfMessagesReceived += 1
if (noOfMessagesReceived == 2) {
println(s"The actor that received both messages is ${self.path.name}")
context.system.terminate()
}
else {
//Figures out id of sender
val pathValue = sender().path.name
//Gets its own name
val ownId = self.path.name.toInt
//Finds out the previous neighbor
val prev = if (ownId - 1 == 0) n else ownId - 1
//Finds next neighbour
val next = if (ownId == n) 1 else ownId + 1
//If the message is from deadletter, then this is the initiator actor
if (pathValue == "deadLetters") {
messageNeighbour(s"/user/$prev")
messageNeighbour(s"/user/$next")
}
//If the message is from its next neighbour,send it to previous
else if (pathValue.toInt == next) {
//introducing random delay
Thread.sleep(1 + Random.nextInt(100))
messageNeighbour(s"/user/$prev")
}
//If none of above,then send it to previous.
else {
Thread.sleep(1 + Random.nextInt(100))
messageNeighbour(s"/user/$next")
}
}
}
object CircularActor {
def props(n: Int): Props = Props(new CircularActor(n))
}
The problem seems to be that you assume that messages are processed in the order in which they are sent, which is not necessarily the case. Message passing is asynchronous, and the only guarantee is that messages are processed in the order that they arrive for any given actor. The order in which messages are processed by different actors is not defined.
So in your system the messages could be processed in this order
<dead> -> 1
1 -> 2,4
4 -> 3
3 -> 2
2 -> 3
2 -> <terminate>
As you can see, actor 2 processes two messages before any other actor.
It is not clear what can be done because it is not clear what you are trying to achieve. But it is dangerous to build actor systems with loops like this. In general, an actor system should be organised as a tree or DAG of requests, with replies sent to the requesting actor.
To achieve required behavior we need an actor for tracking the actors to which messages are received.
Model:
abstract class Direction
object Left extends Direction
object Right extends Direction
object StartPoint extends Direction
object Process
BookKeeper Actor
class BookKeeperActor extends Actor {
var visitedActorRefs: mutable.HashSet[String] = mutable.HashSet.empty[String]
override def receive: Receive = {
case Process =>
if (visitedActorRefs.contains(sender().path.toString)) {
context.stop(self)
context.system.terminate()
println(s"The actor that received both messages is ${sender().path.toString}")
}
else
visitedActorRefs.add(sender().path.toString)
}
}
Circular Actor:
class CircularActor(n: Int, bookKeeper: ActorRef) extends Actor {
//generic method to send message using actorPath
def messageNeighbour(path: String, direction: Direction) {
context.actorSelection(path) ! ("hello from x", direction)
}
override def receive: Receive = {
case ("hello from x", direction: Direction) =>
bookKeeper ! Process
//Figures out id of sender
val pathValue = sender().path.name
//Gets its own name
val ownId = self.path.name.toInt
//Finds out the previous neighbor
val prev = if (ownId - 1 == 0) n else ownId - 1
//Finds next neighbour
val next = if (ownId == n) 1 else ownId + 1
Thread.sleep(1 + Random.nextInt(100))
direction match {
case StartPoint =>
messageNeighbour(s"/user/$prev", Left)
messageNeighbour(s"/user/$next", Right)
case Left => messageNeighbour(s"/user/$prev", Left)
case Right => messageNeighbour(s"/user/$next", Right)
}
}
}
object CircularActor {
def props(n: Int, bookeeper: ActorRef): Props = Props(new CircularActor(n, bookeeper))
}
Main App-
object Main extends App {
val actorSystem = ActorSystem("System")
//creating four actor instances with id as 1,2,3,4
val bookKeeperActor = actorSystem.actorOf(Props(new BookKeeperActor))
for (i <- 1 to 4) {
val ac = actorSystem.actorOf(CircularActor.props(4, bookKeeperActor), "" + i)
}
//initiating message to actor 1
actorSystem.actorSelection(s"/user/1") ! ("hello from x", StartPoint)
}
Related
I know one of the advantages of the actor model is that by handling only one message at a time, concurrency issues are simplified. But it seems to me that my actor is handling multiple messages. In pseudo code I have
var status = 0
def receive = {
case DoSomething =>
val dest = sender()
status = 0
for {
otherActor <- resolveOtherActor("/user/OtherActor")
} yield {
for {
res <- {status = 1
otherActor ? doSomething1
}
res <- {status = 2
otherActor ? doSomething2
}
} yield {
dest ! status
}
}
case GetStatus => sender() ! status
}
If I send a DoSomething messages to this actor, and then immediately send GetStatus to this actor repeatedly, I will see status 0, 1 and 2 coming back in sequence. If the actor model only handled one message at a time, I would only ever see status 2 being returned, since I wouldn't have access to the intermediate status.
It seems that locks are still necessary with the actor pattern. What am I missing?
All bets are off when you close over an actor's mutable state and expose it to other threads, which is what your code is doing when it mutates status inside a (nested) Future. The Akka documentation clearly warns against this.
An actor does process one message at a time:
var status = 0
def receive = {
case IncrementStatus =>
status += 1
case GetStatus =>
val s = status
sender ! s
}
Sending an IncrementStatus, another IncrementStatus, then a GetStatus message from the same sender to the above actor will cause that sender to receive a 2.
However, trying to do the same thing with Futures does not guarantee the same outcome, because a Future is completed asynchronously. For example:
object NumService {
// calculates arg + 1 in the future
def addOne(arg: Int): Future[Int] = {
Future { arg + 1 }
}
}
class MyActor extends Actor {
var status = 0
def receive = {
case IncrementStatusInFuture =>
val s = status
NumService.addOne(s)
.map(UpdateStatus(_))
.pipeTo(self)
case UpdateStatus(num) =>
status = num
case GetStatus =>
val s = status
sender ! s
}
}
We map the Future to create a Future[UpdateStatus], then pipe to the actor itself the result of that Future.
If we send an IncrementStatusInFuture, another IncrementStatusInFuture, then a GetStatus message to MyActor from the same sender, we cannot guarantee that the sender will receive a 2. The actor processes those three messages in order, but one or both of the calls to NumService.addOne might not have completed by the time the actor processes the GetStatus message. This nondeterministic behavior is a characteristic of a Future; it is not a violation of the actor principle of one-message-at-a-time processing.
I'm trying to keep counting on each successful import. But here is a problem - Counter works if the router receives a message from its parent but if I'm trying to send a message from its children it receives it but doesn't update the global variable that is out of the scope.
I know it sounds complicated. Let me show you the code.
Here is the router
class Watcher(size: Int) extends Actor {
var router = {
val routees = Vector.fill(size) {
val w = context.actorOf(
Props[Worker]
)
context.watch(w)
ActorRefRoutee(w)
}
Router(RoundRobinRoutingLogic(), routees)
}
var sent = 0
override def supervisorStrategy(): SupervisorStrategy = OneForOneStrategy(maxNrOfRetries = 100) {
case _: DocumentNotFoundException => {
Resume
}
case _: Exception => Escalate
}
override def receive: Receive = {
case container: MessageContainer =>
router.route(container, sender)
case Success =>
sent += 1
case GetValue =>
sender ! sent
case Terminated(a) =>
router.removeRoutee(a)
val w = context.actorOf(Props[Worker])
context.watch(w)
router = router.addRoutee(w)
case undef =>
println(s"${this.getClass} received undefinable message: $undef")
}
}
Here is the worker
class Worker() extends Actor with ActorLogging {
var messages = Seq[MessageContainer]()
var received = 0
override def receive: Receive = {
case container: MessageContainer =>
try {
importMessage(container.message, container.repo)
context.parent ! Success
} catch {
case e: Exception =>
throw e
}
case e: Error =>
log.info(s"Error occurred $e")
sender ! e
case undef => println(s"${this.getClass} received undefinable message: $undef")
}
}
So on supervisor ? GetValue I get 0 but suppose to have 1000.The strangest thing is that when I debug it with the breakpoint right on the case Success => ... the value is incremented every time the new message arrives. But supervisor ? GetValue still returns 0.
Let's assume I want to count on case container: MessageContainer => ... and it will magically work; I'll get desirable number, but it doesn't show if I actually imported anything. What's going on?
Here is the test case.
#Test
def testRouter(): Unit = {
val system = ActorSystem("RouterTestSystem")
// val serv = AddressFromURIString("akka.tcp://master#host:1334")
val supervisor = system.actorOf(Props(new Watcher(20)))//.withDeploy(akka.actor.Deploy(scope = RemoteScope(serv))))
val repo = coreSession.getRepositoryName
val containers = (0 until num)
.map(_ => MessageContainer(MessageFactory.generate("/"), repo))
val watch = Stopwatch.createStarted()
(0 until num).par
.foreach( i => {
supervisor ! containers.apply(i)
})
implicit val timeout = Timeout(60 seconds)
val future = supervisor ? GetValue
val result = Await.result(future, timeout.duration).asInstanceOf[Int]
val speed = result / (watch.elapsed(TimeUnit.MILLISECONDS) / 1000.0)
println(f"Import speed: $speed%.2f")
assertEquals(num, result)
}
Can you please explained it in details. Why is it happening? Why only on message received from the children? Another approach?
Well... there can be many potential problems hidden in the parts of code that you have not shared. But, for the sake of this discussion I will assume that everything else is fine and we will just discuss problems with your shared code.
Now, let me explain a bit about Actors. To put things simply, every actor has a mailbox (where it keeps messages in the sequence they were received) and processes them one by one in the order they were received. Since the mailbox is used like a Queue we will refer to it as a Queue in this discussion.
Also... I don't know what this container.apply(i) is going to return... so I will refer to the return value of that container.apply(1) as MessageContainer__1
In your test runner you are first creating an instance of Watcher,
val supervisor = system.actorOf(Props(new Watcher(20)))
Now, lets say that you are sending these 2 messages (num = 2) to supervisor,
So supervisor's mailbox will look something like,
Queue(MessageContainer__0, MessageContainer__1)
Then you send it another message GetValue so the mailbox will look like,
Queue(MessageContainer__0, MessageContainer__1, GetValue)
Now the actor will process the first message and pass it to the workers, the mail-box will look like,
Queue(MessageContainer__1, GetValue)
Now even if your worker is ultra-fast and instantaneous in sending the reply the mailbox will look like,
Queue(MessageContainer__1, GetValue, Success)
And now since your worker super-ultra-fast and instantaneously replies with a Success, the state after passing the second MessageContainer will look like,
Queue(GetValue, Success, Success)
And... here is the root of your problem. The Supervisor sees the GetValue massage before any Success messages, no matter how fast your workers are.
And thus it will process GetValue and reply with current value of sent which is 0.
I'm trying to make a small Akka example work using the following code:
class Sender extends Actor {
#volatile var numEchoes = 0
def receive = {
case Echo => {
numEchoes += 1
println(numEchoes)
if(numEchoes < Main.NUMBER_OF_ECHOES) {
println("Heard an echo...")
}
else {
// all echoes have been received.
println("All echoes heard.")
context.system.terminate
}
}
}
}
This actor is going to receive Echo messages from several Receiver actors:
class Receiver extends Actor {
def receive = {
case Echo => sender ! Echo
}
}
However, numEchoes always prints out 1. If I was doing something wrong, I'd expect 0, but this value makes me believe something else is at play here, but I'm unable to figure out what it is.
The rest of the code is just sending this specific actor a certain number of messages (lets assume 10). I see Heard an echo... printed out 10 times, but the value of this variable does not change.
#wheaties was correct. The error was in the Main object, due to some late and careless copy pasting... When I was creating 1 Sender and 10 Receiver actors, this is the code I was using:
// Create the actor that will send out the messages
val sender = actorSystem.actorOf(Props[Sender], "sender")
var actorList = List[ActorRef]()
for(i <- 1 to NUMBER_OF_ECHOES) {
val receiver = actorSystem.actorOf(Props[Sender], "receiver"+i)
actorList ::= receiver
}
Of course, creating the receivers as Senders is completely wrong, so all I had to do was fix the typo and correctly spawn the actors:
// Create the actor that will send out the messages
val sender = actorSystem.actorOf(Props[Sender], "sender")
var actorList = List[ActorRef]()
for(i <- 1 to NUMBER_OF_ECHOES) {
val receiver = actorSystem.actorOf(Props[Receiver], "receiver"+i)
actorList ::= receiver
}
Actor B contains reference to actor A called senderr. Actor A asks actor B, waits the response and prints it. However it does not receive response. Why? It must print number 4 at the console but it doesn't.
class A(b: ActorRef) extends Actor {
private implicit val timeout = Timeout(20 seconds)
b ! 1
def receive = {
case 2 => (b ? 3).map(println)
}
}
class B extends Actor {
var senderr : ActorRef = null
def receive = {
case 1 =>
senderr = sender()
sender ! 2
case 3 =>
senderr ! 4
}
}
object Main extends App {
val system = ActorSystem("test")
val b = system.actorOf(Props[B])
val a = system.actorOf(Props(classOf[A], b))
}
ask basically creates a temporary micro-actor with the single purpose of receiving one response of type Any
When B receives 3 it sends 4 to senderr which refers to A. However A does not have a match for 4. In order for (b ? 3).map(println) to receive the 4, B has to send it to sender(), which at that time refers to the temporary actor set up by ask:
case 3 => sender ! 4
Alternatively, you could have a case 4 in A but then you wouldn't even need to use ask
I would like to drop messages from an actor's mailbox if it becomes too full. For example, if the queue size reaches 1000 messages, the oldest one should be deleted.
You cannot work with the mailbox directly, but you can implement Message Expiration pattern on top of the existing library.
Send a creation date with every message:
case class ExpirableMessage(msg: String, createdAt: Long)
Scan the mailbox with reactWithin(0), and filter out expired messages:
react{
case msg: ExpirableMessage =>
// handle the message
// clean the mailbox with nested react
reactWithin(0){
case ExpirableMessage(_, createdAt) if(currentTimeMillis - createdAt > INTERVAL) =>
case TIMEOUT =>
}
}
You can also reify an actor's queue on the heap and throttle its utilization by using a proxy actor. Then you can write something like the following:
// adder actor with a bounded queue size of 4
val adder = boundActor(4) {
loop {
react {
case x: Int => reply(x*2)
}
}
}
// test the adder
actor {
for (i <- 1 to 10) {
adder !! (i, { case answer: Int => println("Computed " + i + " -> " + answer) })
}
}
Here is the implementation of boundedActor. Note that a boundedActor must always reply to its sender, otherwise there is no way to track its queue size, and the boundedActor will freeze refusing to accept any further messages.
object ActorProxy extends scala.App {
import scala.actors._
import scala.actors.Actor._
import scala.collection.mutable._
/**
* Accepts an actor and a message queue size, and
* returns a proxy that drops messages if the queue
* size of the target actor exceeds the given queue size.
*/
def boundActorQueue(target: Actor, maxQueueLength: Int) = actor {
val queue = new Queue[Tuple2[Any, OutputChannel[Any]]]
var lastMessageSender: Option[OutputChannel[Any]] = None
def replyHandler(response: Any) {
if (lastMessageSender.get != null) lastMessageSender.get ! response
if (queue.isEmpty) {
lastMessageSender = None
} else {
val (message, messageSender) = queue.dequeue
forwardMessage(message, messageSender)
}
}
def forwardMessage(message: Any, messageSender: OutputChannel[Any]) = {
lastMessageSender = Some(messageSender)
target !! (message, { case response => replyHandler(response) })
}
loop {
react {
case message =>
if (lastMessageSender == None) {
forwardMessage(message, sender)
} else {
queue.enqueue((message, sender))
// Restrict the queue size
if (queue.length > maxQueueLength) {
val dropped = queue.dequeue
println("!!!!!!!! Dropped message " + dropped._1)
}
}
}
}
}
// Helper method
def boundActor(maxQueueLength: Int)(body: => Unit): Actor = boundActorQueue(actor(body), maxQueueLength)
}