A batch job logs every record it processes with `records.stream().peek(r -> log.debug("processing {}", r.id())).count();`. On Java 8 the audit log was always complete. After a JDK upgrade, the count stays correct but the log goes silent — no exceptions, nothing in the logs. Why, exactly, and what's the fix?
On Java 8, count() had to actually traverse the whole pipeline to get an answer, so peek's logging lambda ran on every element as a side effect of that traversal. Since Java 9, count() got smarter: if the source's Spliterator reports the SIZED characteristic (an ArrayList's does) and nothing in the pipeline changes the element count (peek doesn't — it only observes), count() reads the size directly off the spliterator instead of pushing every element through the pipeline at all. No element gets pushed, so peek's lambda simply never runs, and the count returned is still completely correct, because it never needed the traversal to know the answer. The fix is to stop relying on peek for anything that matters: peek is documented as existing mainly for debugging, and its Javadoc explicitly reserves the right for the JDK to skip it whenever the result can be computed without a full traversal — logging belongs in a terminal operation like forEach, or a plain loop, not in an intermediate stage the pipeline is free to bypass.