reactor-core
reactor-core copied to clipboard
Eager vs Lazy prefetch
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)
FTR while #2202 does not fix it, it at least provides a workaround (no prefetch mode)
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.