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.
Related
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()`
}
I have an actor with some contexts, i.e.
def step2: Receive = {
case _ => log error "Unhandled message"
}
def step1: Receive = {
case _ => log error "Unhandled message"
}
Is there a way to know on which state the actor currently is (step1, step2)?
(I could stock the value on a string but I wonder if there is a better way.)
If it not possible, I would like to understand why since this state should be kept somewhere.
FSM
You can use FSM. stateName gives the name of the state.
Ping the Actor with some special message handled in every state to send the stateName.
sealed trait ExampleState
case object State1 extends ExampleState
case object State2 extends ExampleState
case object C extends ExampleState
import akka.actor.{Actor, FSM}
import akka.event.EventHandler
import akka.util.duration._
case object Move
class ABC extends Actor with FSM[ExampleState, Unit] {
import FSM._
startWith(State1, Unit)
when(State1) {
case Ev(Move) =>
EventHandler.info(this, "Go to B and move on after 5 seconds")
goto(state2) forMax (5 seconds)
}
when(State2) {
case Ev(StateTimeout) =>
EventHandler.info(this, "Moving to C")
goto(C)
}
when(C) {
case Ev(Move) =>
EventHandler.info(this, "Stopping")
stop
}
initialize // this checks validity of the initial state and sets up timeout if needed
}
Hacky Solution
Akka Actor does not store any specific information about the PartialFunction. So I don't think there will be a akka lib function readily available for this.
Have state inside the actor and then update the state when Actor tries to become something.
class FooBar extends Actor with ActorLogging {
var state: Option[String] = Some("receive")
override def receive: Receive = {
case _ => context become state1()
}
def state1: () => Receive = {
() => {
state = Some("state1")
{
case _ => log error "ignore"
}
}
}
def state2: () => Receive = {
() => {
state = Some("state2")
{
case _ => log error "ignore"
}
}
}
}
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()
}
}
}
I am following this tutorial here is my code
case class ArtGroupDeleteFromES (uuidList:List[String])
class ArtGroupDeleteESActor extends Actor{
val log = LoggerFactory.getLogger(this.getClass)
override def preStart() {
log.debug("preStart Starting ArtGroupDeleteESActor instance hashcode # {}",
this.hashCode())
}
override def postStop() {
log.debug("postStop Stopping ArtGroupDeleteESActor instance hashcode # {}",
this.hashCode())
}
override def preRestart(reason: Throwable, message: Option[Any]) {
log.debug("I am restarting")
log.debug("ArtGroupDeleteESActor: preRestart")
log.debug(s" MESSAGE: ${message.getOrElse("")}")
log.debug(s" REASON: ${reason.getMessage}")
super.preRestart(reason, message)
}
override def postRestart(reason: Throwable) {
log.debug("restart completed!")
log.debug("ArtGroupDeleteESActor: postRestart")
log.debug(s" REASON: ${reason.getMessage}")
super.postRestart(reason)
}
def receive = {
case ArtGroupDeleteFromES(uuidList) =>
throw new Exception("Booom")
sender ! true
}
case message =>
log.warn("Received unknown message: {}", message)
unhandled(message)
}
}
and here is the how i am sending this actor a message
class ArtGroupDeletionActor extends Actor{
val log = LoggerFactory.getLogger(this.getClass)
override val supervisorStrategy = OneForOneStrategy(
maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case _:Exception => Restart
}
val artGroupDeleteESActor=context.actorOf(Props[ArtGroupDeleteESActor]
.withDispatcher("akka.actor.ArtGroupDeleteESActor-dispatcher")
,name = "ArtGroupDeleteESActor")
def receive = {
case DeleteArtGroup(uuidList) =>
val future1 = ask(artGroupDeleteESActor, ArtGroupDeleteFromES(uuidList)).mapTo[Boolean]
var isDeletedfromES = Await.result(future1, timeout.duration)
case message =>
log.warn("Unhandled message received : {}", message)
unhandled(message)
}
}
object test extends App{
val artGroupDeletionActor=system.actorOf(Props[ArtGroupDeletionActor]
.withDispatcher("akka.actor.ArtGroupDeletionActor-dispatcher")
,name = "ArtGroupDeletionActor")
artGroupDeletionActor ! DeleteArtGroup(List("123"))
}
the PostRestart() and preRestart() methods are not invoking,but preStart() and postStop() gets called, please guide me where i am doing wrong
(for simplicity I'll call your actors Parent and Child from now on)
What happens here is that when an exception occurs inside Child.receive, it doesn't send a response to Parent, instead, the actor system sends some control instruction for the supervision strategy. However, Parent is blocked on Await waiting for completion of future1, which only happens after the timeout exceeds, and then, in turn, a TimeoutException is thrown inside Parent.receive, killing (restarting) the Parent actor itself, and thus the supervising message of an exception in Child is then passed to deadLetters, never restarting the Child.
You should never, ever, ever block inside an actor, so this is incorrect:
val future1 = ask(artGroupDeleteESActor, ArtGroupDeleteFromES(uuidList)).mapTo[Boolean]
var isDeletedfromES = Await.result(future1, timeout.duration)
Instead, you have to either utilize some kind of message identification to distinguish one reply from another in concurrent environment, or add an onComplete to the Future and send a message to self in the closure (beware: no logic other than sending a message should be executed inside the closure to the Future!).
So, option A:
case class ArtGroupDeleteFromES(id: Long, uuidList: List[String])
case class ArtGroupDeleteFromESResult(id: Long, success: Boolean)
class Parent extends Actor {
override val supervisionStrategy = ...
var msgId = 0L
var pendingRequesters = Map.empty[Long, ActorRef]
val child = context.actorOf(Props[Child])
def nextId = {
msgId += 1
msgId
}
def receive = {
case DeleteArtGroup(uuidList) =>
val id = nextId
pendingRequesters += id -> sender() // store a reference to the sender so that you can send it a message when everything completes
child ! DeleteArtGroupFromES(nextId, uuidList)
case ArtGroupDeleteFromESResult(id, success) =>
// process result...
pendingRequesters(id) ! "done"
pendingRequesters -= id
}
}
And option B:
case class ArtGroupDeleteFromES(uuidList: List[String])
case class ArtGroupDeleteFromESResult(replyTo: ActorRef, success: Boolean)
class Parent extends Actor {
override val supervisionStrategy = ...
val child = context.actorOf(Props[Child])
def receive = {
case DeleteArtGroup(uuidList) =>
val requester = sender() // when the future completes, sender may have already changed, so you need to remember it
(child ? DeleteArtGroupFromES(uuidList)).onComplete {
case Success(success) => self ! ArtGroupDeleteFromESResult(requester, success)
case Failure(e) =>
log.warn("Could not delete...", e)
self ! ArtGroupDeleteFromESResult(requester, success = false)
}
}
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()
}
}