rxjava: Can I use retry() but with delay? - java

I am using rxjava in my Android app to handle network requests asynchronously. Now I would like to retry a failed network request only after a certain time has passed.
Is there any way to use retry() on an Observable but to retry only after a certain delay?
Is there a way to let the Observable know that is is currently being retried (as opposed to tried for the first time)?
I had a look at debounce()/throttleWithTimeout() but they seem to be doing something different.
Edit:
I think I found one way to do it, but I'd be interested in either confirmation that this is the correct way to do it or for other, better ways.
What I am doing is this: In the call() method of my Observable.OnSubscribe, before I call the Subscribers onError() method, I simply let the Thread sleep for the desired amount of time. So, to retry every 1000 milliseconds, I do something like this:
#Override
public void call(Subscriber<? super List<ProductNode>> subscriber) {
try {
Log.d(TAG, "trying to load all products with pid: " + pid);
subscriber.onNext(productClient.getProductNodesForParentId(pid));
subscriber.onCompleted();
} catch (Exception e) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e.printStackTrace();
}
subscriber.onError(e);
}
}
Since this method is running on an IO thread anyway it does not block the UI. The only problem I can see is that even the first error is reported with delay so the delay is there even if there's no retry(). I'd like it better if the delay wasn't applied after an error but instead before a retry (but not before the first try, obviously).

You can use the retryWhen() operator to add retry logic to any Observable.
The following class contains the retry logic:
RxJava 2.x
public class RetryWithDelay implements Function<Observable<? extends Throwable>, Observable<?>> {
private final int maxRetries;
private final int retryDelayMillis;
private int retryCount;
public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {
this.maxRetries = maxRetries;
this.retryDelayMillis = retryDelayMillis;
this.retryCount = 0;
}
#Override
public Observable<?> apply(final Observable<? extends Throwable> attempts) {
return attempts
.flatMap(new Function<Throwable, Observable<?>>() {
#Override
public Observable<?> apply(final Throwable throwable) {
if (++retryCount < maxRetries) {
// When this Observable calls onNext, the original
// Observable will be retried (i.e. re-subscribed).
return Observable.timer(retryDelayMillis,
TimeUnit.MILLISECONDS);
}
// Max retries hit. Just pass the error along.
return Observable.error(throwable);
}
});
}
}
RxJava 1.x
public class RetryWithDelay implements
Func1<Observable<? extends Throwable>, Observable<?>> {
private final int maxRetries;
private final int retryDelayMillis;
private int retryCount;
public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {
this.maxRetries = maxRetries;
this.retryDelayMillis = retryDelayMillis;
this.retryCount = 0;
}
#Override
public Observable<?> call(Observable<? extends Throwable> attempts) {
return attempts
.flatMap(new Func1<Throwable, Observable<?>>() {
#Override
public Observable<?> call(Throwable throwable) {
if (++retryCount < maxRetries) {
// When this Observable calls onNext, the original
// Observable will be retried (i.e. re-subscribed).
return Observable.timer(retryDelayMillis,
TimeUnit.MILLISECONDS);
}
// Max retries hit. Just pass the error along.
return Observable.error(throwable);
}
});
}
}
Usage:
// Add retry logic to existing observable.
// Retry max of 3 times with a delay of 2 seconds.
observable
.retryWhen(new RetryWithDelay(3, 2000));

Inspired by Paul's answer, and if you are not concerned with retryWhen problems stated by Abhijit Sarkar, the simplest way to delay resubscription with rxJava2 unconditionnaly is :
source.retryWhen(throwables -> throwables.delay(1, TimeUnit.SECONDS))
You may want to see more samples and explanations on retryWhen and repeatWhen.

