unable handle exception from future failure - scala

I want the following code to return a custom message when one of the method, callfuture1() or callfuture2(), throws an exception. My understanding was if either of the future fails, f would be a failed future.
However, when callfuture1 throws an exception. f.onFailure is not executed. Instead I see the call stack stopped at line of code in callFuture1() where exception occurred and a standard internalError is returned. Why does that happen?
val f = for {
x <- callfuture1()
y <- callfuture2()
} yield y
f.onFailure {
//send an internalserver error with some custom message
}
f.map {
//send data back
}
====update====
i see from the responses, that potential issue is that Exception is being thrown outside the Future and hence my code fails to catch that failed future.
So i changed the code such that Exception only occurs inside the future. I still am not able to explain the behavior i am seeing. (I wonder if it has to anything to do with Play framework.)
def controllerfunction(id: String) = Action.async{
val f = for{
x <- callfuture1(id)
y <- callfuture2(x)
} yield y
y.onFailure{case t =>
println("This gets printed");
Ok("shit happened, but i am still ok")}
y.map{resp:String => Ok(resp)}
}
def callfuture1(id: String):Future[Obj1] = {
for {
val1 <- callfuture1.1(id)
val2 <- callfuture1.2(val1)
} yield val2
}
def callfuture1.2:Future[Obj3] = Future{
thrown new Exception("TEST ME");
}
def callfuture 1.1:Future[Obj4] = {...}
def callfuture2: Future[String] = {....}
Expectation.
The method callfuture1.2 throws an exception inside the future, so my expectation is onFailure should be executed, (which does get executed), and the response returned should "Shit happened, but i am still ok"
Actuality
The play framework returns InternalServerError and i see error stack on my console. I see the printlin("This gets printed") is getting executed.
Cant understand what is happening. Any insights?
==== update 2 =====
I verified that the issue only happens when called inside controller of play framework ( i am using play 2.5). As a standalone scala program everthing works as expected. I believe play error handling catches the unhandaled exception and prints the stack trace. I think this should only be happening in development environment.

This can happen if callfuture1 throws "outside of a future".
Your for comprehension is desugared into this:
val f = callfuture1.flatMap{ x =>
callfuture2.map{ y =>
y
}
}
If callfuture2 throws right away (as opposed to returning a failed future), you will still end up with a failed future because callfuture2 is called inside Future.flatMap, which catches exceptions and turns them into failed futures (same for Future.map).
The situation is different for callfuture1: if it throws right away, there is no enclosing Future.map or Future.flatMap to turn it into a failed future.
In general you should try to avoid having a method that returns a Future and can also throw an error.
This means that if callfuture1 does anything that can throw, it should catch that and turn the exception in a failed future that you then return.
UPDATE: Concerning your update about how you expected "Shit happened, but i am still ok" to be returned:
As already hinted by Dima in a comment, Future.onFailure can only be used for side effects. Futures are immutable. If you want to recover from a failed exception, there is no way to modify the original (failed) future and all you can actually do is transform it into a new future.
Have a look at Future.recover. It does exactly what you need, namely it allows to transform an input future by matching the failed result (if any) and transforming it into a successful future. It is the equivalent of a catch clause, but for futures. Concretely what you really meant to do is something like this:
def controllerfunction(id: String) = Action.async{
val f = for{
x <- callfuture1(id)
y <- callfuture2(x)
} yield y
f.map{ resp: String =>
Ok(resp)
}.recover{
case t: Throwable =>
println("This gets printed");
Ok("shit happened, but i am still ok")
}
}

It seems that inside callfuture1() you're not wrapping all your process inside the Future constructor like
def callfuture1(): Future[?] = Future {
val x = ...
x
}
but your code seems to be
def callfuture1(): Future[?] = {
val x = ... // some error happen here
Future(x)
}
so because it's outside the future, your error is throwing directly into your program code

Related

Is there a way to chain `Try` as a monad while also not catching exceptions raised along the chain?

