Skip to content

Commit 393a177

Browse files
committed
concurrent readme
1 parent 50bc415 commit 393a177

File tree

1 file changed

+36
-0
lines changed
  • mug/src/main/java/com/google/mu/util/concurrent

1 file changed

+36
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
The virtual threads in Java 21 is an exciting and transformative new feature.
2+
3+
All your server code that currently uses various async frameworks with chained callbacks can be gone.
4+
5+
Already, you can write intuitive code to call outgoing rpc, wait for the result, handle any exception that may occur all in a single method. You won't be guilty of blocking precious platform thread and in turn degrading your server's throughput.
6+
7+
But sometimes when you serve a request, you need to invoke several outgoing rpcs, or read from db. Doing them sequentially results in longer request latency and you spend most of the time waiting.
8+
9+
It makes sense to do these IO-bound operations concurrently. [JEP 453](https://openjdk.org/jeps/453) is still in preview. And the API as it is still feels a bit verbose. So we created a simpler API ([Fanout](https://google.github.io/mug/apidocs/com/google/mu/util/concurrent/Fanout.html)). It focuses on the most common fanout use case where the [ShutDownOnFailure](https://docs.oracle.com/en/java/javase/20/docs/api/jdk.incubator.concurrent/jdk/incubator/concurrent/StructuredTaskScope.ShutdownOnFailure.html) stragegy is sufficient.
10+
11+
For example if you need to concurrently read from db, and invoke a rpc to fetch some data:
12+
13+
```java {.good}
14+
import static com.google.mu.util.concurrent.Fanout.concurrently;
15+
16+
...
17+
Result calculateBilling() {
18+
return concurrently(
19+
() -> readJobTimelineFromDb(...),
20+
() -> callServiceForLatestAccountInfo(...),
21+
(timeline, accountInfo) -> ...);
22+
}
23+
```
24+
25+
Up to 5 concurrent fanout operations are supported.
26+
27+
What you get:
28+
29+
* Reading from db and calling rpcs are executed concurrently in virtual threads.
30+
* Return values from the concurrent operations are passed to the lambda to be combined.
31+
* If any of the concurrent operations fails, the other concurrent operation(s) are canceled, and the exception is propagated to the main thread so you can catch and handle it.
32+
* If the main thread is interrupted while waiting for the concurrent operations, the currently ongoing concurrent operations will be canceled.
33+
34+
By default, the library uses `Executors.newVirtualThreadPerTaskExecutor()` to run the concurrent tasks in virtual threads. But sometimes your org may want to enforce extra context propagation, monitoring or profiling that require a special `Executor`. For these cases, you can create a subclass of `StructuredConcurrencyExecutorPlugin` and implement the `createExecutor()` method. Then put the class name in the `META-INF/services/com.google.mu.util.concurrent.StructuredConcurrencyExecutorPlugin` file for it to be accessible through `java.util.ServiceLoader`.
35+
36+
Optionally you can use Google [`@AutoService`](http://github.com/google/auto/tree/main/service) to automate the `META-INF` part.

0 commit comments

Comments
 (0)