reactor / reactor-core

Non-Blocking Reactive Foundation for the JVM
http://projectreactor.io
Apache License 2.0
4.87k stars 1.18k forks source link

Eager vs Lazy prefetch #1397

Open bsideup opened 5 years ago

bsideup commented 5 years ago

In current version of Reactor ( 3.2.1.RELEASE ), this code will do request(100) (because of eager prefetch):

Flux.range(0, Integer.MAX_VALUE)
        .hide()
        .log("producer")
        .concatMap(Mono::just, 100)
        .log("subscriber")
        .subscribe(new Subscriber<>() {
            @Override
            public void onSubscribe(Subscription s) {
            }

            @Override
            public void onNext(Integer integer) {

            }

            @Override
            public void onError(Throwable t) {

            }

            @Override
            public void onComplete() {

            }
        });

// 11:25:40.166  INFO   --- producer   : onSubscribe(FluxHide.HideSubscriber)
// 11:25:40.182  INFO   --- subscriber : onSubscribe(FluxConcatMap.ConcatMapImmediate)
// 11:25:40.184  INFO   --- producer   : request(100)

Suggestion: Optional prefetch mode, LAZY|EAGER. If it is LAZY, there will be no prefetch unless downstream does it's first request.

Motivation: While it's ok for most of use cases, it might be a big issue when the publisher is some IO source (e.g. Kafka, RSocket, etc), where requesting before the subscriber is ready to consume is a waste of resources and might affect the startup time of the app if there are some global publishers (e.g. message processing)

bsideup commented 3 years ago

FTR while #2202 does not fix it, it at least provides a workaround (no prefetch mode)

simonbasle commented 3 years ago

Keeping this issue (on-hold) to track possible research into different request tradeoffs in a 4.0.0

Prefetching was a tradeoff that was made at project inception, which is hard to change now. As soon as 1-to-N operators like concatMap or flatMap are used, prefetching compounds with request re-patterning these operators necessarily do and the upstream request patterns become less predictable. That's the tradeoff, in exchange for better performance without tuning.

Perhaps a different tradeoff would be made today (predictability of requests upstream, lifecycle management with deeper integration of discarding, ...) but that's such a deep change, it would effectively represent a fourth generation of Reactor.