The Scala Try construct together with its flatMap does not work as I would expect or want it to. The TL;DR is that I want to do a series of operations that can fail in two ways: either by raising an exception, which should be promoted and caught higher up in the call stack, or by returning Failure, as the failure must logically be handled in different parts of the program.
I would expect something like this to do the trick:
def firstStepSucceeds(): Try[Int] = Try {
1
}
def secondStepThrows(input: Int) = {
throw new Exception("Exception thrown in second step")
}
// I expect this to propagate the exception thrown in secondStepThrows
firstStepSucceeds() flatMap (secondStepThrows _)
(Full Scastie with example)
However, in this case, the flatMap() call actually implicitly catches the uncaught exception thrown by secondStepThrows, which is not what I want (which is why I left out the Try block). Is there a way to get the same behaviour without the implicit exception-catching?
What happens in a Try should stay in a Try. Most Scala programmers would be very surprised if a function returning a Try also sometimes threw an exception.
The typical pattern if you want to handle exceptions in different places is to differentiate by the type of the exception. So
val partiallyRecoveredTry = originalTry.recover{
case _: SecondStepException => "second step had an exception"
}
// Further up the call stack
partiallyRecoveredTry.getOrElse("first step had an exception")
Try.flatMap() did not caught exceptions implicitely, it is the essence of Try.
When you use it, it is very explicit, and that's the goal.
I don't really understand what you want, but is something like that is possible for you ?
try {
val first = firstStepSucceeds()
val second = first.map(secondStepThrows).get
val third = secondStepFails(second)
// ...
}
catch {
case e: Exception => ???
}
I did some further experimentation, and what I ended up with was this reimplementation of Try as (the now right-biased and hence monadic) Either:
object CatchAll {
def apply[SomeType](block: => SomeType) = try { Right(block) }
catch { case e: Throwable => Left(e) }
}
def firstStepSucceeds() = CatchAll {
1
}
def firstStepFails() = CatchAll {
throw new Exception("First step failed")
}
def secondStepSucceeds(input: Int) = CatchAll {
input + 1
}
def secondStepFails(input: Int) = CatchAll {
throw new Exception("Second step failed in try block!")
}
def secondStepThrows(input: Int) = {
throw new Exception("Second step failed unexpectedly!")
}
firstStepSucceeds() flatMap (secondStepSucceeds _)
firstStepFails() flatMap (secondStepSucceeds _)
firstStepSucceeds() flatMap (secondStepFails _)
// This now throws an exception as expected
//firstStepSucceeds() flatMap (secondStepThrows _)

Exception not getting handled in recover method

