Reputation: 9421
I've written an application (Scala) Play and when I run it on my local machine, it works exactly as I expect. The method that I'm working on (there may be others with this issue, I haven't checked extensively) is a method triggered by a POST request. It returns an Async object because the evaluation done by the method returns a Future.
But this method behaves quite differently when I deploy it. I've been chasing this down all day, but I've gotten to the point where I have no explanation for what is going on.
The method looks something like this:
def add = Action(parse.json) { request =>
Async {
val req = request.body.asOpt[TaskRequest] map { r =>
val job = createJob(r)
submitJob(job, r)
job map { ij =>
allowOrigin(Created(ij).withHeaders("Location" -> routes.Jobs.read(ij._id.stringify).url));
}
}
req.get
}
}
Overall, this is pretty simple. The createJob
method returns a Future[Job]
. The submitJob
call is a kind of "fire and forget" call to trigger some background stuff (that I'm not interested in waiting for). I then process the job
result to produce a Result
object when the job
has been created.
All this works as I described above on my local machine. But when I deploy this, something really odd happens. That submitJob
call triggers a long running computation. As I said, I'm not interested in waiting for it which is why I don't even use the Future
that it returns anywhere.
But here's the odd part...on my development machine, the response from my POST request (which directly calls add) comes back as soon as the job is created. But on the deployment machine, I only get a response after the submitJob
task is completed! In other words, I've got this req
as a Future
that is seemingly completely unrelated to that submitJob
call (it certainly doesn't depend on its outcome), but in the deployment environment, Async
block won't return until submitJob
is complete (even though req
is fulfilled almost immediately).
What the heck is going on here? I've put println
statements all through the code. It definitely gets past that req.get
call almost immediately. But the overall (HTTP) response definitely waits until submitJob
is done. Does Async
somehow know about the submitJob
call? IF so, why does it behave differently on two different machines?
Any ideas?
Upvotes: 3
Views: 1016
Reputation: 31724
Though you have figured out, but there is an another alternative. This normally happens when you use say scala's default ThreadPool i.e. scala.concurrent.ExecutionContext.Implicits.global
. It uses a newFixedSizeThreadPool
with size as number of cores
of machine.
An alternative would be to use your own ExecutionContext
with the future calls above. i.e. in your above code, add this line:
implicit val exec = ExecutionContext.fromExecutor(Executors.newCachedThreadPool())
All your future
calls will then use the above ExecutionContext
.
Upvotes: 4
Reputation: 9421
OK, I figured it out. Oh what a pain that was. The issue was that my "fire and forget" task was a long running task. But the thread pool size on the deployment machine ended up being smaller than my development machine. As a result, this long running task dropped in the thread pool and clogged it up. So the processing of the AsyncResult
was presumably preempted by the long running calculation (it was actually blocking on a result from a long running calculation, but the bottom line is that it blocked).
After reading a really nice explanation of futures in Scala 2.10 by Heather Miller, I noticed the blocking
construct that can be used to temporarily expand the size of the thread pool for a single future. I wrapped my blocking code with a blocking
call and that did the trick. Presumably, this allocated an additional temporary worker to handle my blocking call and, as a result, the AsyncResult
stuff got processed in a timely manner.
(Note, wrapping the submitJob
call was useless because that, itself, didn't block. What I had to do was go into the submitJob
call and find the place where the actual blocking was and wrap that.)
Upvotes: 4