This example works with jxjava 2.2.2:
Retry without delay:
Single.just(somePaylodData)
.map(data -> someConnection.send(data))
.retry(5)
.doOnSuccess(status -> log.info("Yay! {}", status);
Retry with delay:
Single.just(somePaylodData)
.map(data -> someConnection.send(data))
.retryWhen((Flowable<Throwable> f) -> f.take(5).delay(300, TimeUnit.MILLISECONDS))
.doOnSuccess(status -> log.info("Yay! {}", status)
.doOnError((Throwable error)
-> log.error("I tried five times with a 300ms break"
+ " delay in between. But it was in vain."));
Our source single fails if someConnection.send() fails.
When that happens, the observable of failures inside retryWhen emits the error.
We delay that emission by 300ms and send it back to signal a retry.
take(5) guarantees that our signaling observable will terminate after we receive five errors.
retryWhen sees the termination and doesn't retry after the fifth failure.

This is a solution based on Ben Christensen's snippets I saw, RetryWhen Example, and RetryWhenTestsConditional (I had to change n.getThrowable() to n for it to work). I used evant/gradle-retrolambda to make the lambda notation work on Android, but you don't have to use lambdas (although it's highly recommended). For the delay I implemented exponential back-off, but you can plug in what ever backoff logic you want there. For completeness I added the subscribeOn and observeOn operators. I'm using ReactiveX/RxAndroid for the AndroidSchedulers.mainThread().
int ATTEMPT_COUNT = 10;
public class Tuple<X, Y> {
public final X x;
public final Y y;
public Tuple(X x, Y y) {
this.x = x;
this.y = y;
}
}
observable
.subscribeOn(Schedulers.io())
.retryWhen(
attempts -> {
return attempts.zipWith(Observable.range(1, ATTEMPT_COUNT + 1), (n, i) -> new Tuple<Throwable, Integer>(n, i))
.flatMap(
ni -> {
if (ni.y > ATTEMPT_COUNT)
return Observable.error(ni.x);
return Observable.timer((long) Math.pow(2, ni.y), TimeUnit.SECONDS);
});
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);

instead of using MyRequestObservable.retry I use a wrapper function retryObservable(MyRequestObservable, retrycount, seconds) which return a new Observable that handle the indirection for the delay so I can do
retryObservable(restApi.getObservableStuff(), 3, 30)
.subscribe(new Action1<BonusIndividualList>(){
#Override
public void call(BonusIndividualList arg0)
{
//success!
}
},
new Action1<Throwable>(){
#Override
public void call(Throwable arg0) {
// failed after the 3 retries !
}});
// wrapper code
private static <T> Observable<T> retryObservable(
final Observable<T> requestObservable, final int nbRetry,
final long seconds) {
return Observable.create(new Observable.OnSubscribe<T>() {
#Override
public void call(final Subscriber<? super T> subscriber) {
requestObservable.subscribe(new Action1<T>() {
#Override
public void call(T arg0) {
subscriber.onNext(arg0);
subscriber.onCompleted();
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable error) {
if (nbRetry > 0) {
Observable.just(requestObservable)
.delay(seconds, TimeUnit.SECONDS)
.observeOn(mainThread())
.subscribe(new Action1<Observable<T>>(){
#Override
public void call(Observable<T> observable){
retryObservable(observable,
nbRetry - 1, seconds)
.subscribe(subscriber);
}
});
} else {
// still fail after retries
subscriber.onError(error);
}
}
});
}
});
}

Based on kjones answer here is Kotlin version of RxJava 2.x retry with a delay as an extension. Replace Observable to create the same extension for Flowable.
fun <T> Observable<T>.retryWithDelay(maxRetries: Int, retryDelayMillis: Int): Observable<T> {
var retryCount = 0
return retryWhen { thObservable ->
thObservable.flatMap { throwable ->
if (++retryCount < maxRetries) {
Observable.timer(retryDelayMillis.toLong(), TimeUnit.MILLISECONDS)
} else {
Observable.error(throwable)
}
}
}
}
Then just use it on observable observable.retryWithDelay(3, 1000)

retryWhen is a complicated, perhaps even buggy, operator. The official doc and at least one answer here use range operator, which will fail if there are no retries to be made. See my discussion with ReactiveX member David Karnok.
I improved upon kjones' answer by changing flatMap to concatMap and by adding a RetryDelayStrategy class. flatMap doesn't preserve order of emission while concatMap does, which is important for delays with back-off. The RetryDelayStrategy, as the name indicates, let's the user choose from various modes of generating retry delays, including back-off.
The code is available on my GitHub complete with the following test cases:
Succeeds on 1st attempt (no retries)
Fails after 1 retry
Attempts to retry 3 times but succeeds on 2nd hence doesn't retry 3rd time
Succeeds on 3rd retry
See setRandomJokes method.

Same answer as from kjones but updated to latest version
For RxJava 2.x version: ('io.reactivex.rxjava2:rxjava:2.1.3')
public class RetryWithDelay implements Function<Flowable<Throwable>, Publisher<?>> {
private final int maxRetries;
private final long retryDelayMillis;
private int retryCount;
public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {
this.maxRetries = maxRetries;
this.retryDelayMillis = retryDelayMillis;
this.retryCount = 0;
}
#Override
public Publisher<?> apply(Flowable<Throwable> throwableFlowable) throws Exception {
return throwableFlowable.flatMap(new Function<Throwable, Publisher<?>>() {
#Override
public Publisher<?> apply(Throwable throwable) throws Exception {
if (++retryCount < maxRetries) {
// When this Observable calls onNext, the original
// Observable will be retried (i.e. re-subscribed).
return Flowable.timer(retryDelayMillis,
TimeUnit.MILLISECONDS);
}
// Max retries hit. Just pass the error along.
return Flowable.error(throwable);
}
});
}
}
Usage:
// Add retry logic to existing observable.
// Retry max of 3 times with a delay of 2 seconds.
observable
.retryWhen(new RetryWithDelay(3, 2000));

Now with RxJava version 1.0+ you can use zipWith to achieve retry with delay.
Adding modifications to kjones answer.
Modified
public class RetryWithDelay implements
Func1<Observable<? extends Throwable>, Observable<?>> {
private final int MAX_RETRIES;
private final int DELAY_DURATION;
private final int START_RETRY;
/**
* Provide number of retries and seconds to be delayed between retry.
*
* #param maxRetries Number of retries.
* #param delayDurationInSeconds Seconds to be delays in each retry.
*/
public RetryWithDelay(int maxRetries, int delayDurationInSeconds) {
MAX_RETRIES = maxRetries;
DELAY_DURATION = delayDurationInSeconds;
START_RETRY = 1;
}
#Override
public Observable<?> call(Observable<? extends Throwable> observable) {
return observable
.delay(DELAY_DURATION, TimeUnit.SECONDS)
.zipWith(Observable.range(START_RETRY, MAX_RETRIES),
new Func2<Throwable, Integer, Integer>() {
#Override
public Integer call(Throwable throwable, Integer attempt) {
return attempt;
}
});
}
}

You can add a delay in the Observable returned in the retryWhen Operator
/**
* Here we can see how onErrorResumeNext works and emit an item in case that an error occur in the pipeline and an exception is propagated
*/
#Test
public void observableOnErrorResumeNext() {
Subscription subscription = Observable.just(null)
.map(Object::toString)
.doOnError(failure -> System.out.println("Error:" + failure.getCause()))
.retryWhen(errors -> errors.doOnNext(o -> count++)
.flatMap(t -> count > 3 ? Observable.error(t) : Observable.just(null).delay(100, TimeUnit.MILLISECONDS)),
Schedulers.newThread())
.onErrorResumeNext(t -> {
System.out.println("Error after all retries:" + t.getCause());
return Observable.just("I save the world for extinction!");
})
.subscribe(s -> System.out.println(s));
new TestSubscriber((Observer) subscription).awaitTerminalEvent(500, TimeUnit.MILLISECONDS);
}
You can see more examples here. https://github.com/politrons/reactive

Worked from me with
//retry with retryCount time after 1 sec of delay
observable.retryWhen(throwableFlowable -> {
return throwableFlowable.take(retryCount).delay(1, TimeUnit.SECONDS);
});

Simply do it like this:
Observable.just("")
.delay(2, TimeUnit.SECONDS) //delay
.flatMap(new Func1<String, Observable<File>>() {
#Override
public Observable<File> call(String s) {
L.from(TAG).d("postAvatar=");
File file = PhotoPickUtil.getTempFile();
if (file.length() <= 0) {
throw new NullPointerException();
}
return Observable.just(file);
}
})
.retry(6)
.subscribe(new Action1<File>() {
#Override
public void call(File file) {
postAvatar(file);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
}
});

For Kotlin & RxJava1 version
class RetryWithDelay(private val MAX_RETRIES: Int, private val DELAY_DURATION_IN_SECONDS: Long)
: Function1<Observable<out Throwable>, Observable<*>> {
private val START_RETRY: Int = 1
override fun invoke(observable: Observable<out Throwable>): Observable<*> {
return observable.delay(DELAY_DURATION_IN_SECONDS, TimeUnit.SECONDS)
.zipWith(Observable.range(START_RETRY, MAX_RETRIES),
object : Function2<Throwable, Int, Int> {
override fun invoke(throwable: Throwable, attempt: Int): Int {
return attempt
}
})
}
}

(Kotlin) I little bit improved code with exponential backoff and applied defense emitting of Observable.range():
fun testOnRetryWithDelayExponentialBackoff() {
val interval = 1
val maxCount = 3
val ai = AtomicInteger(1);
val source = Observable.create<Unit> { emitter ->
val attempt = ai.getAndIncrement()
println("Subscribe ${attempt}")
if (attempt >= maxCount) {
emitter.onNext(Unit)
emitter.onComplete()
}
emitter.onError(RuntimeException("Test $attempt"))
}
// Below implementation of "retryWhen" function, remove all "println()" for real code.
val sourceWithRetry: Observable<Unit> = source.retryWhen { throwableRx ->
throwableRx.doOnNext({ println("Error: $it") })
.zipWith(Observable.range(1, maxCount)
.concatMap { Observable.just(it).delay(0, TimeUnit.MILLISECONDS) },
BiFunction { t1: Throwable, t2: Int -> t1 to t2 }
)
.flatMap { pair ->
if (pair.second >= maxCount) {
Observable.error(pair.first)
} else {
val delay = interval * 2F.pow(pair.second)
println("retry delay: $delay")
Observable.timer(delay.toLong(), TimeUnit.SECONDS)
}
}
}
//Code to print the result in terminal.
sourceWithRetry
.doOnComplete { println("Complete") }
.doOnError({ println("Final Error: $it") })
.blockingForEach { println("$it") }
}

in the event when you need to print out the retry count,
you can use the example provided in Rxjava's wiki page https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators
observable.retryWhen(errors ->
// Count and increment the number of errors.
errors.map(error -> 1).scan((i, j) -> i + j)
.doOnNext(errorCount -> System.out.println(" -> query errors #: " + errorCount))
// Limit the maximum number of retries.
.takeWhile(errorCount -> errorCount < retryCounts)
// Signal resubscribe event after some delay.
.flatMapSingle(errorCount -> Single.timer(errorCount, TimeUnit.SECONDS));

Use retryWhen
/**
* Retry Handler Support
* #param errors
* #param predicate filter error
* #param maxTry
* #param periodStrategy
* #param timeUnit
* #return
*/
private Flowable<?> retrySupport(Flowable<Throwable> errors, Predicate<? super Throwable> predicate , Integer maxTry , Function<Long, Long> periodStrategy , TimeUnit timeUnit )
{
LongAdder errorCount = new LongAdder();
return errors
.doOnNext(e -> {
errorCount.increment();
long currentCount = errorCount.longValue();
boolean tryContinue = predicate.test(e) && currentCount < maxTry;
Logger.i("No. of errors: %d , %s", currentCount,
tryContinue ? String.format("please wait %d %s.", periodStrategy.apply(currentCount), timeUnit.name()) : "skip and throw");
if(!tryContinue)
throw e;
} )
.flatMapSingle(e -> Single.timer( periodStrategy.apply(errorCount.longValue()), timeUnit));
}
Sample
private Single<DeviceInfo> getErrorToken( String device)
{
return Single.error( new IOException( "network is disconnect!" ) );
}
//only retry when emit IOExpcetion
//delay 1s,2s,4s,8s,16s
this.getErrorToken( this.deviceCode )
.retryWhen( error -> retrySupport( error,
e-> e instanceof IOException,
5 ,
count-> (long)Math.pow(2,count-1),TimeUnit.SECONDS ) )
.subscribe( deviceInfo1 -> Logger.i( "----Get Device Info---" ) ,
e -> Logger.e( e, "On Error" ) ,
() -> Logger.i("<<<<<no more>>>>>"));

I'm a bit too late for this one, but just in case this could still be useful for someone, I created a Kotlin extension function for RxJava 2 that will retry with an exponential backoff strategy:
private fun <T> Observable<T>.retryWithExponentialBackoff(): Observable<T> {
val retriesSubject = BehaviorSubject.createDefault(0)
return doOnNext { retriesSubject.onNext(0) }
.retryWhen {
it.withLatestFrom(retriesSubject) { _, retryCount ->
retriesSubject.onNext(retryCount + 1)
retryCount
}.flatMap { retryCount ->
when (retryCount) {
MAX_RETRY_COUNT -> Observable.error(RuntimeException("Max number of retries reached"))
else -> Observable.timer(2.0.pow(retryCount).toLong(), SECONDS)
}
}
}
}

Related

How to Interrupt/Abort Rx Java Observable Chain Subscription?

I have a requirement to abort the Rx Java Observable chain, Take Until helps partially but does a graceful completion of Job. I need a handle onAborted on all the the observables part of this chain. Dispose/CompositeDispose none of them are helping to delegate the onAborted handle upward in observable.
Note: The RxJava is not being used here for UI Operations, rather to process/transform shorter data streams in memory.
dataObservable.map(row -> convertRow())
//.takeUntil(getAbortPredicate()/*return true when abort set to true*/)
.collect(Collectors.toList())
.doFinally(getResetAbortAction()/*resets abort flag*/)
.blockingGet()
The dataObservable itself here is chain of observables. I want to interrupt all such observables involved & get a handle on abort for each of them.
public class A {
private Observable<Row> observable;
private Boolean aborted;
private SomeUtilityWithResources utilty
public A(Observable<Row> observable){
this.observable = observable;
this.aborted = false;
this.utilty = SomeUtilityWithResources.Init();
}
public A mergeOp1(A aRight){
Observable<Row> merged = observable.flatMap(lr -> aRight.observable.map(rr -> utilty.merge1(lr.toList(), rr.toList())));
//When abruptly aborted I want to call utilty.resources.abort().... something like joined.OnAborted(utility.resources.abort())
//Tried OnDispose()/OnError doesn't get invoked though
//utilty.merge1 also returns an Observable
return new A(merged)
}
public A mergeOp2(A aRight){
Observable<Row> merged = observable.flatMap(lr -> aRight.observable.map(rr -> utilty.merge2(lr.toList(), rr.toList())));
//When abruptly aborted I want to call utilty.resources.abort().... something like joined.OnAborted(utility.resources.abort())
//Tried OnDispose()/OnError doesn't get invoked though
//utilty.merge2 also returns an Observable
return new A(merged)
}
public A mergeOp3(A aRight){
Observable<Row> merged = observable.flatMap(lr -> aRight.observable.map(rr -> utilty.merge3(lr.toList(), rr.toList())));
//When abruptly aborted I want to call utilty.resources.abort().... something like joined.OnAborted(utility.resources.abort())
//Tried OnDispose()/OnError doesn't get invoked though
//utilty.merge3 also returns an Observable
return new A(merged)
}
public List<Row> execWay1(){
observable.map(row -> convertRowWay1())
//.takeUntil(getAbortPredicate()/*return true when abort set to true*/)
.collect(Collectors.toList())
.doFinally(getResetAbortAction()/*resets abort flag*/)
.blockingGet()
}
public Long count(){
observable
//.takeUntil(getAbortPredicate()/*return true when abort set to true*/)
.count()
.doFinally(getResetAbortAction(action.toString()))
.blockingGet()
}
public void abort(){
this.aborted = true;
}
private Action getResetAbortAction(String action) {
return new Action() {
#Override
public void run() throws Throwable {
if (aborted.get()) {
aborted.set(false);
//LOGGER.error(action + " execution aborted successfully!");
//throw new Exception("Aborted")
}
}
};
}
private Predicate<T> getAbortPredicate() {
return new Predicate<T>() {
#Override
public boolean test(T t) throws Exception {
return aborted.get();
}
};
}
}
/* Executor would be calling:
A a = SomeUtilityWithResources.getDefaultObservableFromSomeSource();
a = a.mergeOp1.mergeOp2.mergedOp3
a.count()
//if taking longer.... a.abort() on seperate thread;
a.execWay1();

Java 8 mulithreading: How can I achieve parallelism along with a timeout for individual threads?

Summary of what I want to achieve:
I want to execute N tasks in parallel such that no individual task should run for more than two seconds (we can mark such tasks as failed). As an output I want to return the output of successful tasks and status of failed tasks as failed. Also a timeout of one task should not lead to a circuit break, i.e., other tasks execution should not stop.
Note: I am restricted to use Java 8.
I referenced this article for parallel processing. I am doing a similar kind of parallel processing as given in the example in this article:
public void parallelProcessing() {
try {
ExecutorService executorService = Executors.newWorkStealingPool(10);
List<CompletableFuture<Integer>> futuresList = new ArrayList<CompletableFuture<Integer>>();
futuresList.add(CompletableFuture.supplyAsync(()->(addFun1(10, 5)), executorService));
futuresList.add(CompletableFuture.supplyAsync(()->(subFun1(10, 5)), executorService));
futuresList.add(CompletableFuture.supplyAsync(()->(mulFun1(10, 5)), executorService));
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futuresList.toArray(new CompletableFuture[futuresList.size()]));
CompletableFuture<List<Integer>> allCompletableFuture = allFutures.thenApply(future -> futuresList.stream().map(completableFuture -> completableFuture.join())
.collect(Collectors.toList()));
CompletableFuture<List<Integer>> completableFuture = allCompletableFuture.toCompletableFuture();
List<Integer> finalList = (List<Integer>) completableFuture.get();
} catch (Exception ex) {
}
}
public static Integer addFun1(int a, int b) {
System.out.println(Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName() + i);
}
return a + b;
}
public static Integer subFun1(int a, int b) {
System.out.println(Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName() + i);
}
return a - b;
}
public static Integer mulFun1(int a, int b) {
System.out.println(Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName() + i);
}
return a * b;
}
This works fine. But I want to set a timeout for an individual thread. I know I can use an overloaded get function in the last line. But that would set the timeout for combined futures, right? E.g., if I want no individual thread should be blocked for more than 2 seconds, and if I set a 2 seconds timeout in the last line, it will be combined timeout, right?
get(long timeout, TimeUnit unit)
Here's what I want to achieve as a final outcome:
Suppose there are five threads and four complete on time, one timeout (due to running more than two seconds). In this case, I want to send the output of four threads and send the error for the fifth thread in the result.
My input/output format is in the following way:
Sample input: List<Input> each item is run in a separate thread, where each input has a uniqueIdentifier.
Sample output: List<Output> such that:
Output :{
uniqueIdentifier: // Same as input to map for which input this output was generated
result: success/fail // This Field I want to add. Currently it's not there
data: {
// From output, e.g., addFun1 and subFun1
}
}
The semantics of what you want to achieve matter very much. On one hand, you say that you want an alternative for orTimeout for Java 8; on the other hand you kind of imply that you want to drop execution of a certain CompletableFuture if it goes beyond a certain threshold.
These are very different things, because orTimeout says in the documentation:
Exceptionally completes this CompletableFuture with a TimeoutException if not otherwise completed before the given timeout.
So something like:
CompletableFuture<Integer> addAsy =
supplyAsync(() -> addFun1(10,5), executorService)
.orTimeout(5, TimeUnit.MILLISECONDS);
will result in a CompletableFuture that is completed exceptionally (assuming that addFun1 takes more than 5 ms). At the same time, this:
CompletableFuture<Void> allFutures = CompletableFuture
.allOf(futuresList.toArray(new CompletableFuture[0]));
as the documentation states in the allOf:
... If any of the given CompletableFutures complete exceptionally, then the returned CompletableFuture also does so...
means that allFutures is a CompletableFuture that is completed exceptionally too (because addAsy is).
Now, because you have this:
CompletableFuture<List<Integer>> allCompletableFuture = allFutures.thenApply(future -> {
return futuresList.stream().map(CompletableFuture::join)
.collect(Collectors.toList());
});
And again, the documentation of thenApply says:
Returns a new CompletionStage that, when this stage completes normally, is executed with this stage's result as the argument to the supplied function.
Your allFutures did not complete normally, as such this is not even called.
So you need to understand what exactly you want to achieve. For a backport of orTimeout you could start by looking here.
You still need some kind of a backport for orTimeout. I will use the method as if it already exists.
static void parallelProcessing() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<CompletableFuture<Integer>> futuresList = new ArrayList<>();
futuresList.add(CompletableFuture.supplyAsync(() -> addFun1(10,5), executorService).orTimeout(2, TimeUnit.SECONDS));
futuresList.add(CompletableFuture.supplyAsync(() -> subFun1(10,5), executorService));
futuresList.add(CompletableFuture.supplyAsync(() -> mulFun1(10,5), executorService));
CompletableFuture<Void> all = CompletableFuture.allOf(futuresList.toArray(new CompletableFuture[0]));
Map<Boolean, List<CompletableFuture<Integer>>> map =
all.thenApply(x -> both(futuresList)).exceptionally(x -> both(futuresList)).get();
List<CompletableFuture<Integer>> failed = map.get(Boolean.TRUE);
List<CompletableFuture<Integer>> ok = map.get(Boolean.FALSE);
System.out.println("failed = " + failed.size());
System.out.println("ok = " + ok.size());
}
private static Map<Boolean, List<CompletableFuture<Integer>>> both(
List<CompletableFuture<Integer>> futuresList) {
return futuresList.stream().collect(Collectors.partitioningBy(
CompletableFuture::isCompletedExceptionally
));
}
The following is a single-file mre (paste the entire code into RunParallelTasks.java and run). It is a prototype of the structure I suggested in my comment aimed to achieve the required functionality by using simple means:
import java.util.Optional;
public class RunParallelTasks {
public static void main(String[] args) {
new Thread(()->{
long duration = 3000;
Callback<Long> cb = new LongTask(duration);
Output<Long> output = new TaskExecuter<Long>().work(cb);
System.out.println( output);
}).start();
new Thread(()->{
long duration = 300;
Callback<Long> cb = new LongTask(duration);
Output<Long> output = new TaskExecuter<Long>().work(cb);
System.out.println( output);
}).start();
new Thread(()->{
long duration = 4000;
Callback<Long> cb = new LongTask(duration);
Output<Long> output = new TaskExecuter<Long>().work(cb);
System.out.println( output);
}).start();
new Thread(()->{
long duration = 1000;
Callback<Long> cb = new LongTask(duration);
Output<Long> output = new TaskExecuter<Long>().work(cb);
System.out.println( output);
}).start();
}
}
class TaskExecuter<T>{
private static final long TIMEOUT = 2000;//millis
private T value = null;
public Output<T> work(Callback<T> call){
Thread t = new Thread(()->{
value = call.work();
});
t.start();
try {
t.join(TIMEOUT);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return new Output<>(t.getId(), value == null ? Optional.empty() : Optional.of(value)) ;
}
}
interface Callback<T> {
T work();
}
class LongTask implements Callback<Long>{
private final long durationInMillis;
public LongTask(long durationInMillis) {
this.durationInMillis = durationInMillis;
}
#Override
public Long work() {
try {
Thread.sleep(durationInMillis);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return durationInMillis;
}
}
class Output<T> {
private final long id;
private boolean success = false;
private T data;
public Output(long id, Optional<T> op) {
this.id = id;
if(!op.isEmpty()) {
data = op.get();
success = true;
}
}
//todo add getters
#Override
public String toString() {
return "task "+ id+ (success ? " Completed, returned "+data : " Failed" );
}
}
I believe that in order to achieve canceling a task if execution takes too long, you need two tasks:
The task itself doing the computation
Another task, which takes care to cancel the first, if it takes too long
This is inspired by my answer here, at least for now i still have not come up with a better way to do it.
Let's say this is Output:
public class Output {
private final String uniqueIdentifier;
private final boolean result;
private final Object data;
//all arguments constructor and getters
#Override
public String toString() {
return "Output{" +
"uniqueIdentifier='" + uniqueIdentifier + '\'' +
", result=" + result +
", data=" + data +
'}';
}
}
For simplicity i'll use only add integers task from your example, wrapping it in a Supplier.
public class AddIntegerTask implements Supplier<Integer> {
private static final long NANOSECONDS_IN_SECOND = 1_000_000_000;
private final String uniqueIdentifier;
private final boolean tooLong;
private final int a;
private final int b;
public AddIntegerTask(boolean tooLong, int a, int b) {
this.uniqueIdentifier = UUID.randomUUID().toString();
this.tooLong = tooLong;
this.a = a;
this.b = b;
}
#Override
public Integer get() {
long nanoseconds = this.tooLong ? 3 * NANOSECONDS_IN_SECOND : NANOSECONDS_IN_SECOND;
long start = System.nanoTime();
long toEnd = start + nanoseconds;
//simulate long execution
while (System.nanoTime() <= toEnd) {
//check for interruption at crucial points
boolean interrupted = Thread.currentThread().isInterrupted();
if (interrupted) {
//custom exception extending RuntimeException
throw new TooLongExecutionException();
}
}
return this.a + this.b;
}
public String getUniqueIdentifier() {
return this.uniqueIdentifier;
}
}
Most important here is, that you need to check the current thread for interruption at key moments in your own implementation.
The cancel task is quite straightforward:
public class CancelTask implements Runnable {
private final Future<?> future;
public CancelTask(Future<?> future) {
this.future = future;
}
#Override
public void run() {
this.future.cancel(true);
}
}
Wrap the canceling of a Future in a Runnable, so it can be scheduled for execution with approproate delay.
And the Runnable, which will wrap the result in an Output, and will be submitted for execution:
public class MyRunnable implements Runnable {
private final Map<String, Output> outputMap;
private final AddIntegerTask calcFunction;
private final CountDownLatch latch;
public MyRunnable(Map<String, Output> outputMap, AddIntegerTask calcFunction, CountDownLatch latch) {
this.outputMap = outputMap;
this.calcFunction = calcFunction;
this.latch = latch;
}
#Override
public void run() {
String uniqueIdentifier = this.calcFunction.getUniqueIdentifier();
Output output;
try {
Integer result = this.calcFunction.get();
output = new Output(uniqueIdentifier, true, result);
} catch (TooLongExecutionException exc) {
output = new Output(uniqueIdentifier, false, null);
}
this.outputMap.replace(uniqueIdentifier, output);
this.latch.countDown();
}
}
Things to note here: CountDownLatch, it looks to me that you know the number of tasks beforehand, so it's a good choice to force main thread to wait until all tasks have finished. TooLongExecutionException is a custom exception extending RuntimeException. If the job completes, set as successful with result, if it was interrupted set to not successful without result.
And a main to combine and test all that:
public class CancelingMain {
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
int taskCount = 6;
CountDownLatch latch = new CountDownLatch(taskCount);
long start = System.nanoTime();
Map<String, Output> outputMap = new LinkedHashMap<>();
for (int i = 1; i <= taskCount; i++) {
boolean tooLong = i % 2 == 0;
AddIntegerTask task = new AddIntegerTask(tooLong, 10, 7);
outputMap.put(task.getUniqueIdentifier(), null);
MyRunnable runnable = new MyRunnable(outputMap, task, latch);
Future<?> future = executorService.submit(runnable);
//schedule cancel task to run once, 2 seconds after scheduling
executorService.schedule(new CancelTask(future), 2, TimeUnit.SECONDS);
}
latch.await();
System.out.println("execution took - " + (System.nanoTime() - start) / 1_000_000_000D);
executorService.shutdown();
outputMap.values().forEach(System.out::println);
}
}
I am using LinkedHashMap in order to keep the tasks in their order of submission.
Here's a fleshed out version of what I suggested in a comment to the question. The idea is to wrap a call to get(long timeout, TimeUnit unit) into another future. I encapsulate the required logic into a BetterFuture class, which delegates to a CompletableFuture under the hood:
import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.concurrent.CompletableFuture.runAsync;
import static java.util.stream.Stream.concat;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.stream.Stream;
public class BetterFuture<T> {
private final CompletableFuture<T> delegate;
private BetterFuture(CompletableFuture<T> delegate) {
this.delegate = delegate;
}
public static <T> BetterFuture<T> completed(T value) {
return new BetterFuture<>(completedFuture(value));
}
public static <T> BetterFuture<T> future(Executor executor, Callable<T> callable) {
CompletableFuture<T> delegate = new CompletableFuture<T>();
runAsync(() -> {
try {
delegate.complete(callable.call());
} catch (Throwable e) {
delegate.completeExceptionally(e);
}
}, executor);
return new BetterFuture<>(delegate);
}
public static <T> BetterFuture<Optional<T>> future(Executor executor, Callable<T> callable, Duration timeout) {
return future(executor, () -> future(executor, callable).get(timeout));
}
public <R> BetterFuture<R> map(Function<T, R> fn) {
return new BetterFuture<>(delegate.thenApply(fn));
}
public <R> BetterFuture<R> andThen(Function<T, BetterFuture<R>> fn) {
return new BetterFuture<>(
delegate.thenCompose(value -> fn.apply(value).delegate));
}
public static <T> BetterFuture<Stream<T>> collect(Stream<BetterFuture<T>> futures) {
return futures
.map(future -> future.map(Stream::of))
.reduce(
BetterFuture.completed(Stream.empty()),
(future1, future2) ->
future1
.andThen(stream1 ->
future2
.map(stream2 ->
concat(stream1, stream2)))
);
}
public T get() throws ExecutionException, InterruptedException {
return delegate.get();
}
public Optional<T> get(Duration timeout) throws ExecutionException, InterruptedException {
try {
return Optional.of(delegate.get(timeout.toMillis(), TimeUnit.MILLISECONDS));
} catch (TimeoutException e) {
return Optional.empty();
}
}
}
Most of the methods just delegate to the underlying CompletableFuture without adding much additional functionality.
To start an async task with a timeout, use the method
<T> BetterFuture<Optional<T>> future(Executor executor, Callable<T> callable, Duration timeout)
If a timeout occurs, it completes with empty and with an optional of T otherwise.
In addition the method
public static <T> BetterFuture<Stream<T>> collect(Stream<BetterFuture<T>> futures)
provides a convenient way for collecting a stream of futures into a future of a stream of the same type:
Stream<BetterFuture<Optional<String>>> futures = ...
BetterFuture<Stream<Optional<String>>> futureStream = BetterFuture.collect(futures);
Here's a full fledged examples where the first future times out and the second one completes successfully:
#Test
public void timeoutTest() throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
BetterFuture<Optional<String>> fa = BetterFuture.future(executor, () -> {
Thread.sleep(3000);
return "a";
}, Duration.ofSeconds(2));
BetterFuture<Optional<String>> fb = BetterFuture.future(executor, () -> {
Thread.sleep(1000);
return "b";
}, Duration.ofSeconds(2));
Stream<BetterFuture<Optional<String>>> futures = Stream.of(fa, fb);
BetterFuture<Stream<Optional<String>>> c = BetterFuture.collect(futures);
System.out.println(c.get().toList());
}
When run, it prints
[Optional.empty, Optional[b]]
As a final note, the implementation does nothing about the running threads when a timeout occurs. That is, it only times out the future but it does not interrupt the running thread. The thread will keep running in the background until in completes naturally.
It totally depends on the tasks / calculations you are running in parallel if you can really cancel them. Keep in mind that the Java Runtime is not an operating system, and you cannot forcible kill a thread like you could do with a process.
So if you want to interrupt long-running calculations, you will have to write time in a way so they regularly check if they should stop execution. For waiting on some other stuff (sleep, sync on other threads etc.) it is a totally different strategy: you can interrupt such threads and the code receives a InterruptedException that can be used to really stop the code with much less cooperation from the code.
I prepared a small example here to show you the difference:
package examples.stackoverflow.q71322315;
import java.util.concurrent.*;
public class Q71322315 {
public static final long COUNTER = 10000000000L;
public static final boolean SLEEP = false;
private static final ExecutorService taskExec = Executors.newCachedThreadPool();
public static void timedRun(Runnable r, long timeout, TimeUnit unit) throws InterruptedException {
Future<?> task = taskExec.submit(r);
try {
task.get(timeout, unit);
System.out.println("completed");
} catch (TimeoutException e) {
// task will be cancelled below
System.out.println("timeout");
} catch (ExecutionException e) {
System.out.println("exex");
// exception thrown in task; rethrow
throw new RuntimeException(e.getCause());
} finally {
// Harmless if task already completed
task.cancel(true); // interrupt if running
}
}
public static void main(String[] args) throws InterruptedException {
timedRun(new Task(SLEEP), 2000, TimeUnit.MILLISECONDS);
taskExec.shutdown();
System.out.println("finish");
}
private static class Task implements Runnable {
private final boolean sleep;
private Task(boolean sleep) {
this.sleep = sleep;
}
#Override
public void run() {
try {
if (sleep) {
Thread.sleep(5000L);
} else {
longRunningMethod(COUNTER);
}
System.out.println("Success");
} catch (Exception e) {
e.printStackTrace();
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
}
}
private void longRunningMethod(long counter) {
for (long y = 0; y < counter; y++) {
Math.sqrt(y);
}
}
}
}
The example is based on some example code of the already mentioned Java Concurrency in Practice - "7.10 Cancelling a task using Future."
The code as above performs a long running calculation that doesn't care about any interruptions. (You might have to increase the value of COUNTER, just add zeros at the end until the whole method takes longer than 2 seconds.)
You will see that you first get the "timeout" message that indicates that the task wasn't finished in the wanted timeout. But the code continues running and also prints out "finish" and "Success".
When you flip the SLEEP constant to true it uses an interruptible call to Thread.sleep() instead and the output will not contain the "Success" message.
After you managed to build a cancellable / interruptible computation you can then set up multiple threads that each execute the timedRun execution in parallel, so the tasks are started in parallel and also interrupted after the timeout.
This does not yet include the collection of the results, but instead of the sysouts for completed and timeout you can collect results or count the timed out tasks.
(And if you want to use that code in production, please clean it up very thoroughly, it has really some huge smells that should never land in any production ready code ;-)
We had similar requirement where we need capture timeout of each thread and ignore the results. Java 8 doesn't have this in built.
One of the ways we achieved it,
List<CompletableFuture<?>> futures = new ArrayList<>();
List<?> results = new ArrayList<>(); // It can be anything you collect
futures.add(asyncService.fetchMethod()
.acceptEither(
timeoutAfter(timeout, TimeUnit.SECONDS),
results:add)
.handle(
(result, ex) -> {
//Handle the timeout exception
results.add(...);
return result
});
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
private <T> CompletableFuture<T> timeoutAfter(long timeout, TimeUnit unit) {
CompletableFuture<T> result = new CompletableFuture<>();
// We need a separate executor here
scheduledExecutor.schedule(
() -> result.completeExceptionally(new TimeoutException()), timeout, unit);
);
return result;
}
Suppose you want 10 threads running and want a returned value, you can use the Callable<Boolean> interface, submit it to ExecutorService, and then get the result using Future#get returned as a Boolean.
Here is an example usage.
final int NUM_THREADS = 10;
List<Boolean> results = new ArrayList<Boolean>();
List<Callable<Boolean>> callables = new ArrayList<Callable<Boolean>>();
for(int i=0; i<NUM_THREADS; ++i)
{
callables.add(new Callable<Boolean>()
{
public Boolean call()
{
// Add your task here
return isTaskCompleted;
}
});
}
ExecutorService executorService = ExecutorService.newFixedThreadPool(NUM_THREADS); // Run 10 threads
for(Callable<Boolean> callable:callables)
{
Future<Boolean> future = executor.submit(callable);
try
{
results.add(future.get(2, TimeUnit.SECONDS)); // Timeout 2 seconds and add the result
}
catch(Exception ex)
{
results.add(false); // Set result to false if task throw TimeOutExeption
}
}
If you want more information about these classes you can read this book: O'Reilly - Learning Java, Chapter 9: Threads.
Here is what helped me.
Problem in hand: Given X seconds as timeout return the value from the Task which completes first, in case none of the Task is able to finish then return default value.
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Test {
public static void main(String[] args) {
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> func("Task 1", 1000));
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> func("Task 2", 2000));
String str = null;
try {
str = (String) CompletableFuture.anyOf(f1, f2).get(3, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
str = "Default";
e.printStackTrace();
}
System.out.println(str);
}
public static String func(String task, int sleepTime) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
return task;
}
}

How to wrap a supplier with a java future that returns a value on timeout but keeps running in the background (with option to cancel)

As an example, I have a supplier that might take time to run:
Supplier<Integer> numLoader = sneaky(() -> {
Thread.sleep(10000);
System.out.println("5 Calculated!");
return 5;
});
* sneaky is just a utility to convert to runtime exception.
I'd like to be able to do something like this:
Future<Integer> future = createFutureValueOnTimeout(-1, numLoader);
// numLoader takes 10 seconds to complete so -1 is returned.
int num = future.get(1000, TimeUnit.MILLISECONDS);
if (resourcesAreLow()) {
future.cancel(true);
}
doSomethingWithTheValue(num);
I also have a partial implementation for createFutureValueOnTimeout:
private static <V> Future<V> createFutureValueOnTimeout(V v, Supplier<V> supplier) {
CompletableFuture<V> completableFuture = CompletableFuture.supplyAsync(supplier);
return new FutureDecorator<V>(completableFuture) {
#Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException {
return completableFuture.completeOnTimeout(v, timeout, unit).get();
}
};
}
The problem is that when calling cancel, the sleep isn't interrupted.
How can I get the cancel to work?
Is there an easier way to return a value on timeout?
Complete test:
public class TimeoutTest {
#SneakyThrows
#Test
public void testTimeout() {
int loadTimeMillis = 10000;
Supplier<Integer> numLoader = () -> {
try {
// Simulate long operation
Thread.sleep(loadTimeMillis);
} catch (InterruptedException e) {
System.out.println("Interrupted! message: " + e.getMessage());
throw Lombok.sneakyThrow(e);
}
System.out.println("5 Calculated!");
return 5;
};
Future<Integer> future = createFutureValueOnTimeout(-1, numLoader);
long start = System.currentTimeMillis();
// numLoader takes 10 seconds to complete so -1 is returned.
int num = future.get(1000, TimeUnit.MILLISECONDS);
System.out.println("Got: num: " + num + ". time: " + (System.currentTimeMillis() - start));
if (resourcesAreLow()) {
future.cancel(true);
}
// Don't stop the test. Give time for the cancel to kick in.
Thread.sleep(loadTimeMillis);
System.out.println("Finished. Time: " + (System.currentTimeMillis() - start));
}
private boolean resourcesAreLow() {
return true;
}
private static <V> Future<V> createFutureValueOnTimeout(V v, Supplier<V> supplier) {
CompletableFuture<V> completableFuture = CompletableFuture.supplyAsync(supplier);
return new FutureDecorator<V>(completableFuture) {
#Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException {
return completableFuture.completeOnTimeout(v, timeout, unit).get();
}
};
}
private static class FutureDecorator<V> implements Future<V> {
private final Future<V> inner;
private FutureDecorator(Future<V> inner) {this.inner = inner;}
#Override
public boolean cancel(boolean mayInterruptIfRunning) {
return inner.cancel(mayInterruptIfRunning);
}
#Override
public boolean isCancelled() {
return inner.isCancelled();
}
#Override
public boolean isDone() {
return inner.isDone();
}
#Override
public V get() throws InterruptedException, ExecutionException {
return inner.get();
}
#Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return inner.get(timeout, unit);
}
}
}
Output: (Notice the lack of the Interrupted! message)
Got: num: -1. time: 1007
5 Calculated!
Finished. Time: 11021
You can combine the Executor/Future API which supports cancelation with CompletableFuture:
public static <R> CompletableFuture<R> withInterruptionSupport(Callable<R> c) {
CompletableFuture<R> cf = new CompletableFuture<>();
FutureTask<R> ft = new FutureTask<R>(c) {
#Override
protected void set(R v) {
super.set(v);
cf.complete(v);
}
#Override
protected void setException(Throwable t) {
super.setException(t);
cf.completeExceptionally(t);
}
};
cf.defaultExecutor().execute(ft);
cf.whenComplete((x,y) -> ft.cancel(true));
return cf;
}
Since support for interruption in the actual function typically implies dealing with InterruptedException, it’s convenient to use Callable instead of Supplier, so it is allowed to throw this exception.
The Future which supports cancelation with interruption is unconditionally canceled whenever the CompletableFuture completes, which works without problems as whenever the completion stems from the task itself, the future is already completed and the subsequent cancelation will be ignored.
This means, we do not need to distinguish between the different possibilities of completion here. Not only does completeOnTimeout work, you can also invoke cancel(…) on the CompletableFuture and it will interrupt the evaluation of the Callable (the boolean argument still is irrelevant though). Even calling complete with an alternative result without waiting for a timeout would interrupt the now-obsolete evaluation.
So, the following works:
for(int timeout: new int[] { 5, 15 }) {
System.out.println("with timeout of " + timeout);
Integer i = withInterruptionSupport(() -> {
Thread.sleep(10000);
System.out.println("5 Calculated!");
return 5;
})
.completeOnTimeout(42, timeout, TimeUnit.SECONDS)
.join();
System.out.println("got " + i);
}
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
with timeout of 5
got 42
with timeout of 15
5 Calculated!
got 5

Creating a Flowable that emits items at a limited rate to avoid the need to buffer events

I've got a data access object that passes each item in a data source to a consumer:
public interface Dao<T> {
void forEachItem(Consumer<T> item);
}
This always produces items in a single threaded way - I can't currently change this interface.
I wanted to create a Flowable from this interface:
private static Flowable<String> flowable(final Dao dao) {
return Flowable.create(emitter -> {
dao.forEachItem(item ->
emitter.onNext(item));
emitter.onComplete();
}, ERROR);
}
If I use this Flowable in a situation where the processing takes longer than the rate at which items are emitted then I understandably get a missing back pressure exception as I am using ERROR mode:
Dao<String> exampleDao =
itemConsumer ->
IntStream.range(0, 1_000).forEach(i ->
itemConsumer.accept(String.valueOf(i)));
flowable(exampleDao)
.map(v -> {
Thread.sleep(100);
return "id:" + v;
})
.blockingSubscribe(System.out::println);
I don't wish to buffer items - seems like this could lead to exhausting memory on very large data sets - if the operation is significantly slower than the producer.
I was hoping there would be a backpressure mode that would allow the emitter to block when passed next/completion events when it detects back pressure but that does not seem to be the case?
In my case as I know that the dao produces items in a single threaded way I thought I would be able to do something like:
dao.forEachItem(item -> {
while (emitter.requested() == 0) {
waitABit();
}
emitter.onNext(item)
});
but this seems to hang forever.
How wrong is my approach? :-) Is there a way of producing items in a way that respects downstream back pressure given my (relatively restrictive) set of circumstances?
I know I could do this with a separate process writing to a queue and then write a Flowable based on consuming from that queue- would that be the preferred approach instead?
Check the part of the Flowable, especially the part with Supscription.request(long). I hope that gets you on the right way.
The TestProducerfrom this example produces Integerobjects in a given range and pushes them to its Subscriber. It extends the Flowable<Integer> class. For a new subscriber, it creates a Subscription object whose request(long) method is used to create and publish the Integer values.
It is important for the Subscription that is passed to the subscriber that the request() method which calls onNext()on the subscriber can be recursively called from within this onNext() call. To prevent a stack overflow, the shown implementation uses the outStandingRequests counter and the isProducing flag.
class TestProducer extends Flowable<Integer> {
static final Logger logger = LoggerFactory.getLogger(TestProducer.class);
final int from, to;
public TestProducer(int from, int to) {
this.from = from;
this.to = to;
}
#Override
protected void subscribeActual(Subscriber<? super Integer> subscriber) {
subscriber.onSubscribe(new Subscription() {
/** the next value. */
public int next = from;
/** cancellation flag. */
private volatile boolean cancelled = false;
private volatile boolean isProducing = false;
private AtomicLong outStandingRequests = new AtomicLong(0);
#Override
public void request(long n) {
if (!cancelled) {
outStandingRequests.addAndGet(n);
// check if already fulfilling request to prevent call between request() an subscriber .onNext()
if (isProducing) {
return;
}
// start producing
isProducing = true;
while (outStandingRequests.get() > 0) {
if (next > to) {
logger.info("producer finished");
subscriber.onComplete();
break;
}
subscriber.onNext(next++);
outStandingRequests.decrementAndGet();
}
isProducing = false;
}
}
#Override
public void cancel() {
cancelled = true;
}
});
}
}
The Consumer in this example extends DefaultSubscriber<Integer> and on start and after consuming an Integer requests the next one. On consuming the Integer values, there is a little delay, so the backpressure will be built up for the producer.
class TestConsumer extends DefaultSubscriber<Integer> {
private static final Logger logger = LoggerFactory.getLogger(TestConsumer.class);
#Override
protected void onStart() {
request(1);
}
#Override
public void onNext(Integer i) {
logger.info("consuming {}", i);
if (0 == (i % 5)) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
// can be ignored, just used for pausing
}
}
request(1);
}
#Override
public void onError(Throwable throwable) {
logger.error("error received", throwable);
}
#Override
public void onComplete() {
logger.info("consumer finished");
}
}
in the following main method of a test class the producer and consumer are created and wired up:
public static void main(String[] args) {
try {
final TestProducer testProducer = new TestProducer(1, 1_000);
final TestConsumer testConsumer = new TestConsumer();
testProducer
.subscribeOn(Schedulers.computation())
.observeOn(Schedulers.single())
.blockingSubscribe(testConsumer);
} catch (Throwable t) {
t.printStackTrace();
}
}
When running the example, the logfile shows that the consumer runs continuously, while the producer only gets active when the internal Flowable buffer of rxjava2 needs to be refilled.

