Reactor-Core 3.4.0 is part of 2020.0.0 Release Train (codename Europium).
This is the first GA release of Europium 🎉
This note focuses on 3.4.0 proper, curating changes from across all milestones.
Note that the 3.4.0 effort was started right after 3.3.5.RELEASE, so this version also contains changes from 3.3.6 to 3.3.11 (see the end of the notes for links).
⚠️ Known Issues
- the
firstWithValuenew operator has an undocumented way of checking the cause of failure for individual sources, but it uses a suppressed exception rather than a cause. Please refrain from using that, as we'll switch toinitCausein3.4.1(and properly document the behavior, setting it in stone) => #2459
⚠️ Update considerations
⚠️ 🗑️ Removals
- Schedulers deprecated back in 3.3 have been removed (
WorkQueueProcessorandTopicProcessor, #2208) - Previously deprecated methods and types have been removed (see #2277)
⚠️ ⌛ Deprecations
Schedulers.elastic()and variants are deprecated, to be removed in 3.5 (#1893)FluxProcessor(as well as all concrete implementations of it) are now deprecated and will be removed in 3.5 (#2188, #2431)- see the main focus section below for more details
- [api-change]
MonoProcessoris made abstract and deprecated, to be removed in 3.5 (#2188, #1053 / f04bede, #2431)- see the main focus section below for more details
Flux.subscribe(Consumer<Subscription>)variant is deprecated, to be removed in 3.5 (#1825)firstis deprecated to be renamed tofirstWithSignal(seefirstWithValuein the new features below). The old deprecated method will be removed in 3.5 (#2173)Contextchanges (#2282):- Operators exposing the
Contextat points where there is no use attempting to use write methods are deprecated and will be removed in 3.5 (#2293, 7d6c862)- these all receive a
ContextView-based variant (see new features below) deferWithContext=>deferContextualwith aContextViewSignal.getContext()=>Signal.getContextView()
- these all receive a
- Operators modifying the
Contextare renamed to be clearer. Old names are deprecated and will be removed in 3.5 (see new feature below, #2148, eed93d6)
- Operators exposing the
⚠️ ♻️ Behavior Changes
- Most operators that take a
Durationand convert it to milliseconds now use nanosecond precision (#1734) Operators.onErrorDroppedno longer throws, but only logs the dropped exception (#1431)- When an
onErrorsignal reaches the last (lambda based) subscriber in a chain which doesn't have an error handler, the exception is no longer thrown but only logged (#2176) - Vanilla implementations of
Schedulernow initiate their backing infrastructure and applySchedulersdecorator instart()rather than the constructor (#1778)start()is also now implicitly called when instantiating aSchedulervia theSchedulers.Factory- Allows to avoid
thisleaking from constructor - ⚠️ Custom implementations should similarly initialized and optionally decorate in
start()
EmitterProcessorwith a full backpressure queue no longer blocks until data is requested, but fails fast (#2049)- Metrics changes
- Change in how metric meters/tags are named in accordance with the
nameoperator (#1928, #2302)flowtag has been removed entirely- using the
name({String})operator will replacereactor.prefix in meter names with the user-provided{String}. - ⚠️ using custom tags via the
tags(String...)operator, but notname(String)still creates a sparse set of tags in the application which could be rejected by some metrics backend
- Change name of some meters that used to include (redundant) unit names (#1807)
reactor_requested_requested_amount=>{name}_requestedreactor_subscribed_subscribers=>{name}_subscribed- notice these meters also take the user-provided
{name}into account from the above change
- Differentiate empty vs valued sequences in metrics via the
statustag, introducingcompletedEmptyvalue (#1803)
- Change in how metric meters/tags are named in accordance with the
⚠️ ✨ Main focus: Sinks
Sinks are a new API constructed to replace the FluxProcessor and MonoProcessor APIs, which have been deprecated (#2431, #1053).
We distinguish Sinks.Many<T> (a Flux-like sink), Sinks.One<T> (a Mono-like sink) and Sinks.Empty (a Mono<Void>-like sink).
These sinks are constructed through specs under the Sinks class. They expose a two-faced API:
- methods starting with
tryEmitare atomic, never throw (#2319, #2329, #2336, #2426) and immediately return anEmitResultenum (originally named Emission) indicating various variations of success or error (#2319, #2338) - methods starting with
emitattempt to provide an easier facade over the above, take anEmitFailureHandlerallowing to fine tune some aspects of the default behavior (#2377)
By default, the instances are "serialized", detecting parallel usage and failing fast in tryEmit (#2342, #2410, #2365, 19fc1ba, #2412).
The vanilla reactor sink implementations of emit API terminate with the equivalent of an emitError when parallel usage is detected (#2365) but by using the EmitFailureHandler to drive the sink to retry, possibly with a small amount of sleeping, it should be possible to optimistically get rid of the contention.
It is also possible to get a low-overhead, low-protection instance of vanilla sinks by using the Sinks.unsafe() spec (#2418).
This doesn't detect parallel usage, so it must be used in a responsible way (in a context where the Reactive Streams contract is already externally enforced).
Sinks also expose a asFlux() view (or asMono(), as relevant) for them to be passed to downstream and subscribed, and also expose a bit of state through currentSubscriberCount() getter (#2372) and by being Scannable (at a minimum for TERMINATED and CANCELLED attributes, #2394).
Compared to existing processors, most flavors have a sink equivalent except DirectProcessor, with two close cousins under Sinks.many().multicast() (#2392, #2451):
directAllOrNothingwill ignoretryEmitNextattempts when at least one subscriber doesn't have enough demand,directBestEffortwill instead push to the subset of subscribers with demand and drop from the perspective of slow subscribers.
This differs from DirectProcessor in the sense that both these sinks are kept open in such a situation, so slow subscribers that increase their request will start seeing values pushed after that (instead of being immediately terminated in DirectProcessor).
There is also a new flavor: an onBackpressureError() variant of the unicast Sink (#2347)
🐞 Bugfixes
See links to Dysprosium releases (3.3.6 to 3.3.11) at the end.
✨ New features
concatMapnow has a variant with 0 prefetch (#2202)- All
Scannableoperators answer the newAttr.RUN_STYLEattribute with aRunStyleenum (#2058, #2123)- the enum allows to identify operators that run synchronously. Attribute is accessible both at
PublisherandSubscriberlevel RunStyle.SYNCguarantees the operator doesn't change threadsRunStyle.ASYNCindicates the operator MAY change threadsRunStyle.UNKNOWN(the default) indicates there's no available information (eg. due to the operator wrapping an arbitraryPublisher)
- the enum allows to identify operators that run synchronously. Attribute is accessible both at
- A Reactor-wide Micrometer
MetricsRegistryother than the global one can now be configured (#2253, 64fd556)- Use
Metrics.MicrometerConfiguration#useRegistryfor that purpose - The above will attempt to load Micrometer classes, so Micrometer MUST be on the classpath
- Use
- New reactive context features (#2282):
Contextnow extends a simplifiedContextViewAPI which only expose the read methods (#2279, #2293)- the goal is to avoid exposing a seemingly writable (copy-on-write)
Contextwhen the only meaningful operations are read operations - exposed in
Signal#getContextView(),deferContextualoperator (variant to deferWithContext) and newtransformDeferredContextualoperator
- the goal is to avoid exposing a seemingly writable (copy-on-write)
- Added
transformDeferredContextualoperator that takes aBiFunction, allowing to access theContextViewin the transformation (#2280 then renamed in #2293)Mono.subscribe(valueConsumer, errorConsumer, ...)errorConsumernow gets notified of exceptions thrown by the valueConsumer(#1995)
- Renamed
Contextoperators with clearer names (#2148, eed93d6)Mono.subscriberContext()is not aliased, we now advise to useMono.deferContextual(Mono::just)to get the exact same behaviorsubscriberContext(Context)is replaced bycontextWrite(ContextView)(notice the use ofContextView)- subscriberContext(Function) is replaced by
contextWrite(Function)
- Added method to snapshot factory+global schedulers (#2325, #2326, f9c7993)
- use
Schedulers.setFactoryWithSnapshot(Factory)to get aSnapshotobject while replacing theFactory - reset the old factory by using
resetFrom(Snapshot) - this is used by the reactor-test
VirtualTimeSchedulerto reset previously customized factories
- use
- The
ParallelFlux.subscribe(array)method is now public to allow delegation in wrappers (#2328) - The
Retryobject driving#retryWhenoperator can now store user-provided state in the form of aContextView, which is exposed throughRetrySignal(#2312, bd8db8a) - The
GroupedFlux#key()method is now marked as@NonNull(#2397) - Added
firstWithValuefactory operator (#2173, 41c937f)- like
first, it let multiple sources compete - except it prioritizes valued sources: empty sources are considered irrelevant
- if no source completes with a value, a
NoSuchElementExceptionis thrown
- like
📈 Improvements
- Have push(emitter) delegate to push(emitter, backpressure) (#2177)
- Lazily instantiate exception in Mono#repeatWhenEmpty (#2221)
log()will log context access at FINE(ST) level and now uses the more correctcurrentContextprefix (#2220)Mono#materialize()now takes into account that the source IS aMono, so it doesn'tcancel()it when materializing itsonNext(#2424, 765bfe8)- this could be considered a behavior change, but the previous behavior was unnecessary. cancelling the result of the materialization still propagates to the source.
📖 Documentation, Tests and Build
- [doc] fix #2170 Make parallel runOn methods doc consistent about work stealing
- [doc] Document Android 21 desugaring options (#2232)
- [build] #2237 remove most compilation warnings
- [doc] fix #2136 Turn discard/errorMode javadoc tags into plain paragraphs
- [build] fix #2400 use a different name for the jcstress jar
- [build] Remove compilation warnings related to jsr305 (85da74b)
👍 Thanks to the following contributors that also participated to this release
@AayushyaVajpayee, @camsteffen, @cnabro, @hamidshahid, @Inego, @jonenst, @josemalonsom, @OlegDokuka, @robotmrv, @seants, @smaldini, @steppedreckoner, @yschimke
Links to Dysprosium release notes during 3.4.0 development effort
All changes from these releases have been forward-merged into 3.4.0: