Description
Hey,
We have a use case in which a consumer might not be able to process items fast enough than what is emitted from a source observable. I understood that in this case, a backpressure with either onBackPressureBuffer()
or onBackPressureDrop()
might be useful. In case of overflow / drop, we would like to store items to a local storage and try processing them later when the consumer in this case is again able to handle the input rate. Our consumer is actually a remote REST call which might timeout or not be available in which case we retry.
Anyways, I tried alternative ways to address the problem but I can't find a suitable way to solve it. To illustrate my testings, here is some code:
package io.reactivex;
import org.junit.Test;
import rx.Observable;
import rx.Subscriber;
import rx.schedulers.Schedulers;
public class BackPressureTest {
@Test
public void testOnBackPressureDrop() throws InterruptedException {
Observable<Integer> emitter = toObservable()
.subscribeOn(Schedulers.newThread());
emitter.onBackpressureDrop(i -> System.out.println("Dropped " + i))
.observeOn(Schedulers.computation())
.map(this::doWork)
.doOnNext(i -> System.out.println("Output " + i))
.toBlocking()
.subscribe(new SingleItemSubscriber<>());
}
@Test
public void testOnBackPressureBuffer() throws InterruptedException {
Observable<Integer> emitter = toObservable()
.subscribeOn(Schedulers.newThread());
emitter.onBackpressureBuffer(2, () -> System.out.println("Overflow"))
.observeOn(Schedulers.computation())
.map(this::doWork)
.doOnNext(i -> System.out.println("Output " + i))
.toBlocking()
.subscribe(i -> System.out.println("Subscriber received " + i));
}
private Observable<Integer> toObservable() {
return Observable.create(subscriber -> {
for (int i = 0; i < 10; i++) {
System.out.println("Emitting " + i);
subscriber.onNext(i);
try {
Thread.sleep(250);
} catch (InterruptedException e) {
subscriber.onError(e);
}
}
subscriber.onCompleted();
});
}
private int doWork(int integer) {
System.out.println("Consuming " + integer);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return integer;
}
private static class SingleItemSubscriber<T> extends Subscriber<T> {
@Override
public void onStart() {
request(1);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(T t) {
System.out.println("Subscriber received " + t);
request(1);
}
}
}
In testOnBackPressureDrop()
I would assume that after the emitter
has queued some items, it would start dropping them. However, it seems that the backpressure operation subscription gets a receive size of 128 items. 128 items in memory in this case is far too much for us and we would like to control the size of the request items.
In testOnBackPressureBuffer()
I would assume that the emitter
would overflow after emitting more than two items into the buffer.
However, in neither of the cases, I don't experience an oveflow or dropped items. Also I realized that when using onBackPressureBuffer()
it seems that in overflow, the observable emits onError()
. To me that wouldn't be an option since I want the emitter
to continue and I wan't to deal with the problem myself.
Could you please instruct me that what we are missing here or are we trying to do something that is not yet even possible, e.g. is the API missing an operator like onBackPressureBufferAndDrop(int capacity, Action1 onDrop)
?
I wrote my tests based on the documentation in https://github.com/ReactiveX/RxJava/wiki/Backpressure