|
| 1 | + |
1 | 2 | [](https://search.maven.org/search?q=g:%22io.github.q3769%22%20AND%20a:%22conseq4j%22) |
2 | 3 |
|
3 | 4 | # conseq4j |
@@ -93,16 +94,12 @@ public interface ExecutorServiceFactory { |
93 | 94 | } |
94 | 95 | ``` |
95 | 96 |
|
96 | | -This API style loosely takes the form of "thread affinity". Sequence keys are used to summon executors of JDK |
97 | | -type [ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html). The same |
98 | | -sequence key always gets back the same sequential executor. All tasks of that sequence key can then be "affined" to and |
99 | | -executed sequentially by the summoned executor in the same submission order. |
| 97 | +This API style loosely takes the form of "thread affinity". Sequence keys are used to summon executors of the [ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) JDK |
| 98 | +type. The same sequence key always gets back the same sequential executor. All tasks of that sequence key can then be "affined" to and executed sequentially by the summoned executor in the same submission order. |
100 | 99 |
|
101 | | -The total number of executors concurrently available at runtime is configurable. As each executor is sequential, the |
102 | | -number of available executors equals the number of tasks that can be executed in parallel. |
| 100 | +The total number of executors concurrently available at runtime is configurable, and default to the available runtime processors of the JVM. Since each executor is sequential, the number of available executors equals the number of tasks that can be executed in parallel. |
103 | 101 |
|
104 | | -Consider using this style when the summoned executor needs to provide |
105 | | -the [syntax and semantic richness](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html#method.summary) |
| 102 | +Consider using this style when the summoned executor needs to provide the [syntax and semantic richness](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html#method.summary) |
106 | 103 | of the JDK `ExecutorService` API. |
107 | 104 |
|
108 | 105 | - Sample usage |
@@ -148,18 +145,11 @@ As with hashing, collision may occur among different sequence keys. When hash co |
148 | 145 | sequence keys are assigned to the same executor. Due to the single-thread setup, the executor still ensures the local |
149 | 146 | sequential execution order for each individual sequence key's tasks. However, unrelated tasks of different sequence |
150 | 147 | keys now assigned to the same bucket/executor may delay each other's execution inadvertently while waiting in the |
151 | | -executor's task queue. Consider this a trade-off of the executor's having the same syntax and semantic richness as a |
152 | | -JDK [ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html). |
| 148 | +executor's task queue. Consider this a trade-off of getting the syntax and semantic richness of the JDK [ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html). |
153 | 149 |
|
154 | | -To account for hash collision, conseq4j Style 1 does not support any shutdown action on the API-provided |
155 | | -executor ([ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html)) |
156 | | -instance. That is to prevent unintended task cancellation across different sequence keys. |
157 | | -The [Future](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html) instance(s) subsequently |
158 | | -returned by the executor, though, is still cancellable. The hash collision may not be an issue for workloads that are |
159 | | -asynchronous and focused on overall through-put, but is something to be aware of. |
| 150 | +To account for hash collision, this `conseq4j` style does not support any shutdown action on its provided executor ([ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html)) instances (It'd throws a runtime exception). That is to prevent unintended task cancellation across different sequence keys in the same bucket. The [Future](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html) instances subsequently produced by the executor, however, are still cancellable. The hash collision may not be an issue for workloads that are asynchronous and focused on overall through-put, but is something to be aware of. |
160 | 151 |
|
161 | | -The default general concurrency is the JVM |
162 | | -run-time's [availableProcessors](https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors--): |
| 152 | +The default general concurrency is the JVM run-time's [availableProcessors](https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors--): |
163 | 153 |
|
164 | 154 | ```jshelllanguage |
165 | 155 | ConseqFactory.instance(); |
@@ -208,12 +198,8 @@ This API style is more concise. Bypassing the JDK ExecutorService API, it servic |
208 | 198 | execution semantics holds: Tasks of the same sequence key are executed in the same submission order; tasks of different |
209 | 199 | sequence keys are managed to execute in parallel. |
210 | 200 |
|
211 | | -For versions requiring Java 21+, conseq4j Style 2 defaults to have no preset limit on the overall concurrency when |
212 | | -executing tasks; for other versions, this style's default concurrency is the number of JVM run-time' |
213 | | -s [availableProcessors](https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors--). Prefer |
214 | | -this style when the full-blown syntax and semantic support of |
215 | | -JDK [ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) is not |
216 | | -required. |
| 201 | +For versions requiring Java 21+, this `conseq4j` style defaults to have no preset limit on the overall concurrency when |
| 202 | +executing tasks; for other versions, this style's default concurrency is the number of JVM run-time [availableProcessors](https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors--). Prefer this style when the full-blown syntax and semantic support of JDK [ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) is not required by the use case. |
217 | 203 |
|
218 | 204 | - Sample usage |
219 | 205 |
|
@@ -244,17 +230,12 @@ public class MessageConsumer { |
244 | 230 | } |
245 | 231 | ``` |
246 | 232 |
|
247 | | -The implementation relies on |
248 | | -JDK's [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) to |
249 | | -achieve sequential execution of related tasks. One single pool of threads is used to facilitate the overall |
250 | | -asynchronous execution. The concurrency to execute unrelated tasks is generally limited only by the backing work |
251 | | -thread pool's capacity, or unlimited in the case of default/virtual thread mode. |
| 233 | +The implementation relies on JDK's [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) to achieve sequential execution of related tasks. One single pool of threads is used to facilitate the overall asynchronous execution. The concurrency to execute unrelated tasks is generally limited only by the backing work thread pool's capacity, or "unlimited" in the case of the default/virtual thread mode. |
252 | 234 |
|
253 | 235 | Instead of thread-affinity or bucket hashing, tasks are decoupled from their execution threads. All pooled threads are |
254 | 236 | anonymous and interchangeable to execute any tasks. Even sequential tasks of the same sequence key may be executed by |
255 | | -different threads, albeit in sequential order. When the work thread pool has idle threads available, a task awaiting |
256 | | -execution must have been blocked only by its own related task(s) of the same sequence key - as is necessary, and not |
257 | | -by unrelated tasks of different sequence keys in the same "bucket" - as is unnecessary. This can be a desired |
| 237 | +different threads, albeit in sequential order. When the work thread pool has idle threads available, yet a task is still awaiting |
| 238 | +execution, then the task must have been blocked only by its own related task(s) of the same sequence key - as is necessary, and not by unrelated tasks of different sequence keys in the same "bucket" - as is unnecessary. This can be a desired |
258 | 239 | advantage over the thread-affinity API style, at the trade-off of lesser syntax and semantic richness than the |
259 | 240 | JDK [ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html). |
260 | 241 |
|
@@ -297,18 +278,17 @@ out of order in a globally concurrent process. This implies: |
297 | 278 | received. |
298 | 279 |
|
299 | 280 | (3) The message consumer ensures that related messages are processed in the same order, e.g. by using a |
300 | | -sequence/correlation key as with this API. |
| 281 | +sequence/correlation key, as with the `conseq4j` API. |
301 | 282 |
|
302 | 283 | ### 2. Curative |
303 | 284 |
|
304 | 285 | This is more on the business rule level. Sometimes preventative measures are either not possible or not worthwhile to |
305 | 286 | pursue. By the time messages arrive at the consumer, they may be intrinsically out of order. E.g. when the messages are |
306 | | -coming in from independent producers and sources, there may be no guarantee of correct ordering in the first place. In |
307 | | -such cases, the message consumer may be able to take a curative approach, by applying business rules to restore |
| 287 | +coming in from independent producer and source systems, there may be no guarantee of correct ordering in the first place. In such cases, the message consumer may be able to take a curative approach, by applying business rules to restore |
308 | 288 | necessary order and properly handle the out-of-order messages. |
309 | 289 |
|
310 | 290 | Compared to preventative measures, corrective ones can be much more complicated in terms of design, implementation and |
311 | | -runtime performance. E.g. for each incoming event, it may help to do a stateful/historical look-up of all the data and |
| 291 | +runtime performance. E.g. for each incoming event, it may help to perform a stateful/historical look-up of all the data and |
312 | 292 | other events that are related; this forms a correlated and collective information session of the incoming event. A |
313 | 293 | comprehensive review of such session can detect and determine if the incoming event is out of order per business rules; |
314 | 294 | corrective measures can then be taken to restore the right order, among other reactive actions. This may fall into the |
|
0 commit comments