I'm working on a Java server application with the general following architecture:
- Clients make RPC requests to the server
- The RPC server (gRPC) I believe has its own thread pool for handling requests
- Requests are immediately inserted into
Thread Pool 1
for more processing - A specific request type, we'll call
Request R
, needs to run a few asynchronous tasks in parallel, judging the results to form a consensus that it will return to the client. These tasks are a bit more long running, so I use a separateThread Pool 2
to handle these requests. Importantly, eachRequest R
will need to run the same 2-3 asynchronous tasks.Thread Pool 2
therefore services ALL currently executingRequest R
's. However, aRequest R
should only be able to see and retrieve the asynchronus tasks that belong to it. - To achieve this, upon every incoming
Request R
, while its inThread Pool 1
, it will create a newCompletionService
for the request, backed byThread Pool 2
. It will submit 2-3 async tasks, and retrieve the results. These should be strictly isolated from anything else that might be running inThread Pool 2
belonging to other requests. - My questions:
- Firstly, is Java's
CompletionService
isolated? I couldn't find good documentation on this after checking the JavaDocs. In other words, if two or moreCompletionService
's are backed by the same thread pool, are any of them at risk of pulling a future belonging to anotherCompletionService
? - Secondly, is this bad practice to be creating this many
CompletionService
's for each request? Is there a better way to handle this? Of course it would be a bad idea to create a new thread pool for each request, so is there a more canonical/correct way to isolate futures within aCompletionService
or is what I'm doing okay?
- Firstly, is Java's
Thanks in advance for the help. Any pointers to helpful documentation or examles would be greatly appreciated.
Code, for reference, although trivial:
public static final ExecutorService THREAD_POOL_2 =
new ThreadPoolExecutor(16, 64, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
// Gets created to handle a RequestR, RequestRHandler is run in Thread Pool 1
public class RequestRHandler {
CompletionService<String> cs;
RequestRHandler() {
cs = new ExecutorCompletionService<>(THREAD_POOL_2);
}
String execute() {
cs.submit(asyncTask1);
cs.submit(asyncTask2);
cs.submit(asyncTask3);
// Lets say asyncTask3 completes first
Future<String> asyncTask3Result = cs.take();
// asyncTask3 result indicates asyncTask1 & asyncTask2 results don't matter, cancel them
// without checking result
// Cancels all futures, I track all futures submitted within this request and cancel them,
// so it shouldn't affect any other requests in the TP 2 pool
cancelAllFutures(cs);
return asyncTask3Result.get();
}
}