How to wait for Akka actor to start during tests? - scala

I have a test that needs to make an assertion on something that happens during an actor's preStart(), but I haven't figured out how to wait until that happens, and sometimes it doesn't happen before the assertion is made (and sometimes it does). I have tried this:
EventFilter.debug(start = "started", occurrences = 1).assertDone(10.seconds)
but I get an error message when using it:
java.lang.AssertionError: assertion failed: 1 messages outstanding on DebugFilter(None,Left(started),false)

You could place the creation of the actor inside an intercept block:
import akka.actor._
import akka.testkit.EventFilter
import com.typesafe.config.ConfigFactory
class MyActor extends Actor with ActorLogging {
override def preStart(): Unit = {
log.debug("started MyActor...")
}
def receive = {
case m => log.debug(s"Received this message: $m")
}
}
object MyActor {
def props() = Props[MyActor]
}
object EventFilterTest extends App {
implicit val system = ActorSystem("testsystem", ConfigFactory.parseString("""
akka.loggers = ["akka.testkit.TestEventListener"]
akka.loglevel = "DEBUG"
"""))
EventFilter.debug(start = "started", occurrences = 1) intercept {
val myActor = system.actorOf(MyActor.props)
myActor ! "cows"
}
}
Running the above code produces the following output:
[DEBUG] [...] [run-main-0] [EventStream(akka://testsystem)] logger log1-TestEventListener started
[DEBUG] [...] [run-main-0] [EventStream(akka://testsystem)] Default Loggers started
[DEBUG] [...] [testsystem-akka.actor.default-dispatcher-5] [akka://testsystem/user/$a] Received this message: cows
The intercept "catches" the debug statement in the actor's preStart hook.

Related

Akka TCP Request Response need Thread.sleep

Good morning , I hava a problem with basic akka IO by TCP
I've a basic implemetation of Client and Server as shown on akka documentation:
Client is https://github.com/akka/akka/blob/v2.5.20/akka-docs/src/test/scala/docs/io/IODocSpec.scala#L67-L103
And Handler is [SimpleEchoHandler] (https://github.com/akka/akka/blob/v2.5.20/akka-docs/src/test/scala/docs/io/EchoServer.scala#L227-L304) but also the other act as the same way.
I've a main test method that stop on first connection to the server:
package core.september
import java.net.InetSocketAddress
import akka.actor.{Actor, ActorLogging, ActorRef, ActorSystem, Props}
import akka.util.ByteString
import com.typesafe.config.ConfigFactory
import core.september.fastchain.network.Client
/**
* #author ${user.name}
*/
object App {
class ClientHandler extends Actor with ActorLogging {
def receive = {
case toLog ⇒ {
log.debug("Client received "+ toLog.toString)
}
//Thread.sleep(200)
}
}
def main(args : Array[String]) {
val config = ConfigFactory.parseString("akka.loglevel = DEBUG")
implicit val system = ActorSystem("EchoServer", config)
var clientHand:ActorRef = system.actorOf(Props(classOf[ClientHandler]))
var address:InetSocketAddress = new InetSocketAddress("localhost",5080)
var ackServer = system.actorOf(Props(classOf[EchoManager], classOf[SimpleEchoHandler],5080), "simple")
var client:ActorRef = system.actorOf(Props(classOf[Client],address,clientHand));
//Thread.sleep(200)
client ! ByteString("echo")
//Thread.sleep(200)
client ! "close"
}
}
If I' don't comment out the two Thread.sleep after each message I can't see the output of the sent message, the output, without sleep is just:
[DEBUG] [02/07/2019 15:47:21.812] [EchoServer-akka.actor.default-dispatcher-4] [akka://EchoServer/system/IO-TCP/selectors/$a/0] Attempting connection to [localhost/127.0.0.1:5080]
[DEBUG] [02/07/2019 15:47:21.816] [EchoServer-akka.actor.default-dispatcher-4] [akka://EchoServer/system/IO-TCP/selectors/$a/0] Connection established to [localhost/127.0.0.1:5080]
[DEBUG] [02/07/2019 15:47:21.825] [EchoServer-akka.actor.default-dispatcher-3] [akka://EchoServer/user/$a] Client received Connected(localhost/127.0.0.1:5080,/127.0.0.1:54616)
I completely loose ByteString message and the "close" message.
My question is why i need to put the main thread in sleep to show also other messages.
With thread.sleep message are correctly logged:
[DEBUG] [02/07/2019 15:53:55.988] [EchoServer-akka.actor.default-dispatcher-5] [akka://EchoServer/system/IO-TCP/selectors/$a/0] Attempting connection to [localhost/127.0.0.1:5080]
[DEBUG] [02/07/2019 15:53:55.999] [EchoServer-akka.actor.default-dispatcher-5] [akka://EchoServer/system/IO-TCP/selectors/$a/0] Connection established to [localhost/127.0.0.1:5080]
[DEBUG] [02/07/2019 15:53:56.011] [EchoServer-akka.actor.default-dispatcher-5] [akka://EchoServer/user/$a] Client received Connected(localhost/127.0.0.1:5080,/127.0.0.1:54712)
[DEBUG] [02/07/2019 15:53:56.157] [EchoServer-akka.actor.default-dispatcher-2] [akka://EchoServer/user/$a] Client received ByteString(101, 99, 104, 111)
[DEBUG] [02/07/2019 15:53:56.374] [EchoServer-akka.actor.default-dispatcher-4] [akka://EchoServer/user/$a] Client received connection closed
ClientActor implementation is:
package core.september.fastchain.network
import akka.actor.{ Actor, ActorRef, Props }
import akka.io.{ IO, Tcp }
import akka.util.ByteString
import java.net.InetSocketAddress
object Client {
def props(remote: InetSocketAddress, replies: ActorRef) =
Props(classOf[Client], remote, replies)
}
class Client(remote: InetSocketAddress, listener: ActorRef) extends Actor {
import Tcp._
import context.system
import akka.io.Tcp
/*if (listener == null) {
listener = Tcp.get(context.system).manager
}*/
IO(Tcp) ! Connect(remote)
def receive = {
case CommandFailed(_: Connect) ⇒
listener ! "connect failed"
context stop self
case c # Connected(remote, local) ⇒
listener ! c
val connection = sender()
connection ! Register(self)
context become {
case data: ByteString ⇒
connection ! Write(data)
case CommandFailed(w: Write) ⇒
// O/S buffer was full
listener ! "write failed"
case Received(data) ⇒
listener ! data
case "close" ⇒
connection ! Close
case _: ConnectionClosed ⇒
listener ! "connection closed"
context stop self
}
}
}
thank you vey much.
You gotta wait for the actor to process your messages before exiting the app.
The easiest way is to use Akka's gracefulStop pattern:
import akka.pattern.gracefulStop
client ! ByteString("echo")
client ! "close"
Await.result(gracefulStop(client, 1 second)(system)

Scala Grpc failed with error io.grpc.StatusRuntimeException: CANCELLED: Failed to read message

I am trying to write a streaming service in Scala using GRPC. Towards this I wrote this proto file
syntax = "proto3";
package com.abhi.grpc;
message TimeRequest{}
message TimeResponse {
int64 currentTime = 1;
}
service Clock {
rpc StreamTime(TimeRequest) returns (stream TimeResponse);
}
This is my server side code
import com.abhi.grpc.clock.{ClockGrpc, TimeRequest, TimeResponse}
import io.grpc.stub.StreamObserver
import monix.execution.Scheduler
import monix.execution.Scheduler.{global => scheduler}
import scala.concurrent.duration._
object ClockGrpcServer extends GrpcServer with App {
val ssd = ClockGrpc.bindService(new ClockGRPC(), Scheduler.global)
runServer(ssd, "Clock")
}
class ClockGRPC extends ClockGrpc.Clock {
override def streamTime(request: TimeRequest, responseObserver: StreamObserver[TimeResponse]): Unit = {
scheduler.scheduleWithFixedDelay(0.seconds, 3.seconds) {
responseObserver.onNext(TimeResponse(System.currentTimeMillis))
}
}
}
and this is my client
object ClockGrpcClient extends App {
val channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext(true).build()
val stub = ClockGrpc.stub(channel)
val observer = new StreamObserver[TimeResponse] {
override def onError(t: Throwable): Unit = println(s"failed with error ${t}")
override def onCompleted(): Unit = println("closing observer")
override def onNext(value: TimeResponse): Unit = println(s"received time ${new DateTime(value)}")
}
stub.streamTime(TimeRequest(), observer)
StdIn.readLine()
}
When I run the server and the client. The server throws the following error as soon as it receives any message from the client
io.grpc.StatusRuntimeException: CANCELLED
at io.grpc.Status.asRuntimeException(Status.java:534)
at io.grpc.stub.ServerCalls$ServerCallStreamObserverImpl.onNext(ServerCalls.java:279)
at com.abhi.ClockGRPC.$anonfun$streamTime$1(ClockGRPC.scala:22)
at monix.execution.internal.RunnableAction.run(RunnableAction.scala:25)
at monix.execution.schedulers.ReferenceScheduler$$anon$1.run(ReferenceScheduler.scala:45)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:140)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
I googled a little and found this article
https://blog.codecentric.de/en/2017/01/hello-grpc-scalapb/
based on this I changed my server to use the java.util scheduler
class ClockGRPC extends ClockGrpc.Clock {
val scheduler = Executors.newSingleThreadScheduledExecutor()
override def streamTime(request: TimeRequest, responseObserver: StreamObserver[TimeResponse]): Unit = {
val tick = new Runnable {
val counter = new AtomicInteger(10)
def run() =
if (counter.getAndDecrement() >= 0) {
val currentTime = System.currentTimeMillis()
responseObserver.onNext(TimeResponse(currentTime))
} else {
scheduler.shutdown()
responseObserver.onCompleted()
}
}
scheduler.scheduleAtFixedRate(tick, 0l, 3000l, TimeUnit.SECONDS)
}
}
But I still get the CANCELLED error. So i cannot get the streaming example to work.
I had almost given up on this problem. But came back today and resolved it.
The problem is with the line
override def onNext(value: TimeResponse): Unit = println(s"received time ${new DateTime(value)}")
value cannot be passed to new DateTime
To further make the matters worse. if the exception occurs in the callback method. Grpc Swallows it and replaces it with a generic error message
info] Running com.abhi.ClockGrpcClient failed with error io.grpc.StatusRuntimeException: CANCELLED: Failed to read message.
My bad luck that he DateTime uses an object as a parameter so the compilation succeeded, but the call failed at runtime and the exception was swallowed by the Grpc.
I am leaving this here so that it helps someone else.
[info] Running com.abhi.ClockGrpcClient failed with error io.grpc.StatusRuntimeException: CANCELLED: Failed to read message
Means the someone went wrong in the callback function.

Akka Testkit does not work when using state variable in stub in other than last test case

I have a problem when using the following stub with Akka Testkit:
import akka.actor.{Props, Status}
import akka.camel.{CamelExtension, CamelMessage, Consumer}
import akka.testkit.{ImplicitSender, TestProbe}
import org.scalatest._
[import own packages]
import scala.concurrent.duration.DurationInt
import scala.language.postfixOps
class ActorSpec extends AkkaTestBase
with ImplicitSender with WordSpecLike
with Matchers with BeforeAndAfterAll with IOSugars {
var stubResponse: String = ""
var doNotRespond = false
val stubEndpoint = "direct:test"
val reportProbe = TestProbe()
val actorUnderTest= TestProbe()
// create stub consumer
val stubConsumer = system.actorOf(Props(new Consumer {
override val endpointUri = stubEndpoint
override def replyTimeout = 1 second
override def receive: Receive = {
case msg: CamelMessage if doNotRespond => ()
case msg: CamelMessage => sender() ! stubResponse;
case _ => sender() ! "ack"
}
}), "stubActor")
[...]
}
When I run the following test cases:
"Respond with failure when camel produces a failure" in {
doNotRespond = true
actorUnderTest ! // Send relevant message
// (which returns the () above because of doNotRespond set to true)
doNotRespond = false // Return to original state
testProbe.expectMsg // Expect relevant message
"Respond with a failure when 'ERROR' status code is returned" in {
stubResponse = readStringFromResource("/failure.xml")
actorUnderTest ! // send relevant message
expectMsgClass(classOf[Status.Failure])
}
Unfortunately this does not work. We get a timeout:
ERROR o.a.c.processor.DefaultErrorHandler - Failed delivery for (MessageId: xxx on ExchangeId: xxx). Exhausted after delivery attempt: 1 caught: java.util.concurrent.TimeoutException: Failed to get response from the actor [ActorEndpointPath(akka://xxx/stubActor] within timeout [1 second].
When the test case that changes the doNotRespond state variable is put as last test case everything does work.
Why is this and how can it be fixed?

How to send message from Supervisor to Actor once Actor restarts?

Requirement?
- There has to be a long running process(daemon) that should run forever
- In case of any exceptions, it should be restarted, but if it fails again twice, no restart efforts should be taken
Problem I face?
- The actor is restarted but no message sent again
What I have?
Main Class
package com.learner.ahka.runforever
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
object RaceEvent extends App {
val config = ConfigFactory.parseString( """
akka.loglevel = "DEBUG"
akka.actor.debug {
receive = on
lifecycle = on
}
""")
val system = ActorSystem.create("race", config)
val coach = system.actorOf(Coach.props(), "coach")
coach ! GetSetGo
}
Supervisor
package com.learner.ahka.runforever
import akka.actor.SupervisorStrategy.{Escalate, Restart}
import akka.actor._
import akka.event.LoggingReceive
import scala.concurrent.duration._
case object GetSetGo
object Coach {
def props(): Props = Props[Coach];
}
class Coach() extends Actor with ActorLogging {
val runner = context.actorOf(Runner.props(new Marathon), "runner")
override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy(maxNrOfRetries = 2, withinTimeRange = 5 seconds) {
case _: RuntimeException => Restart
}
override def receive = LoggingReceive {
case GetSetGo => runner ! GoForIt
}
}
Actor
package com.learner.ahka.runforever
import akka.actor.Status.Failure
import akka.actor.{Actor, ActorLogging, Props}
import akka.event.LoggingReceive
import akka.pattern.pipe
object Runner {
def props(race: Race) = Props(classOf[Runner], race)
}
class Runner(race: Race) extends Actor with ActorLogging {
import context.dispatcher
override def receive: Receive = LoggingReceive {
case GoForIt => race.start pipeTo self
case Failure(throwable) => throw throwable
}
}
Actual work
package com.learner.ahka.runforever
import scala.concurrent.Future
case object GoForIt
trait Race {
def start: Future[Any]
}
class Marathon extends Race {
import scala.concurrent.ExecutionContext.Implicits.global
override def start: Future[Any] = future
val future = Future {
for (i <- 1 to 3) {
println("I am a Marathon Runner!")
Thread.sleep(1000)
}
throw new RuntimeException("MarathonRunner is tired")
}
}
Logs
[DEBUG] [05/30/2015 16:03:35.696] [main] [EventStream(akka://race)] logger log1-Logging$DefaultLogger started
[DEBUG] [05/30/2015 16:03:35.698] [main] [EventStream(akka://race)] Default Loggers started
[DEBUG] [05/30/2015 16:03:35.704] [race-akka.actor.default-dispatcher-4] [akka://race/system] now supervising Actor[akka://race/system/deadLetterListener#-1391310385]
[DEBUG] [05/30/2015 16:03:35.706] [race-akka.actor.default-dispatcher-3] [akka://race/system/deadLetterListener] started (akka.event.DeadLetterListener#191ba186)
[DEBUG] [05/30/2015 16:03:35.710] [race-akka.actor.default-dispatcher-2] [akka://race/user] now supervising Actor[akka://race/user/coach#-1161587711]
I am a Marathon Runner!
[DEBUG] [05/30/2015 16:03:35.722] [race-akka.actor.default-dispatcher-3] [akka://race/user/coach] started (com.learner.ahka.runforever.Coach#66f0f319)
[DEBUG] [05/30/2015 16:03:35.722] [race-akka.actor.default-dispatcher-4] [akka://race/user/coach/runner] started (com.learner.ahka.runforever.Runner#72f67980)
[DEBUG] [05/30/2015 16:03:35.723] [race-akka.actor.default-dispatcher-3] [akka://race/user/coach] now supervising Actor[akka://race/user/coach/runner#755574648]
[DEBUG] [05/30/2015 16:03:35.723] [race-akka.actor.default-dispatcher-3] [akka://race/user/coach] received handled message GetSetGo
[DEBUG] [05/30/2015 16:03:35.725] [race-akka.actor.default-dispatcher-4] [akka://race/user/coach/runner] received handled message GoForIt
I am a Marathon Runner!
I am a Marathon Runner!
[DEBUG] [05/30/2015 16:03:38.739] [race-akka.actor.default-dispatcher-3] [akka://race/user/coach/runner] received handled message Failure(java.lang.RuntimeException: MarathonRunner is tired)
[ERROR] [05/30/2015 16:03:38.752] [race-akka.actor.default-dispatcher-4] [akka://race/user/coach/runner] MarathonRunner is tired
java.lang.RuntimeException: MarathonRunner is tired
at com.learner.ahka.runforever.Marathon$$anonfun$1.apply(Race.scala:22)
at com.learner.ahka.runforever.Marathon$$anonfun$1.apply(Race.scala:17)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
[DEBUG] [05/30/2015 16:03:38.753] [race-akka.actor.default-dispatcher-2] [akka://race/user/coach/runner] restarting
[DEBUG] [05/30/2015 16:03:38.755] [race-akka.actor.default-dispatcher-2] [akka://race/user/coach/runner] restarted
UPDATE
If I don't delegate to Future, everything works as expected, even in case of restart, but when delegated to Future, Future does not get executed in case of actor restart. See here
You could override the postRestart method to send a message back to the parent to notify it of the restart, then watch for that new message type in the parent and respond accordingly. If context.parent won't work happily for that purpose (I tend not to rely on it), then have the Coach actor pass it's self actor reference as a new constructor parameter when instantiating the Runner.
It is not well known, but you can access sender() from the supervisorStrategy. This means that you can easily identify the actor that you are going to restart (sender() will point to the ActorRef that is currently being decided on). This is completely safe.

Reaper pattern in Akka sends its Terminated message to dead letter box

I've been trying the reaper pattern suggested by the Akka team. It does sound good in theory, but the provided unit test does not pass in either sbt or Eclipse's JUnit runner.
import scala.concurrent.duration._
import akka.util.Timeout
import akka.actor._
import akka.testkit.{TestKit, ImplicitSender, TestProbe}
import org.scalatest.{WordSpec, BeforeAndAfterAll}
import org.scalatest.matchers.MustMatchers
import org.scalatest.junit.JUnitRunner
import org.junit.runner.RunWith
#RunWith(classOf[JUnitRunner])
class ReaperSpec extends TestKit(ActorSystem("ReaperSpec"))
with ImplicitSender with WordSpec with BeforeAndAfterAll with MustMatchers
{
import Reaper._
override def afterAll() {
system.shutdown()
}
// Our test reaper. Sends the snooper a message when all the souls have been reaped
private class TestReaper(snooper: ActorRef) extends Reaper {
def allSoulsReaped(): Unit = snooper ! "Dead"
}
"Reaper" should {
"work" in {
// Set up some dummy Actors
val a = TestProbe()
val b = TestProbe()
val c = TestProbe()
val d = TestProbe()
// Build our reaper
val reaper = system.actorOf(Props(new TestReaper(testActor)))
// Watch a couple
reaper ! WatchMe(a.ref)
reaper ! WatchMe(d.ref)
// Stop them
system.stop(a.ref)
system.stop(d.ref)
// Make sure we've been called
within (1 seconds, 5 seconds) {
expectMsg("Dead")
}
}
}
}
After adding logging to the actors, I noticed that the Terminated message ended up in the dead letter box. So the reaper actor never got the Terminated message.
[ERROR] [08/05/2014 11:10:27.244] [ReaperSpec-akka.actor.default-dispatcher-4]
[akka://ReaperSpec/user/$a] Monitored actor [Actor[akka://ReaperSpec/system/testActor2#-1060476766]] terminated (akka.actor.DeathPactException)
[INFO] [08/05/2014 11:10:27.248] [ReaperSpec-akka.actor.default-dispatcher-2]
[akka://ReaperSpec/user/$a] Message [akka.actor.Terminated] from Actor[akka://ReaperSpec/system/testActor5#2029818496] to Actor[akka://ReaperSpec/user/$a#1787786349] was not delivered.
[1] dead letters encountered, no more dead letters will be logged. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
I thought that system.stop(watchedActorRef) would guarantee delivery of a Terminated message to all the actors that are watching the watchedActorRef. What am I missing?