@Test public void serializedSinkSingleProducer() throws Exception { WorkQueueProcessor<Integer> queueProcessor = WorkQueueProcessor.<Integer>builder() .share(false).build(); FluxSink<Integer> sink = queueProcessor.sink(); Assertions.assertThat(sink).isInstanceOf(SerializedSink.class); sink = sink.next(1); Assertions.assertThat(sink).isInstanceOf(SerializedSink.class); sink = sink.onRequest(n -> {}); Assertions.assertThat(sink).isInstanceOf(SerializedSink.class); }
@Test public void serializedSinkMultiProducerWithOnRequest() throws Exception { int count = 1000; WorkQueueProcessor<Integer> queueProcessor = WorkQueueProcessor.<Integer>builder() .share(true) .build(); TestSubscriber subscriber = new TestSubscriber(count); queueProcessor.subscribe(subscriber); FluxSink<Integer> sink = queueProcessor.sink(); AtomicInteger next = new AtomicInteger(); FluxSink<Integer> serializedSink = sink.onRequest(n -> { for (int i = 0; i < n; i++) { synchronized (s) { // to ensure that elements are in order for testing FluxSink<Integer> retSink = sink.next(next.getAndIncrement()); Assertions.assertThat(retSink).isInstanceOf(SerializedSink.class); } } }); Assertions.assertThat(serializedSink).isInstanceOf(SerializedSink.class); subscriber.await(Duration.ofSeconds(5)); sink.complete(); assertNull("Unexpected exception in subscriber", subscriber.failure); }
@Test(timeout = 5_000) public void testBufferSize1Created() throws Exception { WorkQueueProcessor<String> broadcast = WorkQueueProcessor.<String>builder() .share(true).name("share-name") .bufferSize(1) .autoCancel(true) .build(); int simultaneousSubscribers = 3000; CountDownLatch latch = new CountDownLatch(simultaneousSubscribers); Scheduler scheduler = Schedulers.single(); FluxSink<String> sink = broadcast.sink(); Flux<String> flux = broadcast.filter(Objects::nonNull) .publishOn(scheduler) .cache(1); for (int i = 0; i < simultaneousSubscribers; i++) { flux.subscribe(s -> latch.countDown()); } sink.next("data"); Assertions.assertThat(latch.await(4, TimeUnit.SECONDS)) .overridingErrorMessage("Data not received") .isTrue(); }
/** * Create a new shared WorkQueueProcessor using the passed buffer size, blockingWait * Strategy and auto-cancel. <p> A Shared Processor authorizes concurrent onNext calls * and is suited for multi-threaded publisher that will fan-in data. <p> A new Cached * ThreadExecutorPool will be implicitly created and will use the passed name to * qualify the created threads. * @param name Use a new Cached ExecutorService and assign this name to the created * threads * @param bufferSize A Backlog Size to mitigate slow subscribers * @param <E> Type of processed signals * @return a fresh processor */ public static <E> WorkQueueProcessor<E> share(String name, int bufferSize) { return WorkQueueProcessor.<E>builder().share(true).name(name).bufferSize(bufferSize).build(); }
@Test public void highRate() throws Exception { WorkQueueProcessor<String> queueProcessor = WorkQueueProcessor.<String>builder() .share(true) .name("Processor") .bufferSize(256)
@Test(timeout = 5_000) public void testBufferSize1Shared() throws Exception { WorkQueueProcessor<String> broadcast = WorkQueueProcessor.<String>builder() .share(true) .name("share-name") .bufferSize(1) .autoCancel(true) .build(); int simultaneousSubscribers = 3000; CountDownLatch latch = new CountDownLatch(simultaneousSubscribers); Scheduler scheduler = Schedulers.single(); FluxSink<String> sink = broadcast.sink(); Flux<String> flux = broadcast.filter(Objects::nonNull) .publishOn(scheduler) .cache(1); for (int i = 0; i < simultaneousSubscribers; i++) { flux.subscribe(s -> latch.countDown()); } sink.next("data"); Assertions.assertThat(latch.await(4, TimeUnit.SECONDS)) .overridingErrorMessage("Data not received") .isTrue(); }
@Test public void nonSerializedSinkMultiProducer() throws Exception { int count = 1000; WorkQueueProcessor<Integer> queueProcessor = WorkQueueProcessor.<Integer>builder() .share(true) .build(); TestSubscriber subscriber = new TestSubscriber(count); queueProcessor.subscribe(subscriber); FluxSink<Integer> sink = queueProcessor.sink(); Assertions.assertThat(sink).isNotInstanceOf(SerializedSink.class); for (int i = 0; i < count; i++) { sink = sink.next(i); Assertions.assertThat(sink).isNotInstanceOf(SerializedSink.class); } subscriber.await(Duration.ofSeconds(5)); assertNull("Unexpected exception in subscriber", subscriber.failure); }
@Test public void shareOverrideAll() { ExecutorService executor = Executors.newSingleThreadExecutor(); ExecutorService requestTaskExecutor = Executors.newSingleThreadExecutor(); int bufferSize = 1024; WaitStrategy waitStrategy = WaitStrategy.busySpin(); boolean autoCancel = false; WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .executor(executor) .requestTaskExecutor(requestTaskExecutor) .bufferSize(bufferSize) .waitStrategy(waitStrategy) .autoCancel(autoCancel) .build(); assertProcessor(processor, true, null, bufferSize, waitStrategy, autoCancel, executor, requestTaskExecutor); }
@Test public void shareDefaultExecutorOverrideAll() { String name = "nameOverride"; int bufferSize = 1024; WaitStrategy waitStrategy = WaitStrategy.busySpin(); boolean autoCancel = false; WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .name(name) .bufferSize(bufferSize) .waitStrategy(waitStrategy) .autoCancel(autoCancel) .build(); assertProcessor(processor, true, name, bufferSize, waitStrategy, autoCancel, null, null); }
@Test public void shareOverrideExecutorBufferSizeWaitStrategyAutoCancel() { ExecutorService executor = Executors.newSingleThreadExecutor(); int bufferSize = 1024; WaitStrategy waitStrategy = WaitStrategy.busySpin(); boolean autoCancel = false; WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .executor(executor) .bufferSize(bufferSize) .waitStrategy(waitStrategy) .autoCancel(autoCancel) .build(); assertProcessor(processor, true, null, bufferSize, waitStrategy, autoCancel, executor, null); }
/** * Create a new shared WorkQueueProcessor using the passed buffer size, blockingWait * Strategy and auto-cancel. <p> A Shared Processor authorizes concurrent onNext calls * and is suited for multi-threaded publisher that will fan-in data. <p> A new Cached * ThreadExecutorPool will be implicitly created and will use the passed name to * qualify the created threads. * @param name Use a new Cached ExecutorService and assign this name to the created * threads * @param bufferSize A Backlog Size to mitigate slow subscribers * @param <E> Type of processed signals * @return a fresh processor */ public static <E> WorkQueueProcessor<E> share(String name, int bufferSize) { return WorkQueueProcessor.<E>builder().share(true).name(name).bufferSize(bufferSize).build(); }
@Test public void shareOverrideNameBufferSizeWaitStrategy() { String name = "nameOverride"; int bufferSize = 1024; WaitStrategy waitStrategy = WaitStrategy.busySpin(); WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .name(name) .bufferSize(bufferSize) .waitStrategy(waitStrategy) .build(); assertProcessor(processor, true, name, bufferSize, waitStrategy, null, null, null); }
@Test public void shareOverrideExecutorBufferSizeWaitStrategy() { ExecutorService executor = Executors.newSingleThreadExecutor(); int bufferSize = 1024; WaitStrategy waitStrategy = WaitStrategy.busySpin(); WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .executor(executor) .bufferSize(bufferSize) .waitStrategy(waitStrategy) .build(); assertProcessor(processor, true, null, bufferSize, waitStrategy, null, executor, null); }
@Test public void shareOverrideExecutorBufferSizeAutoCancel() { ExecutorService executor = Executors.newSingleThreadExecutor(); int bufferSize = 1024; boolean autoCancel = false; WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .executor(executor) .bufferSize(bufferSize) .autoCancel(autoCancel) .build(); assertProcessor(processor, true, null, bufferSize, null, autoCancel, executor, null); }
@Test public void shareOverrideExecutor() { ExecutorService executor = Executors.newSingleThreadExecutor(); WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .executor(executor) .build(); assertProcessor(processor, true, null, null, null, null, executor, null); }
@Test public void shareOverrideAutoCancel() { boolean autoCancel = false; WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .autoCancel(autoCancel) .build(); assertProcessor(processor, true, null, null, null, autoCancel, null, null); }
@Test public void shareOverrideExecutorAutoCancel() { ExecutorService executor = Executors.newSingleThreadExecutor(); boolean autoCancel = false; WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .executor(executor) .autoCancel(autoCancel) .build(); assertProcessor(processor, true, null, null, null, autoCancel, executor, null); }
@Test public void shareOverrideExecutorBufferSize() { ExecutorService executor = Executors.newSingleThreadExecutor(); int bufferSize = 1024; WorkQueueProcessor<Integer> processor = WorkQueueProcessor.<Integer>builder() .share(true) .executor(executor) .bufferSize(bufferSize) .build(); assertProcessor(processor, true, null, bufferSize, null, null, executor, null); }