reactor / reactor-core

Non-Blocking Reactive Foundation for the JVM

Home Page:http://projectreactor.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support Considering Individual Element Weight in Determining Buffer Boundary instead of Element Counts

moksie opened this issue · comments

Motivation

I have a use case where I need to group and accumulate incoming objects by keys obtained from the objects until the size of the accumulation in bytes reaches a predefined threshold. If adding an object exceeds the threshold it should cut off and add the new object to a new batch, and emits the old batch. Along with this size-based boundary condition, it should also consider a timeout. When the timeout elapses, it should emit the batch and start accumulating in a new one.

Here is a simplified workflow that I would like to implement -

    class BatchProvider {
	Bridge bridge;
        Consumer<Collection<String>> consumer;

        public BatchProvider(Consumer<Collection<String>> consumer) {
             this.consumer = consumer;
        }

	public void start() {
		initializePipeline();
	}

	public void batch(String key, String value) {
		bridge.emit(new Pair(key, value));
	}

	public void end() {
		bridge.complete();
	}
	
	private void initializePipeline() {
		bridge  = new Bridge<>();
                 Function<String, Integer> weightProvider = String::length;
  		Flux.<Pair>create(sink -> bridge.sink = sink)
				.groupBy(Pair::key, Pair::value)
				.flatMap(gfp -> gfp.weightedBufferTimeout(1 << 10, Duration.ofSeconds(1L), weightProvider)
						.doOnNext(c -> consumer.accept(c)))
				.subscribe();
	}

	private record Pair(String key, String value){};

	private static class Bridge<T> {
		private FluxSink<T> sink;

		void emit(T value) {sink.next(value);}
		void error(Throwable error) {sink.error(error);}
		void complete() {sink.complete();}
	}
    }

Desired solution

This would be achieved if the buffer size were generalized to consider the weight of the individual elements. By default, the weight of the elements would be 1, which would give the same effect as counting the elements as buffer size. Either bufferTimeout could be modified to generalize the boundary computation or a new function could be introduced as used in the example above.

Alternatively, bufferUntil could be modified to support timeouts.

Considered alternatives

  • bufferTimeout did not work as it does not support the dynamic buffer size determination.
  • bufferTimeout with the hack mentioned in here did not work. Could not achieve the strict size limit (as much as possible less than equal to limit).
  • bufferUntil did not work as it does not support timeouts. I could achieve timeout, but the hack would be too ugly.

Instead of implementing those ugly hacks, extending Flux would be much elegant.