@Test public void bounded() { ReplayProcessor<Integer> rp = ReplayProcessor.create(16, false); AssertSubscriber<Integer> ts = AssertSubscriber.create(0L); rp.subscribe(ts); rp.onNext(1); rp.onNext(2); rp.onNext(3); rp.onComplete(); Assert.assertFalse("Has subscribers?", rp.hasDownstreams()); ts.assertNoValues(); ts.request(1); ts.assertValues(1); ts.request(2); ts.assertValues(1, 2, 3) .assertNoError() .assertComplete(); }
/** * Create a {@link ReplayProcessor} that caches the last element it has pushed, * replaying it to late subscribers. This is a buffer-based ReplayProcessor with * a history size of 1. * <p> * <img class="marble" src="https://raw.githubusercontent.com/reactor/reactor-core/v3.1.3.RELEASE/src/docs/marble/replaylast.png" * alt=""> * * @param <T> the type of the pushed elements * * @return a new {@link ReplayProcessor} that replays its last pushed element to each new * {@link Subscriber} */ public static <T> ReplayProcessor<T> cacheLast() { return cacheLastOrDefault(null); }
@Test public void echo() throws Exception { int count = 100; Flux<String> input = Flux.range(1, count).map(index -> "msg-" + index); ReplayProcessor<Object> output = ReplayProcessor.create(count); this.client.execute(getUrl("/echo"), session -> session .send(input.map(session::textMessage)) .thenMany(session.receive().take(count).map(WebSocketMessage::getPayloadAsText)) .subscribeWith(output) .then()) .block(TIMEOUT); assertEquals(input.collectList().block(TIMEOUT), output.collectList().block(TIMEOUT)); }
/** * Create a {@link ReplayProcessor} that caches the last element it has pushed, * replaying it to late subscribers. If a {@link Subscriber} comes in <b>before</b> * any value has been pushed, then the {@code defaultValue} is emitted instead. * This is a buffer-based ReplayProcessor with a history size of 1. * <p> * <img class="marble" src="https://raw.githubusercontent.com/reactor/reactor-core/v3.1.3.RELEASE/src/docs/marble/replaylastd.png" * alt=""> * * @param value a default value to start the sequence with in case nothing has been * cached yet. * @param <T> the type of the pushed elements * * @return a new {@link ReplayProcessor} that replays its last pushed element to each new * {@link Subscriber}, or a default one if nothing was pushed yet */ public static <T> ReplayProcessor<T> cacheLastOrDefault(@Nullable T value) { ReplayProcessor<T> b = create(1); if (value != null) { b.onNext(value); } return b; }
private void emitValues() { emitter1.onNext(1L); emitter2.onNext(2L); emitter1.onNext(3L); emitter2.onNext(4L); emitter1.onComplete(); emitter2.onComplete(); }
@Test public void scanProcessor() { ReplayProcessor<String> test = ReplayProcessor.create(16, false); Subscription subscription = Operators.emptySubscription(); test.onSubscribe(subscription); assertThat(test.scan(Scannable.Attr.PARENT)).isEqualTo(subscription); assertThat(test.scan(Scannable.Attr.CAPACITY)).isEqualTo(16); assertThat(test.scan(Scannable.Attr.TERMINATED)).isFalse(); assertThat(test.scan(Scannable.Attr.ERROR)).isNull(); test.onError(new IllegalStateException("boom")); assertThat(test.scan(Scannable.Attr.ERROR)).hasMessage("boom"); assertThat(test.scan(Scannable.Attr.TERMINATED)).isTrue(); }
/** * Create a new {@link ReplayProcessor} that replays an unbounded number of elements, * using a default internal {@link Queues#SMALL_BUFFER_SIZE Queue}. * * @param <E> the type of the pushed elements * * @return a new {@link ReplayProcessor} that replays the whole history to each new * {@link Subscriber}. */ public static <E> ReplayProcessor<E> create() { return create(Queues.SMALL_BUFFER_SIZE, true); }
@Test public void boundedFused() { ReplayProcessor<Integer> rp = ReplayProcessor.create(16, false); for (int i = 0; i < 256; i++) { rp.onNext(i); } rp.onComplete(); StepVerifier.create(rp) .expectFusion(Fuseable.ASYNC) .expectNextCount(256) .verifyComplete(); }
@Test public void echo() throws Exception { int count = 100; Flux<String> input = Flux.range(1, count).map(index -> "msg-" + index); ReplayProcessor<Object> output = ReplayProcessor.create(count); client.execute(getUrl("/echo"), session -> { logger.debug("Starting to send messages"); return session .send(input.doOnNext(s -> logger.debug("outbound " + s)).map(s -> session.textMessage(s))) .thenMany(session.receive().take(count).map(WebSocketMessage::getPayloadAsText)) .subscribeWith(output) .doOnNext(s -> logger.debug("inbound " + s)) .then() .doOnSuccessOrError((aVoid, ex) -> logger.debug("Done with " + (ex != null ? ex.getMessage() : "success"))); }) .block(Duration.ofMillis(5000)); assertEquals(input.collectList().block(Duration.ofMillis(5000)), output.collectList().block(Duration.ofMillis(5000))); }
@Test public void boundedLong() { ReplayProcessor<Integer> rp = ReplayProcessor.create(16, false); for (int i = 0; i < 256; i++) { rp.onNext(i); } rp.onComplete(); StepVerifier.create(rp.hide()) .expectNextCount(16) .verifyComplete(); }
@Test public void checkHotSource() { ReplayProcessor<Long> processor = ReplayProcessor.create(1); processor.onNext(1L); processor.onNext(2L); processor.onNext(3L); StepVerifier.create(processor.switchOnFirst((s, f) -> f.filter(v -> v % s.get() == 0))) .expectNext(3L) .then(() -> { processor.onNext(4L); processor.onNext(5L); processor.onNext(6L); processor.onNext(7L); processor.onNext(8L); processor.onNext(9L); processor.onComplete(); }) .expectNext(6L, 9L) .verifyComplete(); } }
@Test public void timedAndBound() throws Exception { ReplayProcessor<Integer> rp = ReplayProcessor.createSizeAndTimeout(5, Duration.ofSeconds(1)); for (int i = 0; i < 10; i++) { rp.onNext(i); } VirtualTimeScheduler.get().advanceTimeBy(Duration.ofSeconds(2)); for (int i = 10; i < 20; i++) { rp.onNext(i); } rp.onComplete(); StepVerifier.create(rp.hide()) .expectFusion(Fuseable.NONE) .expectNext(15,16,17,18,19) .verifyComplete(); Assert.assertFalse("Has subscribers?", rp.hasDownstreams()); }
@Test public void analyticsTest() throws Exception { ReplayProcessor<Integer> source = ReplayProcessor.create(); long avgTime = 50l; Mono<Long> result = source .log("delay") .publishOn(asyncGroup) .delayElements(Duration.ofMillis(avgTime)) .elapsed() .skip(1) .groupBy(w -> w.getT1()) .flatMap(w -> w.count().map(c -> Tuples.of(w.key(), c))) .log("elapsed") .collectSortedList(Comparator.comparing(Tuple2::getT1)) .flatMapMany(Flux::fromIterable) .reduce(-1L, (acc, next) -> acc > 0l ? ((next.getT1() + acc) / 2) : next.getT1()) .log("reduced-elapsed") .cache(); source.subscribe(); for (int j = 0; j < 10; j++) { source.onNext(1); } source.onComplete(); Assert.assertTrue(result.block(Duration.ofSeconds(5)) >= avgTime * 0.6); }
ReplayProcessor<Integer> source = ReplayProcessor.create(); Scheduler scheduler = Schedulers.newParallel("test", 2); Mono<List<Integer>> res = source.subscribeOn(scheduler) .delaySubscription(Duration.ofMillis(1L)) .log("streamed") source.onNext(1); source.onNext(2); source.onNext(3); source.onNext(4); source.onComplete();
@Test public void timedAndBoundError() throws Exception { ReplayProcessor<Integer> rp = ReplayProcessor.createSizeAndTimeout(5, Duration.ofSeconds(1)); for (int i = 0; i < 10; i++) { rp.onNext(i); } VirtualTimeScheduler.get().advanceTimeBy(Duration.ofSeconds(2)); for (int i = 10; i < 20; i++) { rp.onNext(i); } rp.onError(new Exception("test")); StepVerifier.create(rp.hide()) .expectFusion(Fuseable.NONE) .expectNext(15,16,17,18,19) .verifyErrorMessage("test"); Assert.assertFalse("Has subscribers?", rp.hasDownstreams()); }
@Test public void cancel() { ReplayProcessor<Integer> rp = ReplayProcessor.create(16, false); AssertSubscriber<Integer> ts = AssertSubscriber.create(); rp.subscribe(ts); ts.cancel(); Assert.assertFalse("Has subscribers?", rp.hasDownstreams()); }