Because I do some "complex" operations, I think my Actor became asyncronous. The main problem I think is that I use Await.result inside the method which return responses.
actor:
def process(subscribers: Set[ActorRef]): Receive = {
case Join(ref) => context become process(subscribers + ref)
case Leave(ref) => context become process(subscribers - ref)
case Push(request) =>
val filteredSubscribers = (subscribers - sender())
.filter(s => exists(s, request)) // just some actor filters
filteredSubscribers.foreach { subscriber =>
// here I have a Map with each actor requests
val actorOptions = getActorOptions(subscriber)
subscriber ? getResponse(actorOptions, request)
}
}
The problem is inside getResponse (I think).
getResponse(actorOptions: JsValue, request: SocketRequest): JsValue = {
(actorOptions \ "dashboardId").asOpt[Int] match {
case Some(id) => {
val response = widgetsService.getByDashboadId(id) map { widgets =>
val widgetsResponse: List[Future[String]] = widgets.map(w => {
widgetsService.getDataById(w.id) map {
data => s"""{ "widgetId": ${w.id}, "data": $data }"""
}
})
var responses: List[String] = List.empty
widgetsResponse.foreach(f => {
f.onComplete {
case Success(value) => responses = value :: responses
case Failure(e) => println(s"Something happened: ${e.getMessage}")
}
})
// first time when I use Await.result
// used to populate the responses list with data from all futures
Await.result(Future.sequence(widgetsResponse), Duration.Inf)
Json.parse(s"""{
"dashboardId": $id,
"widgets": [${response.mkString(", ")}]
}""".stripMargin)
}
// second time when I use Await.result
// used to return a JsValue instead of a Future[JsValue]
Await.result(response, Duration.Inf)
}
case None => buildDefaultJson // return default json value, unimportant for this example
}
}
Due of that, In frontend, if I have 2 sockets clients, the response for the second will be send only after first.
I found that I can obtain a "fake" increase of performance if I embrance the getResponse in a future inside of my Actor.
filteredSubscribers.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
Future(subscriber ? getResponse(actorOptions, request))
}
So, for both subscribers the action will be started in same time, but when the first will reach the Await.result, the second will be locked until first is done.
I need to avoid using Await.result there, but I don't know how to get the results of a list of futures, without using for-comprehension (because is a dynamically list) for first time where I use it.
Because Akka ask operator (?) return a Future[Any], I tried that my getResponse method to return directly a JsValue to be mapped then in Future[JsValue]. If I remove the second Await.result and my method will return Future[JsValue], then the actor will return a Future[Future[JsValue]] which I don't think is too right.
After some more researches and solutions found on so, my code become:
Future.sequence(widgetsResponse) map { responses =>
Json.parse(
s"""
|{
|"dashboardId": $id,
|"tableSourceId": $tableSourceId,
|"widgets": [ ${responses.mkString(", ")}]
|}""".stripMargin
)
}
getResponse returns a Future[JsValue] now, removing both Await.result cases, and actor case become:
filteredSubscribers.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
getResponse(actorOptions, request) map { data =>
subscriber ? data
}
}
I don't know why, still have a synchronous behavior. Damn, this can be due of my subscribers type: Set[ActorRef]? I tried to use parallel foreach and this looks like solving my problem:
filteredSubscribers.par.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
getResponse(actorOptions, request) map { data =>
subscriber ? data
}
}
Related
I have an actorA that takes data from dataActor, and responds data to requestActors:
class A extends Actor {
var data = Option.empty[Int]
val requestors = mutable.Set[ActorRef]
def receive = {
case data: Int =>
this.data = Some(data)
requestors.foreach {_ ! data}
case Request =>
if (data.isEmpty) {
requestors.add(sender)
//What should I send back to sender here?
} else {
sender ! data.get
}
}
}
In the sender actor, I use the ask pattern:
(actorA ? Request()).map {
//if the data is not ready here, how can I wait for data?
}
In the data provider actor, it sends the data to the actorA:
dataActor ! 100
The problem is the data may not be ready when the requestor is asking for it, so the Future from the ask may fail. I need to send the sender something to promise there is data, but apparently Akka does not seem to have promise AKAIK.
How to implement it here?
A couple of possibilities.
First, you could return the actual Option (data.get is "code smell" anyway, and better be avoided):
case Request() => data
And then just keep trying on the client end:
def result: Future[Int] = (actor ? Request()).map {
case Some(n) => n
case None => after(1 second, system.scheduler)(result)
}
Alternatively, return a Future:
val ready = Promise[Int]()
case data: Int =>
this.data = data
ready.complete(data)
case Request() =>
data.fold(ready.future)(Future.successful)
Now, you just need to flatten it on the caller side:
val result: Future[Int] = (actor ? Request()).flatMap {
case f: Future[Int] => f
}
val value = authenticateUser
private def authenticateUser = {
val holder = WS.url(platformUrl + "/userApi/auth/login?username=testuser&password=testPass")
val res = holder.post(Results.EmptyContent()).onComplete {
case Success(response) => response.cookies.map{cookie => println(cookie.value.get)}
case Failure(errors) => println("")
// The `Future` failed.
}
}
How to return cookie.value.get from authenticateUser method?
First of all, you seem to have many cookies, so it's not clear, the value of which one you want to return.
More importantly, you should not actually return the value itself. The idea is to return the Future, that can be further transformed downstream:
def authenticateUser = WS.url(..)
.post(Results.EmptyContent)
.map { _.cookies }
.map { _.find(_.name == "Auth").flatMap(_.value).getOrElse("") }
Now, somewhere downstream, you can have something like:
def serve(request: Request): Future[Response] = authenticateUser
.map { user =>
serveRequest(user, request)
}.map { result => makeResponse(result) }
I don't know the specifics of Play, so, consider the Request/Response stuff "pseudocode", but the basic idea is that your entire processing should be a chain of Future transformations. The general rule is to never block the service thread.
I have actually a problem while returning a Seq back to frontend.
My code looks like this:
def getContentComponentsForProcessSteps: Action[AnyContent] = Action.async { implicit request =>
println("-----------------------------------------------New Request--------------------------------------------------------")
println(request.body.asJson)
request.body.asJson.map(_.validate[ProcessStepIds] match {
case JsSuccess(steps, _) =>
val contentComponents: Seq[Future[Seq[Future[ContentComponentModel]]]] = steps.steps.map(stepId => { //foreach
// Fetching all ContentComponent Relations
contentComponentDTO.getContentComponentsByStepId(stepId).map(contentComponents => { // Future[Seq[ContentComponent_ProcessStepTemplateModel]]
// Iteration über die Gefundenen Relations
contentComponents.map(contentComponent => { // foreach
// Fetch Content Component
contentComponentDTO.getContentComponentById(contentComponent.contentComponent_id).flatMap(contentComponent => { // Future[Option[ContentComponentModel]]
// Fetch Content Component Data for the types
val res = getContentComponentDataforOneContentComponent(contentComponent.get)
res.map(con => con)
})
})
})
})
Future.sequence(contentComponents).map(eins => {
println(eins)
Ok(Json.obj("Content Components Return" -> "true", "result" -> eins))
})
case JsError(_) =>
Future.successful {
BadRequest("Can't fetch Content Components")
}
case _ => Future.successful {
BadRequest("Can't fetch Content Components")
}
}).getOrElse(Future.successful {
BadRequest("Can't fetch Content Components")
})
}
Error is the following.
Thanks for any hint
Look at the type of eins your error message is telling you that it is a Seq[Seq[Future[ContenetComponentModel]]] and not simply a Seq like you thought.
There are two problems with this:
You can't write a Future (or in your case, a sequence of futures) to Json.
You need to have an implicit function in scope to convert your ContenetComponentModel to a JSON value.
Depending on what you want your result to look like, you could try flattening eins and then using another Future.sequence, but I think what you really should be doing is changing a lot of your .map calls to .flatMap calls to avoid the nesting in the first place.
I'm new to asynchronous programming. I read this tutorial http://danielwestheide.com/blog/2013/01/09/the-neophytes-guide-to-scala-part-8-welcome-to-the-future.html and was surprised by how effortless I can incorporate Future into the program. However, when I was using Future with Routing, the return type is kind of wrong.
get {
optionalCookie("commToken") {
case Some(commCookie) =>
val response = (MTurkerProgressActor ? Register).mapTo[..].map({...})
val result = Await.result(response, 5 seconds)
setCookie(HttpCookie("commToken", content = result._2.mturker.get.commToken)) {
complete(result._1, result._2.mturker.get)
}
case None => // ...
}
}
I really don't want to use Await (what's the point of asynchronous if I just block the thread and wait for 5 seconds?). I tried to use for-comprehension or flatMap and place the setCookie and complete actions inside, but the return type is unacceptable to Spray. For-comprehension returns "Unit", and flatMap returns a Future.
Since I need to set up this cookie, I need the data inside. Is Await the solution? Or is there a smatter way?
You can use the onSuccess directive:
get {
optionalCookie("commToken") { cookie =>
//....
val response = (MTurkerProgressActor ? Register).mapTo[..].map({...})
onSuccess(response) {
case (result, mTurkerResponse) =>
setCookie(HttpCookie("commToken", content = mTurkerResponse.mturker.get.commToken)) {
complete(result, mturkerResponse.mturker.get)
}
}
}
There's also onFailure and onComplete (for which you have to match on Success and Failure) See http://spray.io/documentation/1.2.1/spray-routing/future-directives/onComplete/
Also, instead of using get directly it's much more idiomatic to use map (I assume the mturker is an Option or something similar):
case (result, mTurkerResponse) =>
mTurkerResponse.mturker.map { mt =>
setCookie(HttpCookie("commToken", content = mt.commToken)) {
complete(result, mt)
}
}
You can also make a custom directive using this code -
case class ExceptionRejection(ex: Throwable) extends Rejection
protected def futureDirective[T](x: Future[T],
exceptionHandler: (Throwable) => Rejection = ExceptionRejection(_)) =
new Directive1[T] {
override def happly(f: (::[T, HNil]) => Route): Route = { ctx =>
x
.map(t => f(t :: HNil)(ctx))
.onFailure { case ex: Exception =>
ctx.reject(exceptionHandler(ex))
}
}
}
Example usage -
protected def getLogin(account: Account) = futureDirective(
logins.findById(account.id)
)
getAccount(access_token) { account =>
getLogin(account) { login =>
// ...
}
}
I have an Akka actor responsible of handling http calls. I use scala dispatch to send multiple HTTP requests over an API:
urls.foreach { u
val service = url(u)
val promise = Http(service OK as.String).either
for(p <- promise)
{
p match
{
case Left(error) =>
faultHandler(error)
case Right(result) =>
resultHandler(result)
}
}
In the resultHandlerfunction, I increment an instance variable nbOfResults and compare to the number of calls I have done.
def resultHandler(result:String)
{
this.nbOfResults++
...
if(nbOfResults == nbOfCalls)
// Do something
}
Is it safe ? May the nbOfResultsvaraible be accessed at the same time if two calls return their results simultaneously ?
For now, I believed that the actor is more or less equivalent to a thread and therefore the callback functions are not executed concurrently. Is it correct ?
Here is a variant of Alexey Romanov response using only dispatch :
//Promises will be of type Array[Promise[Either[Throwable, String]]]
val promises = urls.map { u =>
val service = url(u)
Http(service OK as.String).either
}
//Http.promise.all transform an Iterable[Promise[A]] into Promise[Iterable[A]]
//So listPromise is now of type Promise[Array[Either[Throwable, String]]]
val listPromise = Http.promise.all(promises)
for (results <- listPromise) {
//Here results is of type Array[Either[Throwable, String]]
results foreach { result =>
result match {
Left(error) => //Handle error
Right(response) => //Handle response
}
}
}
There is a far better way:
val promises = urls.map {u =>
val service = url(u)
val promise = Http(service OK as.String).either
}
val listPromise = Future.sequence(promises)
listPromise.onComplete { whatever }
I agree with Alexey Romanov on his answer. Whatever way you choose to synchronize your http requests beware of the way your are processing the promises completion. Your intuition is correct in that concurrent access may appear on the state of the actor. The better way to handle this would be to do something like this:
def resultHandler(result: String) {
//on completion we are sending the result to the actor who triggered the call
//as a message
self ! HttpComplete(result)
}
and in the actor's receive function:
def receive = {
//PROCESS OTHER MESSAGES HERE
case HttpComplete(result) => //do something with the result
}
This way, you make sure that processing the http results won't violate the actor's state from the exterior, but from the actor's receive loop which is the proper way to do it
val nbOfResults = new java.util.concurrent.atomic.AtomicInteger(nbOfCalls)
// After particular call was ended
if (nbOfResults.decrementAndGet <= 0) {
// Do something
}
[EDIT] Removed old answer with AtomicReference CAS - while(true), compareAndSet, etc