This method throws exception
#throws(classOf[QuestionNotFoundException])
def getQuestionFromQuestionID(questionKey: PracticeQuestionKeys) = {
logger.trace(s"getting question with keys ${questionKey}")
val practiceQuestionFuture: Future[Option[PracticeQuestion]] = findOne(questionKey)
for (questionOption <- practiceQuestionFuture) yield {
questionOption.fold(throw QuestionNotFoundException())(question => {
logger.trace("got question " + question)
question
})
}
}
I use it like follows
def function1 = {
...
val res = for{existingQuestion <- questionsRepository.getQuestionFromQuestionID(questionKey){..}
res.recover {
case exception => {
logger.trace(s"exception ${exception}")
Ok(Json.toJson(JsonResultError(messagesApi("error.answerAdditionFail")(langs.availables(0))+". "+exception.getMessage())))
}
...
}
I want to test that function1 handles the thrown exception. I have written the following test case
"function" should {
"should return error if the question id is not correct" in {
...
when(answerTestEnv.mockPracticeQuestionsRepository.getQuestionFromQuestionID(ArgumentMatchers.any[PracticeQuestionKeys])).thenThrow(
QuestionNotFoundException()
)
val response:Accumulator[ByteString,Result] = controller.function1(request)
//note that run is how accumulator gets the data and starts processing it. no parameter means no data
val resultOfAccumulatorRun:Future[Result] = response.run(ByteString(body))
val responseBody = contentAsJson(resultOfAccumulatorRun)(Timeout(Duration(5000,"millis")),TestEnvImplicits.mat)
println(s"received response ${responseBody}")
val result = (responseBody \ "result").get.as[String]
val additionalInfo = (responseBody \ "additional-info").get.as[String]
result mustBe "error"
additionalInfo mustBe components.messagesApi("error.answerAdditionFail")(components.langs.availables(0))+". "+QuestionNotFoundException().msg
}
}
But when I run my test case, the Exception gets thrown but it seems that res.recover doesn't handle it. I see this in the console.
Question not found
utilities.QuestionNotFoundException: Question not found
at controllers.AnswerController.$anonfun$newAnswer$1(AnswerController.scala:428)
Why doestres.recover` doesn't handle it.
the partial function passed to recover() gets invoked on a failed Future, but your Future isn't a failed Future, it is a Future that contains an exception as its 'successful' result type (actually it will be forced to be something more like Future[AnyRef], as it could be either an Exception or a PracticeQuestion).
You probably don't want to be using Option.fold() like that. Instead try:
practiceQuestionFuture.flatMap { qOpt =>
qOpt.map(q => Future(q))
.getOrElse(Future.failed(QuestionNotFoundException()))
}
(NB: I'm not near a compiler to try this out right now, so I may have made a syntactical mistake or two, but there should be enough there to get you going).

How to correctly do error handling that involves asynchronous methods

Suppose I have the following methods defined in some service used in my Play application each of which executes some command, on failure, updates database and sends some notification asynchronously.
def thisMightFail(): Future[SomeResult]
def thisUpdatesStatus(): Future[Unit]
def thisSendsNotification(): Future[Unit]
thisUpdatesStatus and thisSendsNotification are independent of each other and are called for error handling like the below. (No further execution is possible on failure)
for {
_ <- Future{ Some process }
result <- thisMightFail() transform {
case Success(v) => v
case Failure(cause) =>
val f = Future.sequence(List(
thisUpdatesStatus(),
thisSendsNotification()
))
Failure(new Exception("Command execution failed", cause))
}
_ <- Future{ Another process that uses "result" }
:
} yield ...
My question is should the f be waited before returning Failure(cause) or is there some better way to handle the error in this kind of situation?
My question is should the f be waited before returning Failure(cause)
or is there some better way to handle the error in this kind of
situation
Currently your code does not handle failure of thisUpdatesStatus() and thisSendsNotification(). are you ok with the failure of these functions call ? I suspect it will not be ok if thisMightFail() fails and you do not update the database. Also user should receive some notification if thisMightFail() fails.
You should use a workflow platform for such cases such as cadence . You can find a nice introduction of cadence here.

How to return early in a pattern match of akka actor receive

Tried googling variations on this trivial question but didn't get an answer...
Basically I have a pattern match in my receive method.
In some cases I want to break early from the receive handling
override def receive = {
case blah => {
... preflight code
if (preflight failed) {
sender() ! errorMSG
"break" or "return" here // get error "method receive has a return statement ; needs result type -
// I tried adding Unit to the receive and return statements
}
... more code
....
if (something happened) {
sender() ! anotherErrorMSG
"break" or "return" here
}
...
}
case foo => {...}
case bar => {...}
} // end receive
See this discussion of return's semantics and remember that receive returns a PartialFunction[Any, Unit] which is then evaluated after receive has returned. In short, there's no way to return early.
Ă–mer Erden's solution of throwing an exception and using actor supervision works (indeed, exception throwing with all of its overhead is basically the only way to reliably end a computation early), but if you need any state to carry over from message to message, you'll need Akka persistence.
If you don't want to nest if-elses as in chunjef's solution, you can use context.become and stash to create some spaghetti-ish code.
But the best solution may be to have the things that might fail be their own functions with Either result types. Note that the Either API in scala 2.12 is quite a bit nicer than in previous versions.
import scala.util.{ Either, Left, Right }
type ErrorMsg = ...
type PreflightSuccess = ... // contains anything created in preflight that you need later
type MoreCodeSuccess = ... // contains anything created in preflight or morecode that you need later
def preflight(...): Either[ErrorMsg, PreFlightSuccess] = {
... // preflight
if (preflight failed)
Left(errorMsg)
else
Right(...) // create a PreflightSuccess
}
def moreCode1(pfs: PreFlightSuccess): Either[ErrorMsg, MoreCodeSuccess] = {
... // more code
if (something happened)
Left(anotherErrorMSG)
else
Right(...) // create a MoreCodeSuccess
}
def moreCode2(mcs: MoreCodeSuccess): Either[ErrorMsg, Any] = {
... // more code, presumably never fails
Right(...)
}
override def receive = {
case blah =>
val pf = preflight(...)
val result = pf.map(morecode1).joinRight.map(moreCode2).joinRight // only calls morecode1 if preflight succeeded, and only calls morecode2 if preflight and morecode1 succeeded
result.fold(
{ errorMsg => sender ! errorMsg },
()
)
case foo => ...
case bar => ...
}
Whether this is preferable to nested if-else's is a question of taste...
This may not be your question's exact answer but in your case adding supervisor actor would be the better solution. In Akka Supervision model convince you to handle exceptions on supervisor actor instead of sending error messages back to the sender.
This approach brings you a fault-tolerant model and also you can throw exception at any line you want(which solves your current problem), your supervisor actor will handle the throwable with restarting, resuming or stopping the child actor.
please check the link

convert Future[MyType] into Future[Option[MyType]]

I have a Future[MyType] which is produced by an api call across the wire. If the api call fails because the wrong data was sent it just throws an Exception (not very helpful but nothing I can do about it.
So I want to check with a try / catch and return a Future[Option[MyType]] like this:
def myfunc(name:String):Future[Option[MyType]] = {
try {
val d:Future[MyType] = apiCall(name) //returns a Future[MyType]
???? Convert d into future(Some(MyType))
} catch {
future(None)
}
}
Thanks for taking a look
This really depends on where exactly the exception is being thrown. If apiCall is throwing an exception before it creates the Future, you could map the Future to wrap the value in an Option:
try {
val d: Future[Option[MyType]] = apiCall(name).map{result => Some(result)}
} catch {
case t: Throwable => Future.successful(None)
}
However I don't think this is the best way to go about this, since you are effectively losing information as to why the api call failed. Option is generally not a great solution for indicating an error has occurred. I think a better solution would be to return a future containing the exception:
try {
val d:Future[MyType] = apiCall(name)
} catch {
case t: Throwable => Future.failed(t)
}
The reason for this is whatever code you have handling the future is going to have to handle exceptions inside the future already, so this will keep all your error handling in one place.
If the exception is being thrown inside the Future, wrapping apiCall in a try/catch block won't help since the exception is not thrown synchronously, so you can use map and recover:
val d: Future[Option[MyType]] = apiCall(name).map{Some(_)}.recover{
case t: Throwable => None
}