DO NOT MERGE: #34154: refactor(browser) chunk and parallelise with two gatherers - #37469
DO NOT MERGE: #34154: refactor(browser) chunk and parallelise with two gatherers#37469fabrizzio-dotCMS wants to merge 1 commit into
Conversation
Proof of concept for the Lunch & Learn, and a better one than the batching case:
BrowserAPIImpl had both halves of the gatherer API written by hand, in the same
class, twice.
The chunking was a private helper that only delegated:
private <T> List<List<T>> createChunks(List<T> list, int chunkSize) {
return Lists.partition(list, chunkSize);
}
The parallelism was a raw CompletableFuture array with a manual index, a
chunkIndex = i + 1 kept only for logging, an explicit submitter, allOf, and a
second loop to join. hydrateContentletsInParallel did the same with a future
list, and its comment said "Collect results maintaining order" -- a property it
got from iterating the futures in creation order.
Both become one expression. windowFixed cuts, emitting the short final chunk
itself; mapConcurrent runs the loaders on virtual threads and PROMISES input
order, which is exactly what parallelStream() does not.
Net -31 lines (+80/-111). Five unit tests, no database.
What this deliberately gives up, and the reason it stays DO NOT MERGE:
mapConcurrent has no timeout of any kind. The old shape carried orTimeout(90s)
per chunk and a 180s ceiling on the whole set, and neither survives. Re-adding
them means a timeout inside the loader or StructuredTaskScope, still preview in
25. Documented in the helper's javadoc rather than quietly dropped.
Also worth stating: the concurrency bound is the connection pool, not the CPU.
These loaders block on a socket, so more chunks in flight than Hikari has
connections only moves the queue. MAX_CONCURRENT_CHUNKS is a config property,
not availableProcessors().
Verified: test-compile -pl :dotcms-core --am passes; 5/5 green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @fabrizzio-dotCMS's task in 2m 16s —— View job Code Review — #34154 two-gatherer refactor
This is a clean, well-argued refactor and the New Issues
Notes (non-blocking)
· |
Why this one
A better proof of concept than the batching case (#37468, now closed):
BrowserAPIImplhad bothhalves of the gatherer API written by hand, in the same class, twice.
DO NOT MERGE — see the timeouts section, which is the whole finding.
What it replaces
The chunking was a private helper whose entire body was a delegation:
The parallelism was a raw array of futures with a manual index and a counter kept only so the log
line could say which chunk it was:
And
hydrateContentletsInParalleldid the same again with a future list. Its comment read"Collect results maintaining order" — ordering it got as a side effect of iterating the futures
in creation order.
What it becomes
Two gatherers, one concern each:
windowFixedcuts and emits the short final chunk itself,mapConcurrentruns the loaders on virtual threads and promises input order — whichparallelStream()does not. Both call sites now use it.Net −31 lines (+80/−111).
What it gives up — the finding
mapConcurrenthas no timeout of any kind. The shape it replaces carriedorTimeout(90s)perchunk and a 180-second ceiling on the whole set. Neither survives.
Getting them back means either a timeout inside the loader — which puts the ceremony straight back —
or
StructuredTaskScope, still preview in Java 25. It is documented in the helper's javadoc, notquietly dropped, and it is why this stays a draft.
The second thing worth saying out loud: the concurrency bound is the connection pool, not the
CPU. These loaders block on a socket to PostgreSQL, so allowing more chunks in flight than Hikari
has connections just moves the queue from this method into the pool.
MAX_CONCURRENT_CHUNKSistherefore a config property and not
availableProcessors().Tests
Five unit tests, no database:
Thread.currentThread().isVirtual()5/5 green;
test-compile -pl :dotcms-core --ampasses.Refs #34154