Update state in actor from within a future - scala

Consider the following code sample:
class MyActor (httpClient: HttpClient) {
var canSendMore = true
override def receive: Receive = {
case PayloadA(name: String) => send(urlA)
case PayloadB(name: String) => send(urlB)
def send(url: String){
if (canSendMore)
httpClient.post(url).map(response => canSendMore = response.canSendMore)
else {
Thread.sleep(5000) //this will be done in a more elegant way, it's just for the example.
httpClient.post(url).map(response => canSendMore = response.canSendMore)
}
}
}
Each message handling will result in an async http request. (post return value is a Future[Response])
My problem is that I want to safely update counter ( At the moment there is a race condition)
BTW, I must somehow update counter in the same thread, or at least before any other message is processed by this actor.
Is this possible?

You can use become + stash combination to keep on stashing messages when the http request future is in process.
object FreeToProcess
case PayloadA(name: String)
class MyActor (httpClient: HttpClient) extends Actor with Stash {
def canProcessReceive: Receive = {
case PayloadA(name: String) => {
// become an actor which just stashes messages
context.become(canNotProcessReceive, discardOld = false)
httpClient.post(urlA).onComplete({
case Success(x) => {
// Use your result
self ! FreeToProcess
}
case Failure(e) => {
// Use your failure
self ! FreeToProcess
}
})
}
}
def canNotProcessReceive: Receive = {
case CanProcess => {
// replay stash to mailbox
unstashAll()
// start processing messages
context.unbecome()
}
case msg => {
stash()
}
}
}

Related

how to watch akka actor and capture its context in the Terminated message receive

I want to supervise child actors using context.watch(childActor) API
I see that when the child actor has an uncaught exception, indeed the receive message "Terminated" is called on the parent.
But since I am creating many children actors, each with different contexts and messages, I don't know which particular child actor (and surrounding context) actually failed.
For example:
class ParentActor extends Actor {
override def receive = {
case "delegateWorkToChildrenActors" => {
(0 to 100) foreach {
i =>
val child = context.actorOf(Props(new ChildActor(i, "child-actor")
context.watch(child)
val message = SomeSortOfComplexMessageWithManyParameters(...)
val res = child ? message
res.onComplete {
x: Try[Any] => x match {
case Failure(exception: Throwable) => // can only get timeout exception here
case Success(value) => // continue as usual
}
}
}
}
case m # Terminated(actor) => {
// a child actor was terminated but what is its context ? i.e. which message did it try to handle ?
}
}
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 1, withinTimeRange = 1 minute) {
case m # _ => {
Stop // Assume that the actor was stopped or crashed etc.
}
}
}
class ChildActor(number : Int) extends Actor {
override def receive: Receive = {
case SomeSortOfComplexMessageWithManyParameters => {
// might of got some sort of exception here...
// the parent which is monitoring it received the "Terminate" message
}
}
}
How can I get the message that the child actor worked upon?

How to work with Source.Queue in Akka-Stream

I am toying around trying to use a source.queue from an Actor. I am stuck in parttern match the result of an offer operation
class MarcReaderActor(file: File, sourceQueue: SourceQueueWithComplete[Record]) extends Actor {
val inStream = file.newInputStream
val reader = new MarcStreamReader(inStream)
override def receive: Receive = {
case Process => {
if (reader.hasNext()) {
val record = reader.next()
pipe(sourceQueue.offer(record)) to self
}
}
case f:Future[QueueOfferResult] =>
}
}
}
I don't know how to check if it was Enqueued or Dropped or Failure
if i write f:Future[QueueOfferResult.Enqueued] the compile complain
Since you use pipeTo, you do no need to match on futures - the contents of the future will be sent to the actor when this future is completed, not the future itself. Do this:
override def receive: Receive = {
case Process =>
if (reader.hasNext()) {
val record = reader.next()
pipe(sourceQueue.offer(record)) to self
}
case r: QueueOfferResult =>
r match {
case QueueOfferResult.Enqueued => // element has been consumed
case QueueOfferResult.Dropped => // element has been ignored because of backpressure
case QueueOfferResult.QueueClosed => // the queue upstream has terminated
case QueueOfferResult.Failure(e) => // the queue upstream has failed with an exception
}
case Status.Failure(e) => // future has failed, e.g. because of invalid usage of `offer()`
}

Apache Spark Receiver Scheduling

