I am wondering how can I immediately terminate the for comprehension without calling Future.failed and yield the Try[Failure] result instead.
Example:
for {
resultOne <- Future { Successful("Data") }
resultTwo <- Future {
// Something went wrong here...
// Instead of calling Future.failed();
// I want to terminate this for-comprehension and return a
// Failure() object and bypass the next line
}
resultThree <- Future {
// Something went wrong here...
// Instead of calling Future.failed();
// I want to terminate this for-comprehension and return a
// Failure() object and bypass the next line
}
resultFour <- Future {
// ... Some database call which retuns a Try[]
}
} yield resultFour
Another example
You're right in saying that my example is not running in parallel. I guess I don't have other options but to make it like this?
for {
resultOne <- Future { Successful("Data") }
resultFour <- {
// ... Some async function which returns a Try
}
.flatMap {
case Success(resultTwo) => // ... Some async function which returns a Try
case Failure(ex) => Future.success(Failure(ex))
}
.flatMap {
case Success(resultTwo) => // ... Some database call which retuns a Try[]
case Failure(ex) => Future.success(Failure(ex))
}
}
} yield resultFour
What you have there has this fail-fast behaviour but is not running in parallel! You need to create the Futures outside the comprehension in order to run them in parallel.
There is no comprehension behind the scenes, it is syntactic sugar over map/flatMap. So it is not a "thing" you can cancel as a whole.
I assume what you want is to stop other futures if one fails (e.g. to save resources or return as soon as anything fails). Scala's Futures don't support this. Monix is a scala library that provides CanceleableFuture that supports exactly this use case.
Related
I'm trying to use a for comprehension to both run some futures in order and merged results, but also kick off a separate thread after one of those futures completes and not care about the result (basically used to fire some logging info)
I've played around a bit with this with some thread sleeps and it looks like whatever i'm throwing inside the for block will end up blocking the thread.
private def testFunction(): EitherT[Future, Error, Response] =
for {
firstRes <- EitherT(client.getFirst())
secondRes <- EitherT(client.getSecond())
// Future i want to run on a separate async thread outside the comprehension
_ = runSomeLogging(secondRes)
thirdRes <- EitherT(client.getThird(secondRes.value))
} yield thirdRes
def runSomeLogging(): Future[Either[Error, Response]] =
Thread.sleep(10000)
Future.successful(Right(Response("123")))
}
So this above code will wait the 10 seconds before returning the thirdRes result. My hope was to kick off the runSomeLogging function on a separate thread after the secondRes runs. I thought the usage of = rather than <- would cause that, however it doesn't.
The way I am able to get this to work is below. Basically I run my second future outside of the comprehension and use .onComplete on the previous future to only run my logging if certain conditions were meant from the above comprehension. I only want to run this logging function if the secondRes function is successful in my example here.
private def runSomeLogging(response: SecondRes) =
Thread.sleep(10000)
response.value.onComplete {
case Success(either) =>
either.fold(
_ => { },
response => { logThing() }
)
case _ =>
}
private def testFunction(): EitherT[Future, Error, Response] =
val res = for {
firstRes <- EitherT(client.getFirst())
secondRes <- EitherT(client.getSecond())
thirdRes <- EitherT(client.getThird(secondRes.value))
} yield thirdRes
runSomeLogging(res)
return res
This second example works fine and does what I want, it doesn't block the for comprehension for 10 seconds from returning. However, because there are dependencies of this running for certain pieces of the comprehension, but not all of them, I was hoping there was a way to kick off the job from within the for comprehension itself but let it run on its own thread and not block the comprehension from completing.
You need a function that starts the Future but doesn't return it, so the for-comprehension can move on (since Future's map/flatMap functions won't continue to the next step until the current Future resolves). To accomplish a "start and forget", you need to use a function that returns immediately, or a Future that resolves immediately.
// this function will return immediately
def runSomeLogging(res: SomeResult): Unit = {
// since startLoggingFuture uses Future.apply, calling it will start the Future,
// but we ignore the actual Future by returning Unit instead
startLoggingFuture(res)
}
// this function returns a future that takes 10 seconds to resolve
private def startLoggingFuture(res: SomeResult): Future[Unit] = Future {
// note: please don't actually do Thread.sleep in your Future's thread pool
Thread.sleep(10000)
logger.info(s"Got result $res")
}
Then you could put e.g.
_ = runSomeLogging(res)
or
_ <- Future { runSomeLogging(res) }
in your for-comprehension.
Note, Cats-Effect and Monix have a nice abstraction for "start but ignore result", with io.start.void and task.startAndForget respectively. If you were using IO or Task instead of Future, you could use .start.void or .startAndForget on the logging task.
Using Scala 2.13.0:
implicit val ec = ExecutionContext.global
val arr = (0 until 20).toIterator
.map { x =>
Thread.sleep(500);
println(x);
x
}
val fss = arr.map { slowX =>
Future { blocking { slowX } }
}
Await.result(Future.sequence(fss), Inf)
problem
arr is an iterator where each item needs 500ms processing time. We map the iterator with Future { blocking { ... }} with the purpose of making the processing parallel (using the global execution context). Finally we run Future.sequence
to consume the iterator.
Given the definition of Future.apply[T](body: =>T) and blocking[T](body: =>T), body is passed lazily, which means that body will be processed in the Future. If we inject that in the definition of Iterator.map, we get def next() = Future{blocking(self.next())}, so each item of the iterator should be processed in the Future.
But when I try this example however, I can see that the iterator is consumed sequentially, which is not what is expected!
Is that a Scala bug?? Or am I missing something?
No it's not a bug, because:
val arr = (0 until 20).toIterator
// this map invokes first and executed sequentially, because it executes in same thread.
.map { x =>
Thread.sleep(500);
println(x);
x
}
// This go sequentially because upstream map executed sequentially in same thread.
// So, "Future { blocking { slowX } }" can be replaced with "Future.successfull(slowX)"
// because no computation executed
val fss = arr.map { slowX =>
Future { blocking { slowX } }
}
If you want perform completely asynchronously, you can do something like:
def heavyCalculation(x: Int) = {
Thread.sleep(500);
println(x);
x
}
val result = Future.traverse((0 until 20).toList) { x =>
Future(blocking(heavyCalculation(x)))
}
Await.result(result, 1 minute)
Working Scatie example: https://scastie.scala-lang.org/3v06NpypRHKYkqBgzaeVXg
First, this is not a proper benchmark, you actually haven't show formal proof that this is sequential and not parallel (although is "obvious" from the source code that it isn't).
Second, and Iterator of Futures is probably a bad idea; at this point, it may make sense to look into a streaming solution like Akka-Streams, fs2, Monix or ZIO.
Third, what is even the point of having a bunch of blocking futures? you aren't actually winning too much.
Fourth, the problem is that the second map is not passing the block of the first map, just the result. So, you actually did the sleep before creating the Future.
Fifth, you probably want to do this instead.
val result = Future.traverse(data) { elem =>
Future {
blocking {
// Process elem here.
}
}
}
Await.result(result, Inf)
The other answers were pointing in the right direction, but the formal answer is the following: the signature of Iterator.map(f: A => B) tells us that A that A is computed before f is applied to it (because it is not => A). Therefore, next() is computed in the main thread.
I am having problem with my play-framework API server. I need to have some processing running in the background that is returning a Future with the result and then write the result as response. However, the request thread goes all out and return before my Future completes. Here is the code...
def requestAction(): Action[AnyContent] = Action.async { implicit request =>
var fResult: Future[String] = Future { "initial value" }
try {
fResult = doSomethingAsyncAndGetResponseString(); // return "great, everything is done"
}
catch {
case t: Throwable {
fResult = Future { "failed" }
}
}
// done, return response, but the problem is, this is executed first because doSomethingAsyncAndGetResponseString() is still executing and returns later
fResult.map( res => {
// problem here, because I get "initial value" which is not what I want
Ok(res)
}
}
Is there a way to get "great, everything is done" or "failed" without Async.await ? I have been using this format all over in my API server, but today it broke because in a new API that I write, the doSomethingAsyncAndGetResponseString is a bit longer. I didn't expect that, so something must be wrong with how I understand the structure.
Thanks in advance!
You are trying to write Java like code using Scala.
You are doing it wrong. Read about Futures and How to use them.
Here is the tutorial
Futures can be composed using map and flatMap constructs. recover and recoverWith will give the user access to exception happened in the computation pipeline
You have to do something like this, given below
def requestAction(): Action[AnyContent] = Action.async { implicit request =>
Future { "initial value" }.flatMap { _ =>
doSomethingAsyncAndGetResponseString() // returns Future
}.map { res =>
Ok(res)
}.recover { case th =>
Ok(th.getMessage)
}
}
Handling exceptions and recovering from exceptions
Exception handling is inbuilt into Future.
recover gives access to exception and also helps the user to provide the alternative success value in case of exception.
recoverWith gives access to an exception and also helps the user to provide/chain alternative Future computation which can succeed or fail.
Future {
throw new Exception("foo exception")
}.recover {
case th => println(s"msg: ${th.getMessage}")
}
I have a for comprehension of futures that all make remote service calls (REST, RPC, etc...). If one of those future fails, will the entire for comprehension fail?
For instance, let's say I have two futures that make service calls
val service1Future: Future[Response] = ...
val service2Future: Future[Response] = ...
Let's say service2Future also has some recoverWith that throws an exception PartialFunction[Throwable, Future[U]].
If service2Future fails, I don't really care. Can I enforce that in my for comprehension?
for {
service1Response <- service1Future
service2Response <- service2Future
} yield {
// do stuff with service1Response, but I want to get here even if service2Future fails
}
Yes, the entire for-comprehension will fail if the second future fails as written. Would something as simple as this work?
for {
service1Response <- service1Future
service2Response <- service2Future.recoverWith(...)
} yield {
...
}
One way would be to return Future[Try[Response]] instead of Future[Response]. In recoverWith you would return Future(Failure(e)). You can then pattern match in the yield to see if it was successful or not.
I have a function which provides a Context:
def buildContext(s:String)(request:RequestHeader):Future[Granite.Context] = {
.... // returns a Future[Granite.Context]
}
I then have another function which uses a Context to return an Option[Library.Document]:
def getDocument(tag: String):Option[Library.Document] = {
val fakeRequest = play.api.test.FakeRequest().withHeaders(CONTENT_TYPE -> "application/json")
val context = buildContext(tag)(fakeRequest)
val maybeDoc = context.getDocument //getDocument is defined on Granite.Context to return an Option[Library.Document]
}
How would this code take into account if the Future has returned or not? I have seen for/yield used to wait for the return but I always assumed that a for/yield just flatmaps things together and has nothing really to do with waiting for Futures to return. I'm kinda stuck here and don't really no the correct question to ask!
The other two answers are misleading. A for yield in Scala is a compiler primitive that gets transformed into map or flatMap chains. Do not use Await if you can avoid it, it's not a simple issue.
You are introducing blocking behaviour and you have yet to realise the systemic damage you are doing when blocking.
When it comes to Future, map and flatMap do different things:
map
is executed when the future completes. It's an asynchronous way to do a type safe mapping.
val f: Future[A] = someFutureProducer
def convertAToB(a: A): B = {..}
f map { a => convertAToB(a) }
flatMap
is what you use to chain things:
someFuture flatMap {
_ => {
someOtherFuture
}
}
The equivalent of the above is:
for {
result1 <- someFuture
result2 <- someOtherFuture
} yield result2
In Play you would use Async to handle the above:
Async {
someFuture.map(i => Ok("Got result: " + i))
}
Update
I misunderstood your usage of Play. Still, it doesn't change anything. You can still make your logic asynchronous.
someFuture onComplete {
case Success(result) => // doSomething
case Failure(err) => // log the error etc
}
The main difference when thinking asynchronously is that you always have to map and flatMap and do everything else inside Futures to get things done. The performance gain is massive.
The bigger your app, the bigger the gain.
When using a for-comprehension on a Future, you're not waiting for it to finish, you're just saying: when it is finished, use it like this, and For-comprehension returns another Future in this case.
If you want to wait for a future to finish, you should use the Await as follows:
val resultContext = Await.result(context , timeout.duration)
Then run the getDocument method on it as such:
val maybeDoc = resultContext.getDocument
EDIT
The usual way to work with Futures however is to wait until the last moment before you Await. As pointed out by another answer here, Play Framework does the same thing by allowing you to return Future[Result]. So, a good way to do things would be to only use for-comprehensions and make your methods return Futures, etc, until the last moment when you want to finally return your result.
You can use scala.concurrent.Await for that:
import scala.concurrent.duration._
import scala.concurrent.Await
def getDocument(tag: String):Option[Library.Document] = {
val fakeRequest = play.api.test.FakeRequest().withHeaders(CONTENT_TYPE -> "application/json")
val context = Await.result(buildContext(tag)(fakeRequest), 42.seconds)
val maybeDoc = context.getDocument
}
But Await will block thread while future is not completed, so would be better either make buildContext a synchronous operation returning Granite.Context, or make getDocument async too, returning Future[Option[Library.Document]].
Once you are in a future you must stay in the future or you must wait until the future arrives.
Waiting is usually a bad idea because it blocks your execution, so you should work in the future.
Basically you should change your getDocument method to return a Future to something like getDocument(tag: String):Future[Option[Library.Document]]
Then using map ro flatMap, you chain your future calls:
return buildContext(tag)(fakeRequest).map(_.getDocument)
If buildContext fails, map will wrap the Failure
Then call
getDocument("blah").onComplete {
case Success(optionalDoc) => ...
case Failure(e) =>...
}