reactor-core v3.4.0-M2 Release Notes
Release Date: 2020-08-11 // over 3 years ago-
๐ Reactor-Core
3.4.0.-M2
is part of2020.0.0-M2
Release Train (codenameEuropium
).This second milestone brings several important changes:
- further evolution of the way processors and sinks are obtained and used (following up on the changes initiated in M1)
- ๐ improvements of the
Context
related APIs - ๐ improvements on the
Flux
andMono
metrics
๐ This release note focuses on M2-specific changes, but M2 also contains all the changes released in 3.2.19.RELEASE, 3.3.8.RELEASE and 3.3.9.RELEASE.
โก๏ธ โ ๏ธ Update considerations and deprecations
โก๏ธ Metrics update considerations
๐ท The
name()
operator is no longer used in metrics as a tag but as a prefix for the meter names (#1928):This allows to use custom tags via the
tag()
operator, while ensuring unique enough meter names that all bear the same set of tagsThe
[name].flow.duration
meter has astatus
tag that now differentiate betweencompleted
andcompletedEmpty
(#2313)โก๏ธ This allows to detect eg. empty
Mono
s, but code relying on thecompleted
status should be updated to also takecompletedEmpty
into account.The
[name].subscribed
and[name].requested
meters have had their unit dropped (#1807):๐ The unit is part of the name in some backends like prometheus, but the unit was redundant. So the
requested amount
andsubscribers
unit suffixes have been dropped (separator may vary depending on the metrics backend).โก๏ธ Processors and sinks update considerations
The
FluxIdentityProcessor
andProcessors
classes that were introduced in M1 have been rolled back, andSinks
reworked (#2218)๐ The ultimate goal is to remove the need for processors (or greatly diminish it) and adding processor-related APIs was deemed counterproductive...
The sinks from operators (
FluxSinks
andMonoSink
increate
) no longer have a common interface with the "standalone" sinks. The hierarchy has been simplified and we now have 4 uncorrelated interfaces:FluxSink
andMonoSink
(back to what they were in 3.3, oriented towards subscription-like usage) versusSinks.One
andSinks.Many
(both oriented towards publisher-like usage).๐
Sinks
allow building the later two, with a tiered approach:- chose
many()
vsone()
vsempty()
- Then on
many()
, chose a flavor (unicast()
,multicast()
,replay()
) - Finally, fine tune the flavor to obtain a sink (eg.
Sinks.many().multicast().onBackpressureError()
)
These can also be converted to processors efficiently, as their (private) implementation also implement
FluxProcessor
/MonoProcessor
. NB: to convert from aSinks.Many
/Sinks.One
to a processor where a processor is still relevant, useFluxProcessor.fromSink
andMonoProcessor.fromSink
.The
MonoProcessor
class used to be concrete butfinal
. It is nowabstract
(#1053, #2296)๐ฆ Most of its implementation has been moved to package-private
NextProcessor
. This will allow to introduce more flavors ofMonoProcessor
, focusing that class on signaling aProcessor<A, B>
that is also aMono<B>
.A new flavor
VoidProcessor
, has been also immediately added. It backsSinks.empty()
with less overhead than the oldMonoProcessor
.Sinks.Many
andSinks.One
APIsContrary to operator sinks, these two sinks are to be used more like publisher than like subscribers.
That is to say, they are expected to be passed down
asFlux()
orasMono()
, so generally should accommodate multiple subscribers (except forSinks.many().unicast()
).As a result they expose a different and more restrained API. Most relevant methods are the
emitXxx
methods, which are not fluent but return anEmission
enum.
๐ They can be seen as a "best effort" API, indicating failure through the enum rather than an exception (comparable toQueue#offer
vsQueue#add
in the JDK).๐ฑ โ ๏ธ Consequently, the
Emission
enum should be checked to detect error cases.๐ง For instance, the
EmitterProcessor
used to sleep when its configured buffer was full and users continued trying to emit, effectively busy-looping until the internal queue would accept the pushed valued. This is effectively blocking, and can be problematic (#2049).The equivalent sink,
Sinks.many().multicast().onBackpressureBuffer()
, doesn't sleep/block but immediately returnsEmission.FAIL_OVERFLOW
.Note that to get the old behavior, one can manually loop on the
emitNext
attempt until it returnsEmission.OK
, sleeping for a few nanoseconds when it returnsFAIL_OVERFLOW
. But caller could also decide to fail after a number of retries rather than an infinite loop for instance...Context
APIsContextView
introduction (#2279)Knowing when a
Context
can be "written" vs when it cannot is complicated by the fact that operators that aim at exposing the contextual data only for reading still expose the fullContext
API, with write methods.In M2, we introduce
ContextView
, a superinterface toContext
that only bears the read methods of the context.๐ Operators that expose the context for the purpose of reading from it (and where trying to
put
orremove
data would effectively be a no-op) now have an alternative exposing theContextView
, deprecating the old way:deferWithContext(Function<Context, P>)
is superseded bydeferContextual(Function<ContextView, P>)
- we introduce
transformDeferredContextual(BiFunction<P1, ContextView, P2>)
- ๐ฆ
Signal#getContext()
is superseded bySignal#getContextView()
- ๐
Mono.subscriberContext()
is deprecated. No replacement is planned asdeferContextual(Mono::just)
is a readily available replacement.
A
Context
is already aContextView
. In case an API presents aContextView
but one really need aContext
instead, useContext.of(ContextView)
.๐ Improve naming of context operators (#2148)
๐ In 3.3, context operators are a bit confusing since they're only differentiated by their inputs. In 3.4 we deprecate these confusing names and provide alternatives:
- ๐
Mono.subscriberContext()
is to be removed. UseMono.deferContextual(Mono::just)
instead, or even better try to rewrite the code to fully utilize the capacities ofdeferContextual
andtransformDeferredContextual
operators (which could allow reading from theContextView
only once). - โ
subscriberContext(Context)
is superseded bycontextWrite(ContextView)
(which better conveys that the entries in the input are added to the currentContext
) subscriberContext(Function<Context, Context>)
is superseded bycontextWrite(Function<Context, Context>)
โก๏ธ Other update considerations
- ๐ Methods and classes deprecated in 3.3 have been removed (#2277)
- to help with the
retryWhen
migration from legacy throwable-based function, a second adapter has been added (in both M2 and 3.3 latest release):Retry.withThrowable(Function<Flux<Throwable>, Publisher<?>>)
- to help with the
- ๐
subscribe(..., Consumer<Subscription>)
variant has been deprecated (#1825):- too often, people misuse it to capture or log the
Subscription
but forget torequest
it, resulting in hangs
- too often, people misuse it to capture or log the
Mono.subscribe(valueHandler, errorHandler)
now passed errors in thevalueHandler
to theerrorHandler
(#1995)- โฑ All instances of
Scheduler
are now expected to requirestart()
call before usage (#1789)- the impact is limited as
Schedulers.Factory
-produced instances are started automatically bySchedulers
factory methods (eg.Schedulers.parallel()
or evenSchedulers.newBoundedElastic()
)
- the impact is limited as
- ๐ฆ when encountering
onError
signal AFTERonComplete
/onError
, operators will now default to only logging the extraneousThrowable
(#1431)- Same with a sequence that is
subscribe
without a error handler (#2176). - This can be tuned by setting a
Hooks.onErrorDropped
. The old behavior of throwing would cause issues and used to break the rules of Reactive Streams
- Same with a sequence that is
- All operators that take a
Duration
now do their best to deal with it in nanosecond precision (#1734)- However this means that the maximum interpretable
Duration
is nowDuration.ofNanos(Long.MAX_VALUE)
(down fromofMillis(Long.MAX_VALUE)
. This leaves us with 296 years as the maximum duration, which should be enough for all intents and purposes...
- However this means that the maximum interpretable
- โฑ
Schedulers.newElastic
andSchedulers.elastic()
are now deprecated, to be removed in 3.5
๐ฑ โจ New features and improvements
- ๐ fix #2253 redesign how MetricsRegistry is configured: one can now centrally chose the registry to use instead of micrometer's
globalRegistry
- ๐ fix #2058 identify operators with scheduler through new scannable property (#2123)
- ๐ Allow "0" prefetch value in
concatMap
(#2202) - ๐ fix #2220 Log context access in FINE(ST) level and with correct prefix
- ๐ป Lazily instantiate exception in Mono#repeatWhenEmpty (#2221)
๐ ๐ Documentation, Tests and Build
- ๐ various cleanups in code, documentation, tests and build
- [doc] Document Android 21 desugaring options (#2232)
๐ ๐ Thanks to the following contributors that also participated to this release