Cancel task on timeout in RxJava

I'm experimenting with RxJava and Java 8's CompletableFuture class
and do not quite get how to handle timeout conditions.
import static net.javacrumbs.futureconverter.java8rx.FutureConverter.toObservable;
// ...
Observable<String> doSomethingSlowly() {
CompletableFuture<PaymentResult> task = CompletableFuture.supplyAsync(() -> {
// this call may be very slow - if it takes too long,
// we want to time out and cancel it.
return processor.slowExternalCall();
});
return toObservable(task);
}
// ...
doSomethingSlowly()
.single()
.timeout(3, TimeUnit.SECONDS, Observable.just("timeout"));
This basically works (if the timeout of three seconds is reached, "timeout" is published). I would however additionally want to cancel the future task that I've wrapped in an Observable - is that possible with an RxJava-centric approach?
I know that one option would be to handle the timeout myself, using task.get(3, TimeUnit.SECONDS), but I wonder if it's possible to do all task handling stuff in RxJava.
Yes, you can do this. You would add a Subscription to the Subscriber.
This allows you to listen in on unsubscriptions, which will happen if you explicitly call subscribe().unsubscribe() or if the Observable completes successfully or with an error.
If you see an unsubscription before the future has completed, you can assume it's because of either an explicit unsubscribe or a timeout.
public class FutureTest {
public static void main(String[] args) throws IOException {
doSomethingSlowly()
.timeout(1, TimeUnit.SECONDS, Observable.just("timeout"))
.subscribe(System.out::println);
System.in.read(); // keep process alive
}
private static Observable<String> doSomethingSlowly() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
return "Something";
});
return toObservable(future);
}
private static <T> Observable<T> toObservable(CompletableFuture<T> future) {
return Observable.create(subscriber -> {
subscriber.add(new Subscription() {
private boolean unsubscribed = false;
#Override
public void unsubscribe() {
if (!future.isDone()){
future.cancel(true);
}
unsubscribed = true;
}
#Override
public boolean isUnsubscribed() {
return unsubscribed;
}
});
future.thenAccept(value -> {
if (!subscriber.isUnsubscribed()){
subscriber.onNext(value);
subscriber.onCompleted();
}
}).exceptionally(throwable -> {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(throwable);
}
return null;
});
});
}
}

Categories