scala - How to propagate error in future to parent actor -
i try understand failure handling akka
, futures
. example have parent , child actors.
child actor have 2 failure cases:
case 1) error happens while message processing
case 2) error happens inside future
i need propagate error parent in both cases, in second case it's not happens. what's doing wrong?
import akka.actor.supervisorstrategy.{decider, stop} import akka.actor.{actor, actorref, actorsystem, oneforonestrategy, props, supervisorstrategy} import akka.testkit.{testkit, testprobe} import org.junit.{after, before, test} import scala.concurrent.future import scala.util.{failure, success} class parent(_case: string, probe: actorref) extends actor { val child = context.actorof(props(new child(_case)), "mylittlechild") final val defaultstrategy: supervisorstrategy = { def defaultdecider: decider = { case ex: exception => probe ! ex stop } oneforonestrategy()(defaultdecider) } override def supervisorstrategy: supervisorstrategy = defaultstrategy override def receive: receive = { case msg => unhandled(msg) } } class child(_case: string) extends actor { implicit val ec = context.dispatcher override def prestart(): unit = { self ! _case } override def receive: receive = { case "case1" => throw new runtimeexception("fail") case "case2" => future[string] { throw new runtimeexception("fail") }.oncomplete { case success(s) => println(s) case failure(e) => throw e } case msg => unhandled(msg) } } class testexample { protected implicit var system: actorsystem = _ @before def setup(): unit = { system = actorsystem.create("test") } @after def teardown(): unit = { testkit.shutdownactorsystem(system) } @test def case1(): unit = { val testprobe = testprobe() system.actorof(props(new parent("case1", testprobe.ref))) testprobe expectmsgclass classof[runtimeexception] } @test def case2(): unit = { val testprobe = testprobe() system.actorof(props(new parent("case2", testprobe.ref))) testprobe expectmsgclass classof[runtimeexception] } }
to test pass, send exception actor , rethrow exception outside oncomplete
callback:
override def receive: receive = { case "case1" => throw new runtimeexception("fail") case "case2" => future[string] { throw new runtimeexception("fail") }.oncomplete { case success(s) => println(s) case failure(e) => self ! e } case e: runtimeexception => throw e case msg => unhandled(msg) }
however, if must use future
in actor (e.g., third-party library methods return future
), there better ways handle exceptions. example, database api mentioned in comment (databaseapi.load(): future[rows]
), parent actor send loaddb
message child, , child send rows
or error message parent. child's behavior following:
def receive = { case loaddb => val s = sender // capture sender databaseapi .load .oncomplete { case success(rows) => s ! rows case failure(e) => s ! dbfailure(e) } case ... }
an important note make local copy of sender
reference when child receives loaddb
message in order have reference correct sender inside oncomplete
callback. if called sender
inside callback, give erroneous results because sender
have changed time callback executed, explained here. (unlike sender
, self
immutable, it's safe use self
inside oncomplete
.)
Comments
Post a Comment