Greetings,
I have a somewhat unconventional use case for which I'm seeking advice. I already have an implementation that made sense to me based on the Play documentation and the exploration work that I did. During the code review one of my colleagues had some questions that exposed some assumptions I had made.
We have a shiny, new Play 2.3 application that makes use of WS/async for all calls. We also have a Play 1.2 application that generally blocks and
does not make use of WS/async/Promises, etc. We're migrating the APIs from the Play 1 project to the Play 2 project this year and, in an attempt to simplify this migration, I've introduced an adapter to our code which will allow the code we're migrating to make a blocking call by doing:
public T makeBlockingCall(inputs) {
Promise<T> responsePromise = makeWsCallUsingPlayLibsWs(inputs);
return responsePromise.get(SOME_ARBITRARILY_LARGE_VALUE);
}
Since the call to
responsePromise.get will block, I want to make sure that we don't tie up members of Play's default thread pool, so
I have set up a new one following the Play documentation.
My thinking was that, in order to ensure that Play's default threads are returned quickly for each request that uses these blocking operations, we would do:
public static F.Promise<Result> someActionMethod() {
final F.Promise<String> someArbitraryValueFromTheRequest = F.Promise.pure(extractAValueFromTheRequest(request());
return someArbitraryValueFromTheRequest.map(new F.Function<String, Result>() {
@Override
public Result apply(String s) throws Throwable {
return performBlockingOperation(s);
}
}, getExecutionContextForBlockingOperations());
}
This would result in
someArbitraryValueFromTheRequest
being fulfilled on the thread pool which has been created for this purpose; done and done. This is where my colleague's thinking and mine diverge.
Inside of
makeWsCallUsingPlayLibsWs(inputs)
, we do a number map operations which use
promise.map(F.Function<T> someFunction)
for boilerplate stuff like logging elapsed time, error-handling and the like. My assumption was that since the Play WS is async, it's safe to allow it to use the default thread pool since
that thread won't be tied up in the same way the thread from the blocking pool will be. His thinking is that we'd have to use the same ExecutionContext all the way down.
The code samples above illustrate my thinking. I guess his would look like this:
public T makeBlockingCall(inputs, blockingContext) {
Promise<T> responsePromise = makeWsCallUsingPlayLibsWs(inputs, blockingContext);
return responsePromise.get(SOME_ARBITRARILY_LARGE_VALUE);
}
private Promise<T> makeWsCallUsingPlayLibsWs(inputs, blockingContext) {
F.Promise<T> response = WS.url(urlFromInputs).get()
.map(ELAPSED_TIME_FUNCTION, blockingContext)
.map(BOILERPLATE_ERROR_HANDLING_FUNCION, blockingContext)
.map(WSRESPONSE_TO_T_FUNCTION, blockingContext);
return response;
}
Since this post is long enough already, I'll leave it at that. If there are questions/comments (other than, "just don't do it that way") I'll answer whatever I can.
Thank you!
Brian