Reactor EmitterProcessor that only retains last n elements?

George Hawkins :

How do I create an EmitterProcessor that retains only the latest n elements, such that it also works even if there are no subscribers?

At the moment I create a processor like this:

EmitterProcessor<Integer> processor = EmitterProcessor.create();

And an external system provides temperature updates randomly throughout the day. In the callback from that system I do:

void tempConsumer(int temp) {
    processor.onNext(temp);
}

However onNext(...) blocks once processor.getBufferSize() elements have been added.

How can I create a processor that discards the oldest element, in this case, rather than blocking?

This seems to be covered to some degree in reactor-core #763. Simon Baslé first discusses a proposed change to EmitterProcessor such that when "sending data while there are NO subscribers [and] the queue contains bufferSize elements, the oldest element is dropped and the onNext is enqueued." But then in the next comment, he says "we won't go ahead with my suggested change above. We instead advise you to use the sink() rather than directly the onNext. Namely, to use the onRequest callback inside the sink() to perform exactly as many sink.next(...) as there are requests."

However, if I understand things correctly this only covers the case where you can calculate new elements on demand, e.g. like so:

FluxSink<Integer> sink = processor.sink();
Random random = new Random();

sink.onRequest(n -> random.nextInt()); // Generate next n requested elements.

But in my situation, I can't generate the latest n temperature readings on demand. Of course, I could maintain my own external bounded buffer of the latest readings and then read from that in onRequest(...) but I'm assuming Reactor can do this for me?

I presume this question is a dup - but my Google foo has failed me here.


Ricard Kollcaku's answer that one should use ReplayProcessor seems to be the right way to do things. Here is another example that I wrote to get clear in my head how to use it:

ReplayProcessor<Integer> flux = ReplayProcessor.create(Queues.SMALL_BUFFER_SIZE);
FluxSink<Integer> sink = flux.sink();

// ReplayProcessor.getBufferSize() returns unbounded,
// while CAPACITY returns the capacity of the underlying buffer.
int capacity = flux.scan(Scannable.Attr.CAPACITY);

// Add twice as many elements as the underlying buffer can take.
int count = capacity * 2;

for (int i = 0; i < count; i++) {
    sink.next(i);
}

// If `capacity` is 256, this will print value 256 thru to 511.
flux.subscribe(System.out::println);

I also found this section, in Hands-On Reactive Programming with Reactor, useful in explaining things.

Ricard Kollcaku :

You must use ReplayProcessor like this example :

 ReplayProcessor<Integer> directProcessor = ReplayProcessor.cacheLast();

    Flux.range(1, 10)
            .map(integer -> {
                directProcessor.onNext(integer);
                return integer;
            }).doOnComplete(() -> {
        directProcessor.subscribe(System.out::println);
        directProcessor.subscribe(System.out::println);
    })
            .subscribe();

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=147988&siteId=1