In scala concurrent package, there is onComplete method which returns nothing while in akka-http directives, it returns the Directive1.
Is that the only difference? I sometimes got confused on those two methods.
These methods are different in what they aim to accomplish, but they are related in that the akka http onComplete is meant to work with the completion of a Future in order to complete the routing of a request.
The concurrent onComplete is a method on a Future that is meant to perform some sort of side effecting when the Future has completed. There are a lot of other methods on a Future like map, flatMap and filter that will produce another Future as you go through a series of transforms, but onComplete returns nothing, and is sort of a "dead end" in the processing of that Future.
An example of using onComplete while also showing how something like map can be used to change the Future is as follows:
val fut1 = Future{"1"} // A Future[String]
val fut2 = fut1.map(_.toInt) // Now a Future[Int]
fut2.onComplete{
case util.Success(i) => println(s"success: $i")
case util.Failure(ex) => ex.printStackTrace()
}
val badFut1 = Future{"A"}
val badFut2 = badFut.map(_.toInt) //Will be failed
badFut2.onComplete{
case util.Success(i) => println(s"success: $i")
case util.Failure(ex) => ex.printStackTrace()
}
In the first case, we will hit the success case as "1" can be mapped into an Int. In the second example, we will hit the error case as "A" cannot be mapped to an Int. Either way, you can see that something like map can be used to create a new transformed future while onComplete can be used to do something based on the final result of that Future.
In Akka Http, you sometimes need to create Route definitions that need to do something asynchronously. In that case, you need to complete the route when that asynchronous task completed. A Route itself needs to be fully defined in order to compile, so you can't leverage the existing onComplete on a Future because that returns nothing and that would render the definition of that Route as incomplete. Therefore, the guys at Akka defined an onComplete directive that will snap into a routing tree and work with a Future, returning a Directive that will fully define the tree for that Route as oppose to leaving it open ended as the Future.onComplete would. An example:
val myRoute =
get{
val fut = myActor ? "foo"
onComplete(foo){
case util.Success(_) => complete(StatusCodes.OK)
case util.Failure(ex) => complete(StatusCodes.InternalServerError)
}
}
So here, I'm making a request to an Actor using ask (?) and I get a Future back. I still need to produce something that completes the Route I am defining. If I did something like this:
val myRoute =
get{
val fut = myActor ? "foo"
fut.onComplete{
case util.Success(_) => complete(StatusCodes.OK)
case util.Failure(ex) => complete(StatusCodes.InternalServerError)
}
}
It worn't work because the onComplete here is the one from the Future and not the directive, and that returns Unit and my Route won't be full defined and won't compile.
So the onComplete directive is there to allow you to define a Route based on the completion of a Future which you can't do properly with the pre-existing onComplete from Future itself.
Related
What is the proper way to handle Futures from inside an Akka (typed) Actor?
For example, assume there is an Actor OrderActor that receives Commands to place orders... which it does by making an http call to an external service. Since these are http calls to an external service, Futures are involved. So, what is the right way to handle that Future from within the Actor.
I read something about the pipeTo pattern. Is that what needs to happen here or something else?
class OrderActor(context: ActorContext[OrderCommand], orderFacade: OrderFacade)
extends AbstractBehavior[OrderCommand](context) {
context.log.info("Order Actor started")
override def onMessage(msg: OrderCommand): Behavior[OrderCommand] = {
msg match {
case PlaceOrder(
referenceId: OrderReferenceId,
ticker: Ticker,
quantity: Int,
replyTo: ActorRef[OrderResult]
) =>
orderFacade
.placeOrder(ticker, quantity) //this returns a Future
.map(res => {
//transform result
//book keeping / notification (affects state)
replyTo ! transformed
//Can/Should we map like this? I tried adding a log statement in here, but I never see it... and the replyTo doesnt seem to get the message.
})
this
It's generally best to avoid doing Future transformations (map, flatMap, foreach, etc.) inside an actor. There's a distinct risk that some mutable state within the actor isn't what you expect it to be when the transformation runs. In Akka Classic, perhaps the most pernicious form of this would result in sending a reply to the wrong actor.
Akka Typed (especially in the functional API) reduces a lot of the mutable state which could cause trouble, but it's still generally a good idea to pipe the Future as a message to the actor.
So if orderFacade.placeOrder results in a Future[OrderResponse], you might add subclasses of OrderCommand like this
// also include fields from the PlaceOrder which will be useful
case class OrderResponseIs(resp: OrderResponse, replyTo: ActorRef[OrderResult]) extends OrderCommand
// TODO include fields
case class OrderFailed() extends OrderCommand
And then pipe the Future to yourself with:
import scala.util.{ Failure, Success }
context.pipeToSelf(orderFacade.placeOrder) {
case Success(resp) => OrderResponseIs(resp, replyTo)
case Failure(_) => OrderFailed()
}
You then have to handle those messages:
case OrderResponseIs(resp, replyTo) =>
// transform resp
val transformed = ???
replyTo ! transformed
this
case OrderFailed() =>
context.log.warning("Stuff is broken")
this
There's not actually much overhead in doing this versus map and friends (both will typically involve scheduling a task for asynchronous execution on the dispatcher).
I was suggested to use Future[Book] rather than Future[Try[Book]] in a pull request review.
Why is that?
If I use Future[Book], then how I can convert Success or Failure. Is it not clear to me at all.
If we use Future[Book], we are completely losing the notion of success and failure in Scala.
In Scala, the resulting value of a future is wrapped in either a Success or Failure type, which is a type of Try. Hence, the value in a Future is always an instance of one of the Try types: Success or Failure.
Since these are Try types, you can register a callback on a future which accepts a Try type. An example is to use the onComplete which takes a callback function of type Try[T] => U and returns a Unit.
import scala.util.{Success, Failure}
val f: Future[List[String]] = Future {
session.getUserNamesFromDatabase
}
f onComplete {
case Success(users) => for (user <- users) println(user)
case Failure(t) => println("An error has occurred: " + t.getMessage)
}
The benefit of a callback on a future is that it can complete without blocking the client. So the callback can execute completely asynchronously.
Note: that the onComplete method has a unit return type. So it cannot be chained.
I've a scala project with http4s 0.15.16a and slick 3.2.1 with these steps:
Receive a ID by rest call
passing ID to MySlickDAO that responds with a Future
Call Await.result(res, Duration.Inf) on Future returned by MySlickDAO
Create the json
The problem is that I use a Await.result and this is bad practices
is there a better solution ?
Here the code:
val service = HttpService {
//http://localhost:8080/rest/id/9008E75A-F112-396B-E050-A8C08D26075F
case GET -> Root / "rest" / "id" / id =>
val res = MySlickDAO.load(id)
Await.result(res, Duration.Inf)
val ll = res.value.get.get
ll match {
case Failure(x) =>
InternalServerError(x)
case Success(record) =>
val r = record.map(x => MyEntity(x._1, x._2, x._3))
jsonOK(r.asJson)
}
case ....
}
Instead of awaiting, you can chain the result of one Future into another:
val resFut = MySlickDAO.load(id)
resFut.map { record =>
val r = record.map(x => MyEntity(x._1, x._2, x._3))
jsonOK(r.asJson)
} recover { x =>
InternalServerError(x)
}
The result of this will be a Future of a common supertype of jsonOK and InternalServerError (not familiar with the libraries you're using; so I may have the type of load wrong: it's not a Future[Try[_]] is it?).
BTW: your original code has a very problematic line:
val ll = res.value.get.get
res.value is an Option[Try[T]]. Calling get on an Option or a Try is generally a bad idea (even though in this case because of the Await, the Option should never be None, so the get is technically safe) because it can throw an exception. You're much better off using map, flatMap, and friends.
The issue is that http4s 0.15 uses the Scalaz concurrency constructs, while Slick uses the native Scala ones, and the two aren't designed to work with each other. My understanding is that http4s 0.17+ has switched from Scalaz to Cats, which might entail using native Scala Futures, so if you can upgrade that might be worth a shot. If not, you can handle the conversion by manually creating a Task that wraps your future:
def scalaFutureRes = MySlickDAO.load(id)
val scalazTaskRes = Task.async { register =>
scalaFutureRes.onComplete {
case Success(success) => register(success.right)
case Failure(ex) => register(ex.left)
}
}
At this point you've got a Task[ResultType] from the Future[ResultType] which you can map/flatMap with the rest of your logic like in Levi's answer.
You can also use the delorean library for this which has this logic and the opposite direction defined on the classes in question via implicit conversions, so that you can just call .toTask on a Future to get it in a compatible form. Their readme also has a lot of useful information on the conversion and what pitfalls there are.
I'm writing an HTTPS service for a chat bot and find myself dealing with a lot of Futures and Options. Usually if an Option returns None or a Future fails I want to log the exception and reset the user back to the start. Here's a toy example of how I accomplish this:
(for {
user <- userService.retrieve(userId)
userPet <- Future(user.userPet.get)
_ <- sendTextAsJson(s"You're holding a $userPet!")
} yield {}).recover {
case ex: Exception =>
log.error(ex.toString)
fail
}
This works fine but it feels a little weird to wrap things in Future just so their exceptions are swallowed and dealt with in the recover block. It also feels weird to include an empty yield block. Is there a better way?
What you basically do is using onSuccess or onFailure to retrieve the futures result. What you also might try is Try.
There is an example of the underlying functionality.
http://www.scala-lang.org/api/2.9.3/scala/util/Try.html
I might suggest you this article: http://docs.scala-lang.org/overviews/core/futures.html I can't summarize whats stated there in a few sentences. But if you look to the table of contents on the right side, the point Futures explains what happens and how to handle it is stated under Excepetions. Thats the idiomatic way.
I don't think it's too bad tbh, assuming userService.retrieve() returns a Future in the first place. I'd personnally rather use map in this case to make things a bit more explicit :
val futureMsg = userService.retrieve(userId)
.map(user => sendTextAsJson(s"You're holding a ${user.userPet.get}!")
.recover {
case NonFatal(ex) => //Look it up ;)
log.error(ex.toString)
fail
}
You now have Future[Unit] to do whatever you want with.
I agree with you that that's an awkward use of a for-comprehension. This is how I would write this:
import scala.util.control.NonFatal
userService.retrieve(userId)
.map(_.userPet.get)
.map(userPet => s"You're holding a $userPet!")
.flatMap(sendTextAsJson)
.recover {
case NonFatal(ex) =>
log.error(ex.toString)
fail
}
Looking at your sendTextAsJson and fail functions you seem to want to side-effect when the future completes. I would not use map after you perform the Option.get and instead look at Futures onComplete method to either handle the success of the future or handle the exception throw. I am not sure how this way and recover are different though so double check that. I did like #sascha10000 link to the scala doc futures. Its been a long time since I read that but its a good resource from what I remember
Example implementation of your code with onComplete:
import scala.concurrent.Future
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global
object UserFuture extends App {
def fail = println("failed")
def sendTextAsJson(message: String): Unit = println(message)
case class User(userPet: Option[String])
object userService {
def userPet = Some("dog")
val someUser = User(userPet)
def retrieve(userId: Int): Future[User] = Future {
someUser
}
}
def getPet(userId: Int): Unit = {
userService.retrieve(userId)
.map(user => user.userPet.get)
.onComplete {
case Success(userPet) => sendTextAsJson(s"You're holding a $userPet!")
case Failure(ex) => println(ex.toString); fail
}
}
getPet(1)
Thread.sleep(10000) // I forgot how to use Await. This is just here to be able to make sure we see some printouts in the console.
}
Given that we must avoid...
1) Modifying state
2) Blocking
...what is a correct end-to-end usage for a Future?
The general practice in using Futures seems to be transforming them into other Futures by using map, flatMap etc. but it's no good creating Futures forever.
Will there always be a call to onComplete somewhere, with methods writing the result of the Future to somewhere external to the application (e.g. web socket; the console; a message broker) or is there a non-blocking way of accessing the result?
All of the information on Futures in the Scaladocs - http://docs.scala-lang.org/overviews/core/futures.html seem to end up writing to the console. onComplete doesn't return anything, so presumably we have to end up doing some "fire-and-forget" IO.
e.g. a call to println
f onComplete {
case Success(number) => println(number)
case Failure(err) => println("An error has occured: " + err.getMessage)
}
But what about in more complex cases where we want to do more with the result of the Future?
As an example, in the Play framework Action.async can return a Future[Result] and the framework handles the rest. Will it eventually have to expect never to get a result from the Future?
We know the user needs to be returned a Result, so how can a framework do this using only a Unit method?
Is there a non-blocking way to retrieve the value of a future and use it elsewhere within the application, or is a call to Await inevitable?
Best practice is to use callbacks such as onComplete, onSuccess, onFailure for side effecting operations, e.g. logging, monitoring, I/O.
If you need the continue with the result of of your Future computation as opposed to do a side-effecting operation, you should use map to get access to the result of your computation and compose over it.
Future returns a unit, yes. That's because it's an asynchronous trigger. You need to register a callback in order to gather the result.
From your referenced scaladoc (with my comments):
// first assign the future with expected return type to a variable.
val f: Future[List[String]] = Future {
session.getRecentPosts
}
// immediately register the callbacks
f onFailure {
case t => println("An error has occurred: " + t.getMessage)
}
f onSuccess {
case posts => for (post <- posts) println(post)
}
Or instead of println-ing you could do something with the result:
f onSuccess {
case posts: List[String] => someFunction(posts)
}
Try this out:
import scala.concurrent.duration._
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
val f: Future[Int] = Future { 43 }
val result: Int = Await.result(f, 0 nanos)
So what is going on here?
You're defining a computation to be executed on a different thread.
So you Future { 43 } returns immediately.
Then you can wait for it and gather the result (via Await.result) or define computation on it without waiting for it to be completed (via map etc...)
Actually, the kind of Future you are talking about are used for side-effects. The result returned by a Future depends its type :
val f = Future[Int] { 42 }
For example, I could send the result of Future[Int] to another Future :
val f2 = f.flatMap(integer => Future{ println(integer) }) // may print 42
As you know, a future is a process that happens concurrently. So you can get its result in the future (that is, using methods such as onComplete) OR by explicitly blocking the current thread until it gets a value :
import scala.concurrent.Await
import akka.util.Timeout
import scala.concurrent.duration._
implicit val timeout = Timeout(5 seconds)
val integer = Await.result(Future { 42 }, timeout.duration)
Usually when you start dealing with asynchronous processes, you have to think in terms of reactions which may never occur. Using chained Futures is like declaring a possible chain of events which could be broken at any moment. Therefore, waiting for a Future's value is definitely not a good practice as you may never get it :
val integer = Await.result(Future { throw new RuntimeException() }, timeout.duration) // will throw an uncaught exception
Try to think more in terms of events, than in procedures.