Do multiple .repeats() do anything to a flux in project reactor? - java

What/is there a difference between:
flux2 = flux1.repeat().map(x -> x + 1).repeat();
and
flux2 = flux1.repeat().map(x -> x + 1);

As the Flux never completes because of the first repeat(), the second repeat() will never have a chance to resubscribe so it's basically a no-op.
However, note that this is not always the case for any two repeats within a Flux chain. If between the first and the second repeat there is an operator which would complete the publisher based on some condition (e.g. take, takeUntil), then the second repeat would make the otherwise finite Flux infinite.

Related

Does Reactor onErrorContinue operator let the original sequence continue?

The Reactor error handling documentation (https://projectreactor.io/docs/core/3.4.10/reference/index.html#error.handling) states that error-handling operators do not let the original sequence continue.
Before you learn about error-handling operators, you must keep in mind that any error in a reactive sequence is a terminal event. Even if an error-handling operator is used, it does not let the original sequence continue. Rather, it converts the onError signal into the start of a new sequence (the fallback one). In other words, it replaces the terminated sequence upstream of it.
But the javadoc for onErrorContinue states the following (https://projectreactor.io/docs/core/3.4.10/api/index.html) -
Let compatible operators upstream recover from errors by dropping the incriminating element from the sequence and continuing with subsequent elements.
Is onErrorContinue not considered an "error-handling operator"?
It does seem to allow the original sequence to continue -
Flux.range(1, 5)
.map(i -> {
if (i == 3) {
throw new RuntimeException("Forcing exception for " + i);
}
return i;
})
.doOnNext(i -> System.out.println(i))
.onErrorContinue((throwable, o) -> System.err.println("Error while processing " + o + " - " + throwable.getMessage()))
.subscribe();
Result (Dropped 3 but continued with subsequent elements)
1
2
4
5
Error while processing 3 - Forcing exception for 3
Process finished with exit code 0
The documentation does state that onErrorContinue is dependent on operator support. Is there any other way to let the original sequence (source Flux) continue that works for all operators? I dont want an alternate flux to replace my source flux in case of errors (the onErrorResume behaviour) - I just want to ignore the problem element & continue with the source flux.
EDIT 1 (My use case)
I have a reactor kafka source flux & i want to consume from it infinitely regardless of errors. I was using onErrorContinue but based on feedback received on this post, i have replaced it with onErrorResume. Below is the code i have at this point but i am not sure whether it will work in all cases (by "work", i am streaming continuously from kafka regardless of any errors). Any suggestions please?
KafkaReceiver.create(receiverOptions)
.receive()
.flatMap(record -> processRequest(record.value())
.doOnNext(e -> record.receiverOffset().acknowledge())
.doOnError(e -> {
System.err.println("Error occurred for msg: " + record.value() + ", Error " + e);
record.receiverOffset().acknowledge();
})
.onErrorResume(e -> Mono.empty()))
.repeat(() -> true)
.retryWhen(Retry.indefinitely())
.doFinally(signalType -> {
//dont expect control to ever reach here
System.err.println("KafkaReceiverFlux terminating with Signal type: " + signalType);
})
.subscribe();
The reactive streams specification, which reactor follows, states that all errors in a stream are terminal events - and this is what the reactor error handling documentation builds on. In order to handle an error, an error must have occurred, and that error must be terminal according to the spec. In all specification-compliant cases (which is nearly all total cases) this is true.
onErrorContinue() is, however, a rather special type of operator. It is an error-handling operator, but one that breaks the reactive specification by allowing the error to be dropped, and the stream to continue. It can be potentially useful in cases where you want continuous processing, to never stop, with an error side-channel.
That being said, it has a bunch of problems - not just that it requires specific operator support (because operators fully compliant with the reactive streams specification may completely disregard onErrorContinue() while still remaining compliant), but also a whole bunch of other issues. A few of us discussed these here if you're interested in some background reading. In future it's possible that it'll get moved to an unsafe() grouping or similar, but it's a very difficult problem to solve.
That being said, the core advice is that that's in the Javadoc at the moment, that being not to use onErrorContinue() in all but very specific cases, and instead using onErrorResume() on each individual publisher as so:
//Stream
.flatMap(id -> repository.retrieveById(id)
.doOnError(System.err::println)
.onErrorResume(e -> Mono.empty()))
This introduces a greater verbosity and possibly a small performance penalty (not something I've verified), but has the advantage of being far clearer in its behaviour, not breaking the reactive streams spec, and not requiring specific operator support to work. It's what I'd recommend in pretty much every case - I personally feel the subtleties of onErrorContinue() are too complicated to reason about in most cases.

Sample all but first elements from flux in project reactor

In project reactor flux there is a sample method Flux#sample java doc. It changes flux so that it emits events only at the ends of specified periods.
Is it possible to tweak this behaviour and achieve this : on first element - emit it instantly , start sampling with delay from 2nd up to the end. Basically I want to exclude first (and only first) element from sampling so that it is emmited without initial wait.
Would it be possible to achieve using built-in operators ? If not then does anybody have an idea how to approach this problem ?
Here is a simplest example of what I want to achieve :
Flux<String> inputFlux = Flux.just("first", "second", "third").delayElements(Duration.ofMillis(400));
Flux<String> transformed = /*do some magic with input flux*/;
StepVerifier.create(transformed)
.expectNext("first")//first should always be emmited instantly
//second arrives 400ms after first
//third arrives 400ms after second
.expectNoEvent(Duration.ofSeconds(1))
.expectNext("third")//after sample period last received element should be received
.verifyComplete();
By turning the source flux myFlux into a hot flux, you can easily achieve this:
Flux<T> myFlux;
Flux<T> sharedFlux = myFlux.publish().refCount(2);
Flux<T> first = sharedFlux.take(1);
Flux<T> sampledRest = sharedFlux.skip(1).sample(Duration.ofMillis(whatever));
return Flux.merge(first, sampledRest);
You could achieve it with Flux#sample(org.reactivestreams.Publisher<U>) method.
yourFlux.take(1)
.mergeWith(yourFlux.sample(Flux.interval(yourInterval)
.delaySubscription(yourFlux.take(1))))

RxJava - how to do repeat with takeUntil operations with two observables, one observable depending on other

I have a scenario where I need to call search api to get a list of particular item based on the search radius on map. The requirement is to show at least five results. In my case I have two Apis (two observables). First I need to call getExpandedSearchRadius() to get radius and perform second call, doSearch() with radius as parameter. Suppose the doSearchApi Call returns only 2 results I need to repeat the both api calls till I get the minimum result of 5. On each repeat getExpandedSearchRadius need to call to return a new expanded radius and perform doSearch with new radius.
Here the problem is each time the repeat() is called
getExpandedSearchRadius Api is not executing, only the second call is executing with the initial radius, resulting in same searchResponse. below is a sample that I tried.
getExpandedSearchRadius().flatMap{ radius -> doSearch(radius)}
.repeat()
.takeUntil(searchResponse.getItems().size >=5)
.map(anotherClass::displayOnMap)
You can use Observable.defer(), as it acts as an Observable factory, then, when repeat operator takes effect, builder factory .defer() creates another fresh observable and will invoke getExpandedSearchRadius() also.
See doc: (Defer Operator)
Example Code:
Observable.defer(() -> getExpandedSearchRadius())
.flatMap{ radius -> doSearch(radius)}
.repeat()
.takeUntil(searchResponse.getItems().size >=5)
.map(anotherClass::displayOnMap)

Java Reactor StepVerifier.withVirtualTime loop: repeatedly check with "expectNoEvent()", "expectNext()" and "thenAwait()"

I am learning Java 11 reactor. I have seen this example:
StepVerifier.withVirtualTime(() -> Flux.interval(Duration.ofSeconds(1)).take(3600))
.expectSubscription()
.expectNextCount(3600);
This example just checks that with a Flux<Long> which increments 1 after every second till one hour, the final count is 3600.
But, is there any way to check the counter repeatedly after every second?
I know this:
.expectNoEvent(Duration.ofSeconds(1))
.expectNext(0L)
.thenAwait(Duration.ofSeconds(1))
But I have seen no way to repeatedly check this after every second, like:
.expectNoEvent(Duration.ofSeconds(1))
.expectNext(i)
.thenAwait(Duration.ofSeconds(1))
when i increments till 3600. Is there?
PS:
I tried to add verifyComplete() at last in a long-running tests and it will never end. Do I have to? Or just ignore it?
You can achieve what you wanted by using expectNextSequence. You have to pass an Iterable and include every element you expect to arrive. See my example below:
var longRange = LongStream.range(0, 3600)
.boxed()
.collect(Collectors.toList());
StepVerifier
.withVirtualTime(() -> Flux.interval(Duration.ofSeconds(1)).take(3600))
.expectSubscription()
.thenAwait(Duration.ofHours(1))
.expectNextSequence(longRange)
.expectComplete().verify();
If you don't add verifyComplete() or expectComplete().verify() then the JUnit test won't wait until elements arrive from the flux and just terminate.
For further reference see the JavaDoc of verify():
this method will block until the stream has been terminated

RxJava 2 operator combination that delivers only the first Value from a bunch

The idea is when I call publishSubject.onNext(someValue) multiple times I need to get only one value like debounce operator does, but it delivers the last value, and I need to skip all values except first in a bunch till I stop calling onNext() for 1 sec.
I've tried to use something like throttleFirst(1000,TimeUnit.MILLISECONDS) but it's not working like debounce, it just makes windows after every delivery and after 1 sec immediate deliver next value.
Try this:
// Observable<T> stream = ...;
stream.window(stream.debounce(1, TimeUnit.Seconds))
.flatMap(w -> w.take(1));
Explanation: If I understand you correctly, you want to emit items if none have been emitted for 1 second prior. This is equivalent to getting the first element following an item debounced by 1 second. The below marble diagram may also help:
You can use the first operator. Like:
Observable.first()
It will take only the first value

Categories

Resources