Answer ask in AkkaTestKit - scala

I'm trying to test my actor logic with AkkaTestKit. The problem is that my actor uses ask pattern. So I need to answer somehow. It look as this:
case class AskExecution(id: Long)
override def receive: Receive = {
case id : Long =>
implicit val dispatcher = context.dispatcher
implicit val timeout = Timeout(10 seconds)
val executor = sender
//How to answer this?
val f = executor ? AskExecution(id) map(v => v.asInstanceOf[Option[Long]])
f.onComplete{
case Success(k) =>
case Failure(_) =>
}
}
In test I use it as follows:
val ca = TestActorRef(new TheActor())
ca ! 0L //I send 0, Now I want to answer the ask
//How to do so?

To make your code easier to test, give your actor a reference to the executor actor (the actor that handles the AskExecution message).
import akka.pattern.pipe
class TheActor(executor: ActorRef) extends Actor {
def receive = {
case id: Long =>
val s = sender
implicit val dispatcher = context.dispatcher
implicit val timeout = 10.seconds
(executor ? AskExecution(id)).mapTo[Option[Long]].pipeTo(s)
}
class Executor extends Actor {
def receive = {
case AskExecution(id) =>
// do something to get a result
val result: Option[Long] = ???
sender ! result
}
}
To test, assuming your test class extends TestKit and mixes in the ImplicitSender trait:
val executor = system.actorOf(Props[Executor])
val theActor = system.actorOf(Props(classOf[TheActor], executor))
within(10.seconds) {
theActor ! 0L
expectMsgClass(classOf[Option[Long]])
}
// test the executor directly
within(10.seconds) {
executor ! AskExecution(3L)
expectMsgClass(classOf[Option[Long]])
}

Related

Is there a way to send values from actor to actorRef in ActorSystem

I have two actors coded as follows.
class Actor1 extends Actor {
val r : ActorRef = actorOf (Props[Actor2], "master")
def receive: Receive = {
case class Mul (a,b) =>
r ! CalcMul (a,b)
case class MulReply (ans) =>
println("Multiply result : " + ans)
// want to send "ans" value to testObject..
}
}
class Actor2 extends Actor {
def receive: Receive = {
case class CalcMul (a,b) =>
sender ! MulReply (a*b)
}
}
object testObject extends App {
val a = ActorSystem("ActorSystem").actorOf(Props[Actor1], "root")
a ! CalcMul (5, 15)
// how to receive "ans" value here?
}
I am able to receive and print the result in Actor1 but need those values in testObject so I can use them for future operations. Cannot have a receive method in testObject as done to receive a message in Actor1 from Actor2, so cannot send them with tell method.
As you want to receive a response from an actor you can use ask pattern for this purpose.
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern._
import akka.util.Timeout
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.duration.SECONDS
case class CalsMul(a: Int, b: Int)
class Actor1 extends Actor {
val r: ActorRef = context.actorOf(Props[Actor2], "master")
def receive: Receive = {
case req: CalsMul =>
println("received message by Actor1")
r forward req
}
}
class Actor2 extends Actor {
def receive: Receive = {
case request: CalsMul =>
println("received message by Actor2")
Future.successful(request.a * request.b) pipeTo sender
}
}
object testObject extends App {
implicit val system: ActorSystem = ActorSystem("ActorSystem")
val a: ActorRef = system.actorOf(Props[Actor1], "root")
implicit val timeout: Timeout = Timeout(20, SECONDS)
println(system, "sending message to Actor1")
val ans: Future[Int] = (a ? CalsMul(5, 15)).mapTo[Int] // as you are returning the multiplication of a*b
ans.foreach(println)
}
Note: CPU bound operations are not advised to use with actors it can have adverse effect on the performance of your application

Why is my Actor created 2 times

I wrote this code
class TestActor extends Actor {
override def preStart(): Unit = {
println("going to start my test actor")
}
override def postStop(): Unit = {
println("came inside stop")
}
def receive = {
case msg: TestMessage => sender ! s"Hello ${msg.name}"
}
}
object TestActor {
val props = Props(new TestActor)
case class TestMessage(name: String)
}
I call it using this client code
object MyApp extends App {
val ac = ActorSystem("TestActorSystem")
val a = new ClassA(ac).sayHello()
val b = new ClassB(ac).sayHello()
for {
msg1 <- a
msg2 <- b
} {
println(msg1)
println(msg1)
}
Await.result(ac.terminate(), Duration.Inf)
}
class ClassA(ac: ActorSystem) {
def sayHello(): Future[String] = {
implicit val timeout = Timeout(5 seconds)
val actor = ac.actorOf(TestActor.props)
val msg = actor ? TestActor.TestMessage("foo")
msg.map(_.asInstanceOf[String])
}
}
class ClassB(ac: ActorSystem) {
def sayHello() : Future[String] = {
implicit val timeout = Timeout(5 seconds)
val actor = ac.actorOf(TestActor.props)
val msg = actor ? TestActor.TestMessage("bar")
msg.map(_.asInstanceOf[String])
}
}
I see the output
going to start my test actor
going to start my test actor
Hello foo
Hello foo
came inside stop
came inside stop
My question is that in the companion object I had created the props object as a val and therefore there was only 1 val and that 1 val had 1 instance of new TestActor
In the client both classes used the same instance of actor system. Therefore there should have been only 1 actor and both messages from classA and ClassB should have gone to the same actor.
But it seems that both classes got their own Actor instances.
My question is that in the companion object I had created the props object as a val and therefore there was only 1 val and that 1 val had 1 instance of new TestActor
Not really. Yes, you define one val, but that val is a Props. The Props class is essentially a recipe for creating an actor. What you're defining is a single immutable recipe for creating a TestActor. This recipe can be used multiple times, which is what you're doing when you call ac.actorOf(TestActor.props) twice. Both of these calls use the same Props recipe to create a new TestActor; that is, you use the same recipe to create two TestActor instances.
To reuse a single TestActor, do what #mfirry suggested and create this actor outside of ClassA and ClassB. Here's one way to do that:
object MyApp extends App {
val ac = ActorSystem("TestActorSystem")
val testActor = ac.actorOf(TestActor.props)
val a = new ClassA(ac).sayHello(testActor)
val b = new ClassB(ac).sayHello(testActor)
for {
msg1 <- a
msg2 <- b
} {
println(msg1)
println(msg1)
}
Await.result(ac.terminate(), Duration.Inf)
}
class ClassA(ac: ActorSystem) {
def sayHello(actor: ActorRef): Future[String] = {
implicit val timeout = Timeout(5 seconds)
val msg = actor ? TestActor.TestMessage("foo")
msg.map(_.asInstanceOf[String])
}
}
class ClassB(ac: ActorSystem) {
def sayHello(actor: ActorRef): Future[String] = {
implicit val timeout = Timeout(5 seconds)
val msg = actor ? TestActor.TestMessage("bar")
msg.map(_.asInstanceOf[String])
}
}
This is caused by actorOf will create a new actor, so for twice actorOf it will create 2 TestActor. you can use actorSelection to avoid the second creation, like:
class ClassA(ac: ActorSystem) {
def sayHello(): Future[String] = {
implicit val timeout = Timeout(5 seconds)
val actor = ac.actorOf(Props[TestActor], "test")
println(actor.path)
val msg = actor ? TestMessage("foo")
msg.map(_.asInstanceOf[String])
}
}
class ClassB(ac: ActorSystem) {
def sayHello() : Future[String] = {
implicit val timeout = Timeout(5 seconds)
val actor = ac.actorSelection("akka://TestActorSystem/user/test")
val msg = actor ? TestMessage("bar")
msg.map(_.asInstanceOf[String])
}
}

Do i need to create ActorMaterializer multiple times?

I have an app that generate reports, with akka-http + akka-actors + akka-camel + akka-streams. When a post request arrives , the ActiveMqProducerActor enqueue the request into ActiveMq Broker. Then the ActiveMqConsumerActor consumes the message and start the task using akka-streams(in this actor i need the materializer) .
The main class create the ActorSystem and the ActorMaterializer, but i dont know how is the correct way to "inject" the materializer into the akka-actor
object ReportGeneratorApplication extends App {
implicit val system: ActorSystem = ActorSystem()
implicit val executor = system.dispatcher
implicit val materializer = ActorMaterializer()
val camelExtension: Camel = CamelExtension(system);
val amqc: ActiveMQComponent = ActiveMQComponent.activeMQComponent(env.getString("jms.url"))
amqc.setUsePooledConnection(true)
amqc.setAsyncConsumer(true)
amqc.setTrustAllPackages(true)
amqc.setConcurrentConsumers(1)
camelExtension.context.addComponent("jms", amqc);
val jmsProducer: ActorRef = system.actorOf(Props[ActiveMQProducerActor])
//Is this the correct way to pass the materializer?
val jmsConsumer: ActorRef = system.actorOf(Props(new ActiveMQConsumerActor()(materializer)), name = "jmsConsumer")
val endpoint: ReportEndpoint = new ReportEndpoint(jmsProducer);
Http().bindAndHandle(endpoint.routes, "localhost", 8881)
}
The ReportEndPoint class, that have the jmsProducerActor . Mongo is a trait with CRUD methods. JsonSupport(==SprayJsonSupport)
class ReportEndpoint(jmsProducer: ActorRef)
(implicit val system:ActorSystem,
implicit val executor: ExecutionContext,
implicit val materializer : ActorMaterializer)
extends JsonSupport with Mongo {
val routes =
pathPrefix("reports"){
post {
path("generate"){
entity(as[DataRequest]) { request =>
val id = java.util.UUID.randomUUID.toString
// **Enqueue the request into ActiveMq**
jmsProducer ! request
val future: Future[Seq[Completed]] = insertReport(request)
complete {
future.map[ToResponseMarshallable](r => r.head match {
case r : Completed => println(r); s"Reporte Generado con id $id"
case _ => HttpResponse(StatusCodes.InternalServerError, entity = "Error al generar reporte")
})
}
}
}
} ....
The idea of ActiveMqConsumerActor, is send the messages, with streams and backpressure, one by one,because ReportBuilderActor makes many mongo operations (and the datacenter it`s not very good).
//Is this the correct way to pass the materializer??
class ActiveMQConsumerActor (implicit materializer : ActorMaterializer) extends Consumer with Base {
override def endpointUri: String = env.getString("jms.queue")
val log = Logging(context.system, this)
val reportActor: ActorRef = context.actorOf(Props(new ReportBuilderActor()(materializer)), name = "reportActor")
override def receive: Receive = {
case msg: CamelMessage => msg.body match {
case data: DataRequest => {
//I need only one task running
Source.single(data).buffer(1, OverflowStrategy.backpressure).to(Sink.foreach(d => reportActor ! d)).run()
}
case _ => log.info("Invalid")
}
case _ => UnhandledMessage
}
}
Is a good idea have implicit values in companion objects?
Thanks!!

Does Akka cache actor invocation?

I have the following code that I need to run around 100 times:
val system = ActorSystem("mySystem")
val myActorObject = system.actorOf(Props[MyActorClass], name = "myactor")
implicit val timeout = Timeout(60 seconds)
val future = myActorObject ? Request1
val result = Await.result(future, timeout.duration)
Question is: assuming the first two statements can be called just once, should I cache these variables or Akka does it?
Do not make the actor system creation and actor creation part of repetitive code. Once the actor is created. Using the ActorRef can be done as many times as possible.
val system = ActorSystem("mySystem")
val myActorObject = system.actorOf(Props[MyActorClass], name = "myactor")
Something like this
val futureList = for(_ <- 1 to 1000) yield {
implicit val timeout = Timeout(60 seconds)
val future = myActorObject ? Request1
}
val finalF = Future.sequence(futureList)
val result = Await.result(future, timeout.duration)
In play! (+2.5.x) you can get the system by injection. e.g.
#Singleton
class MyController #Inject() (system: ActorSystem) extends Controller {
val myActor = system.actorOf(MyActorClass.props, "myactor")
//...
}
Then you could have an end point to call the actor as many times as you want, for example
def sayHello(name: String) = Action.async {
(myActor ? name).map { message => Ok(message) }
}
The code inside your actor could be something like this
class MyActorClass extends Actor {
def receive = {
case name: String => sender() ! s"hello $name"
}
}
object MyActorClass {
def props: Props = Props(classOf[MyActorClass ])
}

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