Scala Akka actors, ask pattern, dead letters encountered while sending reply - scala

I am trying to send a request to remote actor using ask pattern. The local actor recieves some value and it performs some task on it and updates it.
Then when local actor try to send back the updated value to remote actor , error occurs while sending. How should i handle this error?
Error:
[INFO] [03/31/2017 17:28:18.383] [ClientSystem-akka.actor.default-dispatcher-3] [akka://ClientSystem/deadLetters]
Message [check.package$Rcvdcxt] from Actor[akka://ClientSystem/user/localA1#1050660737] to Actor[akka://ClientSystem/deadLetters] was not delivered.
[1] dead letters encountered.
Remote Actor:
class RemoteActor() extends Actor {
def receive = {
case TaskFromLocal() =>{
implicit val timeout: Timeout = 15000
val currentSender = sender
val f1 = currentSender ? RemoteActor.rtree.cxtA
f1.onComplete{
case Success(Rcvdcxt(cxtA))=>
println("Success"+cxtA)
case Success(s) =>
println("Success :"+s)
case Failure(ex) =>
println("failure:"+ex)
}
}
case _ => println("unknown msg")
}
}
object RemoteActor{
def createRndCxtC(count: Int):List[CxtC] = (for (i <- 1 to count) yield CxtC(Random.nextString(5), Random.nextInt())).toList
def createRndCxtB(count: Int): List[CxtB] = (for (i <- 1 to count) yield CxtB(createRndCxtC(count), Random.nextInt())).toList
def createRndCxtA(count: Int): List[CxtA] = (for (i <- 1 to count) yield CxtA(createRndCxtC(count), 5)).toList
var rtree = RCxt(createRndCxtA(1),createRndCxtB(2),1,"")
def main(args: Array[String]) {
val configFile = getClass.getClassLoader.getResource("remote_application.conf").getFile
val config = ConfigFactory.parseFile(new File(configFile))
val system = ActorSystem("RemoteSystem" , config)
val remoteActor = system.actorOf(Props[RemoteActor], name="remote")
println("remote is ready")
}
}
Local Actor :
class LocalActorA extends Actor{
#throws[Exception](classOf[Exception])
val remoteActor = context.actorSelection("akka.tcp://RemoteSystem#127.0.0.1:5150/user/remote")
def receive = {
case TaskLA1(taskA) => {
implicit val timeout: Timeout = 15000
val rCxt = remoteActor ? TaskFromLocal()
val currentSender = sender
rCxt.onComplete{
case Success(Rcvdcxt(cxtA))=>
println("Success"+cxtA)
println("Sender: "+ sender)
currentSender ! Rcvdcxt(cxtA)
case Success(s)=>
println("Got nothing from Remote"+s)
currentSender ! "Failuree"
case Failure(ex) =>
println("Failure in getting remote")
currentSender ! "Failure"
}
}
}
}
object LocalActorA {
def createRndCxtC(count: Int):List[CxtC] = (for (i <- 1 to count) yield CxtC(Random.nextString(5), Random.nextInt())).toList
def createRndCxtB(count: Int): List[CxtB] = (for (i <- 1 to count) yield CxtB(createRndCxtC(count), Random.nextInt())).toList
def createRndCxtA(count: Int): List[CxtA] = (for (i <- 1 to count) yield CxtA(createRndCxtC(count), 3)).toList
var tree = RCxt(createRndCxtA(2),createRndCxtB(2),1,"")
def main(args: Array[String]) {
val configFile = getClass.getClassLoader.getResource("local_application.conf").getFile
val config = ConfigFactory.parseFile(new File(configFile))
val system = ActorSystem("ClientSystem",config)
val localActorA1 = system.actorOf(Props[LocalActorA], name="localA1")
println("LocalActor A tree : "+tree)
localActorA1 ! TaskLA1(new DummySum())
}
}

Since you didn't post all the code I can't really tell exactly the error, but my best guess is related to the fact that you are calling sender in the onComplete in the LocalActor. This is unsafe and should be avoided at all costs. Instead, do something similar with the remote actor:
class LocalActor {
def receive = {
case TaskLA1(taskA) =>
val currentSender = sender
rCxt.onComplete {
case Success(Rcvdcxt(cxtA))=>
currentSender ! Rcvdcxt(cxtA)
...
}
}
}

Related

Run Futures in parallel in an actor

I understand that using for comprehension on Futures we can make them run in parallel if the they are created outside the for block.
For example:
val f1 = Cloud.generateNumber(1)
val f2 = Cloud.generateNumber(2)
val f3 = Cloud.generateNumber(3)
val future = for {
a <- f1
b <- f2
c <- Future.successful(5 + 2)
d <- f3
} yield {
addInts(a,b,c,d)
}
The above code snippet would run f1, f2, and f3 in parallel.
Now consider the following sippets.
driver:
object DriverApp {
def main(args: Array[String]): Unit = {
val system = ActorSystem("MyActorSystem")
val parent = system.actorOf(Parent.props)
implicit val timeout: Timeout = 30.seconds
(parent ? GetMessageRequest()).mapTo[GetMessageResponse].map { resp =>
println("received response in driver")
println(resp.message)
}
Thread.sleep(5000)
system.shutdown()
}
}
parent:
class Parent extends Actor with ActorLogging {
val system = ActorSystem("MyActorSystem")
val child1Actor = system.actorOf(Child1.props, "child1-actor")
val child2Actor = system.actorOf(Child2.props, "child2-actor")
implicit val timeout: Timeout = 5.seconds
override def receive: Receive = {
case getFinalMessageRequest: GetMessageRequest => getFinalMessage(sender, getFinalMessageRequest)
}
private def getFinalMessage(sender: ActorRef, getFinalMessageRequest: GetMessageRequest): Unit = {
val r1 = getMessageFromC1()
val r2 = getMessageFromC2()
for {
i1 <- r1
i2 <- r2
} yield {
println("received response")
println(i1)
println(i2)
sender ! GetMessageResponse((i1 + i2).toString)
}
}
private def getMessageFromC1(): Future[Int] = {
import scala.concurrent.ExecutionContext.Implicits.global
Thread.sleep(Random.nextInt(500))
println("Executing getMessageFromC1")
(child1Actor ? GetMessageRequest()).mapTo[Int].map(v => v)
}
private def getMessageFromC2(): Future[Int] = {
import scala.concurrent.ExecutionContext.Implicits.global
Thread.sleep(Random.nextInt(500))
println("Executing getMessageFromC2")
(child2Actor ? GetMessageRequest()).mapTo[Int].map(v => v)
}
}
child1:
object Child1 {
final val props = Props[Child1]
case class GetMessageRequest2()
}
class Child1 extends Actor with ActorLogging{
override def receive: Receive = {
case getMessageRequest: GetMessageRequest => getFinalMessage(sender, getMessageRequest)
}
private def getFinalMessage(sender: ActorRef, getFinalMessageRequest: GetMessageRequest): Unit = {
println("in child actor 1")
sender ! 5
}
}
child2:
object Child2 {
final val props = Props[Child2]
}
class Child2 extends Actor with ActorLogging {
override def receive: Receive = {
case getFinalMessageRequest: GetMessageRequest => getFinalMessage(sender, getFinalMessageRequest)
}
private def getFinalMessage(sender: ActorRef, getFinalMessageRequest: GetMessageRequest): Unit = {
println("in child actor 2")
sender ! 10
}
}
For the second example the futures are executed sequentially. I've tested the the app quite a few times, on every occasion it results in the following output
Executing getMessageFromC1
in child actor 1
Executing getMessageFromC2
in child actor 2
received response
5
10
received response in driver
15
Is there some restriction that keeps us from running futures in parallel within actors?

Akka - Ask pattern - Combine result

I am designing an actor system. centerActor need to wait result from both actor1 and actor2 to response to the client.
In the centerActor:
def receive: Receive ={
case request => {
implicit val timeout = Timeout(1.seconds)
val ask1 = actor1 ? Update1.mapTo[Int]
val ask2 = actor2 ? Update2.mapTo[String]
val response =
(for(x <- ask1; y <- ask2) yield Done).recover{case _ => FailReply}
response pipeTo sender
}
}
In this design, my sender(client) cannot receive Done message. I don't know why.
So I am thinking about the second design:
def receive: Receive = {
implicit val timeout = Timeout(1.seconds)
case request => {
val ask1 = actor1 ? Update1.mapTo[Int]
val ask2 = actor2 ? Update2.mapTo[String]
val response =
(for(x <- ask1; y <- ask2) yield Done).recover{case _ => FailReply})
response pipeTo self
context.become(waiting(sender))
}
}
def waiting(s: ActorRef) = {
case Done =>
s ! Done
unstashAll()
context.unbecome()
case FailReply => s ! FailReply
case _ => stash()
}

PersistentActor cannot invoke persist handler in Future onComplete

I am pretty new using PersistentActor ,
when I try to call updateState from a future onComplete, fails , nothing happanes , tried to debug it and I do get to the persist call but not into the updateState
trait Event
case class Cmd(data: String)
case class Evt(data: String) extends Event
class BarActor extends PersistentActor{
implicit val system = context.system
implicit val executionContext = system.dispatcher
def updateState(event: Evt): Unit ={
println("Updating state")
state = state.updated(event)
sender() ! state
}
def timeout(implicit ec: ExecutionContext) =
akka.pattern.after(duration = 2 seconds, using = system.scheduler)(Future.failed(new TimeoutException("Got timed out!")))
val receiveCommand: Receive = {
case Cmd(data) =>
def anotherFuture(i: Int)(implicit system: ActorSystem) = {
val realF = Future {
i % 2 match {
case 0 =>
Thread.sleep(100)
case _ =>
Thread.sleep(500)
}
i
}
Future.firstCompletedOf(Seq(realF, timeout))
.recover {
case _ => -1
}
}
val res = (1 to 10).map(anotherFuture(_))
val list = Future.sequence(res)
list.onComplete{
case _ =>
persist(Evt("testing"))(updateState)
}
}
}
You could try this:
list.onComplete {
case _ => self ! Evt("testing")
}
And add this to receiveCommand
case evt: Evt =>
persist(Evt("testing"))(updateStates)

Akka consolidate concurrent database requests

I want to be able to make concurrent requests to multiple data repositories and consolidate the results. I am trying to understand if my approach is at all valid or if there is a better way to approach this problem. I am definitely new to Akka / Spray / Scala and really want to get a better understanding of how to properly build these components. Any suggestions / Tips would be greatly appreciated. Trying to wrap my head around the use of actors and futures for this type of implementation.
Spray Service:
trait DemoService extends HttpService with Actor with ActorLogging {
implicit val timeout = Timeout(5 seconds) // needed for `?` below
val mongoMasterActor = context.actorOf(Props[MongoMasterActor], "redisactor")
val dbMaster = context.actorOf(Props[DbMasterActor], "dbactor")
val messageApiRouting =
path("summary" / Segment / Segment) { (dataset, timeslice) =>
onComplete(getDbResponses(dbMaster, dataset, timeslice)) {
case Success(dbMessageResponse) => complete(s"The result was $dbMessageResponse")
case Failure(ex) => complete(s"An error occurred: ${ex.getMessage}")
}
}
/** Passes the desired actor reference for a specific dataset and timeslice for summary data retrieval
*
* #param mongoActor an actor reference to the RedisActor that will handle the appropriate request routing
* #param dataset The dataset for which the summary has been requested
* #param timeslice The timeslice (Month, Week, Day, etc.) for which the summary has been requested
*/
def getSummary(mongoActor: ActorRef, dataset: String, timeslice: String): Future[DbMessageResponse] = {
log.debug(s"dataset: $dataset timeslice: $timeslice")
val dbMessage = DbMessage("summary", dataset + timeslice)
(mongoActor ? dbMessage).mapTo[DbMessageResponse]
}
def getDbResponses(dbActor: ActorRef, dataset: String, timeslice: String): Future[SummaryResponse] = {
log.debug(s"dataset: $dataset timeslice: $timeslice")
val dbMessage = DbMessage("summary", dataset + timeslice)
(dbActor ? dbMessage).mapTo[SummaryResponse]
}
def getSummaryPayload(mongoSummary: DbMessageResponse, redisSummary: DbMessageResponse): String = {
mongoSummary.response + redisSummary.response
}
}
Akka Actor / Future mock db requests:
class DbMasterActor extends Actor with ActorLogging {
private var originalSender: ActorRef = _
//TODO: Need to add routing to the config to limit instances
val summaryActor = context.actorOf(Props(new SummaryActor), "summaryactor")
def receive = {
case msg: DbMessage => {
this.originalSender = sender
msg.query match {
case "summary" => {
getDbResults().onComplete{
case Success(result) => originalSender ! result
case Failure(ex) => log.error(ex.getMessage)
}
}
}
}
//If not match log an error
case _ => log.error("Received unknown message: {} ")
}
def getDbResults(): Future[SummaryResponse] = {
log.debug("hitting db results")
val mongoResult = Future{ Thread.sleep(500); "Mongo"}
val redisResult = Future{ Thread.sleep(800); "redis"}
for{
mResult <- mongoResult
rResult <- redisResult
} yield SummaryResponse(mResult, rResult)
}
}
Following the reading of Effective Akka by Jamie Allen, I am going to attempt to apply his "Cameo" pattern suggestion.
Slideshare:
http://www.slideshare.net/shinolajla/effective-akka-scalaio
Github:
https://github.com/jamie-allen/effective_akka
I think what I created will work, but doesn't sound like the best approach based on Jamie's comments in his talks. I will update / edit back to this post what I have implemented (or try to).
Summary Actor (Cameo Actor):
object SummaryResponseHandler {
case object DbRetrievalTimeout
def props(mongoDb: ActorRef, redisDb: ActorRef, originalSender: ActorRef): Props = {
Props(new SummaryResponseHandler(mongoDb, redisDb, originalSender))
}
}
class SummaryResponseHandler(mongoDb: ActorRef, redisDb: ActorRef,
originalSender: ActorRef) extends Actor with ActorLogging {
import SummaryResponseHandler._
var mongoSummary, redisSummary: Option[String] = None
def receive = LoggingReceive {
case MongoSummary(summary) =>
log.debug(s"Received mongo summary: $summary")
mongoSummary = summary
collectSummaries
case RedisSummary(summary) =>
log.debug(s"Received redis summary: $summary")
redisSummary = summary
collectSummaries
case DbRetrievalTimeout =>
log.debug("Timeout occurred")
sendResponseAndShutdown(DbRetrievalTimeout)
}
def collectSummaries = (mongoSummary, redisSummary) match {
case (Some(m), Some(r)) =>
log.debug(s"Values received for both databases")
timeoutMessager.cancel
sendResponseAndShutdown(DataSetSummary(mongoSummary, redisSummary))
case _ =>
}
def sendResponseAndShutdown(response: Any) = {
originalSender ! response
log.debug("Stopping context capturing actor")
context.stop(self)
}
import context.dispatcher
val timeoutMessager = context.system.scheduler.scheduleOnce(
250 milliseconds, self, DbRetrievalTimeout)
}
class SummaryRetriever(mongoDb: ActorRef, redisDb: ActorRef) extends Actor with ActorLogging {
def receive = {
case GetSummary(dataSet) =>
log.debug("received dataSet")
val originalSender = sender
val handler = context.actorOf(SummaryResponseHandler.props(mongoDb,redisDb, originalSender), "cameo-message-handler")
mongoDb.tell(GetSummary(dataSet), handler)
redisDb.tell(GetSummary(dataSet), handler)
case _ => log.debug(s"Unknown result $GetSummary(datset)")
}
}
Common:
case class GetSummary(dataSet: String)
case class DataSetSummary(
mongo: Option[String],
redis: Option[String]
)
case class MongoSummary(
summary: Option[String]
)
case class RedisSummary(
summary: Option[String]
)
trait MongoProxy extends Actor with ActorLogging
trait RedisProxy extends Actor with ActorLogging
Mock Stubs:
class MongoProxyStub extends RedisProxy {
val summaryData = Map[String, String](
"dataset1" -> "MongoData1",
"dataset2" -> "MongoData2")
def receive = LoggingReceive {
case GetSummary(dataSet: String) =>
log.debug(s"Received GetSummary for ID: $dataSet")
summaryData.get(dataSet) match {
case Some(data) => sender ! MongoSummary(Some(data))
case None => sender ! MongoSummary(Some(""))
}
}
}
class RedisProxyStub extends MongoProxy{
val summaryData = Map[String, String](
"dataset1" -> "RedisData1",
"dataset2" -> "RedisData2")
def receive = LoggingReceive {
case GetSummary(dataSet: String) =>
log.debug(s"Received GetSummary for ID: $dataSet")
summaryData.get(dataSet) match {
case Some(data) => sender ! RedisSummary(Some(data))
case None => sender ! RedisSummary(Some(""))
}
}
}
Boot (You should use test, but was just wanting to run from boot):
object Boot extends App{
val system = ActorSystem("DbSystem")
val redisProxy = system.actorOf(Props[RedisProxyStub], "cameo-success-mongo")
val mongoProxy = system.actorOf(Props[MongoProxyStub], "cameo-success-redis")
val summaryRetrieverActor = system.actorOf(Props(new SummaryRetriever(redisProxy, mongoProxy)), "cameo-retriever1")
implicit val timeout = Timeout(5 seconds)
val future = summaryRetrieverActor ? GetSummary("dataset1")
val result = Await.result(future, timeout.duration).asInstanceOf[DataSetSummary]
println(Some(result.mongo).x)
println(result.redis)
system.shutdown()
}
Application Config:
akka.loglevel = "DEBUG"
akka.event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
akka.actor.debug.autoreceive = on
akka.actor.debug.lifecycle = on
akka.actor.debug.receive = on
akka.actor.debug.event-stream = on

how can we get the result from various actors and then send the result from all the actors to one response in play 2.2.0?

case object getValues
case class Data(a: Any, b:Any)
class Testing extends Actor {
def receive = {
case "do something" => {
val result2 = { User.getList }
sender ! result2
}
case _ => println("done")
}
}
class Testing2 extends Actor {
def receive = {
case getValues =>
val result2 = { User.getMap }
}yield Data(a,b)
val child = context.actorOf(Props[Testing], "child")//creating actor
implicit val askTimeout = Timeout(1.second)//giving timeout
val r = child ? "do something"
val res = for {
a <- r
} yield (a)
res map (p=>sender ! Data(p, result2))//sending response to the sender
}
}
//Controller
object Application extends Controller {
def testingActor2 = Action.async {
val system = ActorSystem("ActorSystem")
val actor1 = system.actorOf(Props[Testing2], "Testing2")
implicit val askTimeout = Timeout(1.second)
val res = actor1 ? getValues
implicit val formats = DefaultFormats
val result = for {
r1 <- res
} yield r1
result map (r => { println(r); Ok("got result") })
}
i am not able to get the result of both actors to controller. Please tell me how to use actors if i have to take their result and then send with the response to the page .
val f1 = Future(1) //ask first actor
val f2 = Future(2) //ask second actor
Future.sequence(List(f1, f2)).map(list => {
//we got List[Int] here
list
})
One issue I can see with your code, and it might not be the entire problem, is that in Testing2, you are closing over the sender which is mutable and can lead to issues. The reason is that by the time the async map is run on the Future, the sender could have changed to a different ActorRef or could be nullified (switched to the dead letter actor ref) either way you won't get the response back to where you intended for it to go. You can fix that simply by doing the following:
...
val res = for {
a <- r
} yield (a)
val originator = sender
res foreach (p=>originator ! Data(p, result2))//sending response to the sender
Or you could import the pipeTo pattern and do it like this:
import akka.pattern.pipeTo
...
val res = for {
a <- r
} yield (a)
res map (p=> Data(p, result2)) pipeTo sender