I have implemented a receiver that is supposed to connect to a WebSocket stream and get the messages for processing. Here is the implementation that I have done so far:
class WebSocketReader (wsConfig: WebSocketConfig, stringMessageHandler: String => Option[String],
storageLevel: StorageLevel) extends Receiver[String] (storageLevel) {
// TODO: avoid using a var
private var wsClient: WebSocketClient = _
def sendRequest(isRequest: Boolean, msgCount: Int) = {
while (isRequest) {
wsClient.send(msgCount.toString)
Thread.sleep(1000)
}
}
// TODO: avoid using Synchronization...
private def connect(): Unit = {
Try {
wsClient = createWsClient
} match {
case Success(_) =>
wsClient.connect().map {
case result if result.isSuccess =>
sendRequest(true, 10)
case _ =>
connect()
}
case Failure(ex) =>
// TODO: how to signal a failure so that it is tried the next time....
ex.printStackTrace()
}
}
def onStart(): Unit = {
new Thread(getClass.getSimpleName) {
override def run() { connect() }
}.start()
}
override def onStop(): Unit =
if (wsClient != null) wsClient.disconnect()
private def createWsClient = {
new DefaultHookupClient(new HookupClientConfig(new URI(wsConfig.wsUrl))) {
override def receive: Receive = {
case Disconnected(_) =>
// TODO: use Logging framework, try reconnecting....
println(s"the web socket is disconnected")
case TextMessage(message) =>
stringMessageHandler(message).foreach(store)
case JsonMessage(jsValue) =>
stringMessageHandler(jsValue.toString).foreach(store)
}
}
}
}
How is this Receiver being run? Does this Receiver run on the worker nodes or on the driver node? Is this way of sleeping a thread a correct approach?
The reason why I want to do this is that the server that is exposing the WebSocket end point would need a count on the messages that I want to receive. Say if I ask the server for 100 messages, it would give me 100 messages and so on. So I need a way to periodically schedule this request to the server. Currently, I'm using the Thread.sleep mechanism. Is this advisable? What could be the alternative?

Accessing states of an Akka Actor through messages

I'm trying to access the state of a particular actor through messaging. What I don't know is how I can retrieve the state of the actor. Here, I need to access the state variable of Node, state1. I want to avoid using promises/futures in the code. How should I go about modifying this code if I want to do that?
class Node extends Actor {
val state1:Int = 4
def receive = {
case getState => {
sender ! ??? //How should I send the 'state1' to the sender?
}
}
}
class Master extends Actor {
def recieve = {
case someCase(node_actor:ActorRef) => {
// My aim here is to get the "state1" from node actor into var 's'
var s:Int = node_actor ! getState
}
}
}
Actors are designed perfectly to avoid manual handling of scala.concurrent. things.
Just separate request and response handling into different receive cases:
class Node extends Actor {
import Node._
val state1: Int = 4
def receive: Receive = {
case getState =>
sender ! State(state1)
}
}
class Master extends Actor {
import Master._
def receive: Receive = {
case Action(node) =>
// My aim here is to get the "state1" from node actor into var 's'
node ! Node.GetState
case Node.State(state) =>
// do something with state
}
}
object Master {
case class Action(node: ActorRef)
}
object Node {
case object GetState
case class State(state: Int)
}
Sometimes you could have also some intermediate values calculated and don't want to do something until you'll get the answer but being unreachable also is not acceptable. So you could just wait for node response in separate Receive while stashing incoming messages, like this:
class Master extends Actor with Stash {
import Master._
def receive: Receive = {
case Action(node) =>
val intermediate = scala.util.Random.nextLong()
node ! Node.GetState
context.become(waitingForState(calc = intermediate), discardOld = false)
}
def waitingForState(calc: Long): Receive = {
case Node.State(state) =>
// do something with state and calc
context.unbecome()
unstashAll()
case _ => stash()
}
}

Akka persistentChannel does not delete message from Journal upon confirm

I am writing a piece of code that uses PersistentChannel to send a message to an actor that does some IO. Upon completion it confirms the ConfirmablePersistent message.
The document says that upon confirmation the message shall be deleted in a PersistentChannel. But in my case my files stays in the journal with out getting deleted.
My requirement is that as soon as I get a successful result for the IO or the deadline has exceeded the persisted message should be deleted from the journal.
class IOWorker(config: Config, ref: ActorRef)
extends Actor with ActorLogging {
import IOWorker._
val channel = context.actorOf(PersistentChannel.props(
PersistentChannelSettings(redeliverInterval = 1.minute,
pendingConfirmationsMax = 1,pendingConfirmationsMin = 0)))
val doIOActor = context.actorOf(DOIOActor(config))
def receive = {
case payload # (msg, deadline)=>
channel ! Deliver(Persistent(payload), doIOActor.path)
}
}
object DOIOActor {
def apply(config: Config) = Props(classOf[DOIOActor], config)
}
class DOIOActor(config: Config) extends Actor
with ActorLogging {
def receive = {
case p # ConfirmablePersistent(payload, sequenceNr, redeliveries) =>
payload match {
case (msg, deadline: Deadline) =>
deadline.hasTimeLeft match {
case false => p.confirm()
case true =>
sender ! SAVED(msg)
Try{DOIO}
match
{
case Success(v) =>
sender ! SUCCESS(msg)
p.confirm()
case Failure(doioException) =>
log.warning(s"Could not complete DOIO. $doioException")
throw doioException
}
}
}
}
def DOIO(ftpClient: FTPClient, destination: String, file: AISData) = {
SOMEIOTASK match {
case true => log.info(s"Storing file to $destination.")
case false =>
throw new Exception(s"Could not DOIO to destination $destination")
}
}
}
Deletions are performed asynchronously by most journal implementations, as discussed on the mailing list.