Repository navigation
Conversation
Motivation: Fanout publishers scan every subscription even when outstanding requests already cover buffer capacity or a subscriber has enough demand to fill it. Modification: Skip scans when capacity is fully reserved and stop once demand covers the available capacity. Add focused demand-accounting tests and benchmark finite request batches alongside unbounded demand. Result: Reduce unnecessary subscription traversal while preserving upstream request accounting, buffer limits, and subscription lifecycle behavior. Tests: - Focused Scala 2.13 behavior tests: 72 passed. - FanoutPublisherTest TCK: 31 passed, 7 standard untested cases skipped. - Focused Scala 3 behavior tests: 22 passed. - Cross-version bench-jmh compile and stream MiMa: passed. - Header generation, scoped header checks, scalafmt, diff check: passed. - SortImports scoped task: legacy rule NoSuchMethodError; native scalafmt passed. - Focused before/after JMH comparisons completed; small losses remain possible. - Full validation delegated to remote CI as requested. References: Refs apache#3730
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
After #3730 removes repeated oldest-cursor scans,
Sink.asPublisher(fanout = true)still scans every subscription to calculate upstream demand even when outstanding requests already reserve all available buffer space, or one subscriber's demand already covers that space.This is stacked on #3730 at
9bed9a0e523da8c7356f46a5928306459584c018. The temporary base branchstack/pr-3730-fanout-publisher-cursor-scanpoints to that exact commit so this PR shows only the additional change. Merge #3730 first, then rebase/retarget this PR tomainbefore merging it.Modification
Long.MaxValue.Result
The scan can visit zero subscriptions when capacity is already reserved, or stop at the first subscriber whose demand covers the capacity. Small requests can still require a full scan; this does not remove the worst-case repeated scans for many subscribers requesting one element at a time.
Exploratory JMH results, comparing the parent commit with this change using identical benchmark sources on macOS arm64 / Temurin 17.0.21 / Scala 2.13.18. One JMH thread, two forks, 2 x 1 s warmup and 3 x 1 s measurement per fork. Throughput counts input elements delivered to every subscriber, not total subscriber deliveries.
All corresponding 99.9% confidence intervals overlap. To investigate the lower single-subscriber, batch-1 mean, a separate longer comparison reversed the execution order (this change, then parent), with three forks, 5 x 1 s warmup and 5 x 2 s measurement. It measured 1,988,046 +/- 123,156 elements/s for the parent and 1,950,412 +/- 89,989 for this change, a ratio of 0.981. These intervals also overlap. A small performance loss in that workload remains possible; these measurements do not establish a universal throughput improvement or absence of regression.
Tests
SubscriberManagementSpec(8 tests), plusFanoutPublisherBehaviorSpec,ResizableMultiReaderRingBufferSpec, andFlowSpec(64 tests): passed. The new demand tests also pass against the parent implementation.stream-tests-tck / Test / testOnly org.apache.pekko.stream.tck.FanoutPublisherTest: 31 passed; 7 standard untested TCK cases skipped.SubscriberManagementSpecandFanoutPublisherBehaviorSpec: 22 passed.+bench-jmh/compileand+stream/mimaReportBinaryIssues: passed on Scala 2.13.18 and 3.3.9.sbt headerCreateAll, scoped header checks forstream,stream-tests / Test, andbench-jmh, native scalafmt, andgit diff --check: passed.sortImportsis a root command alias, so the module-scoped invocation is unavailable. The scoped equivalent (scalafixEnable; stream-tests/scalafixAll SortImports) fails with the existing rule'sNoSuchMethodError: scala.meta.package$.Input(). Incidental edits were discarded; native scalafmt, including its SortImports rewrite, passed.References
Refs #3730. This PR is the second change in the fanout publisher optimization stack.