Keep interval Observable running in Java - java

I am new to threading in Java and was wondering how I can keep the following Observable running,
private void init() {
System.out.println("Running...");
Observable o = Observable.interval(5, TimeUnit.SECONDS)
.flatMap(o -> serverService.listServers("all"))
.retryWhen(o -> o.flatMap(s -> Observable.timer(30, TimeUnit.SECONDS)))
.subscribe(serverModels -> System.out.println("onNext"),
e -> System.out.println("ERROR " + e),
() -> System.out.println("Completed"));
}
If I call this method from main(), the process exits immediately. How do I refrain from a process exit?

Sleeping can cause bugs. Instead checkout the blockingSubscribe operator.

Related

Has CompletableFuture.allOf() any advantage over a loop with CompletableFuture.join() when just waiting for completion?

I am making multiple async calls to my database. I store all those async calls on a List<CompletableFuture<X>> list. I want to collect all the results together, so I need to wait for all of those calls to complete.
One way is to create a CompletableFuture.allOf(list.toArray(...))...
Another way is to use: list.stream.map(cf -> cf.join())...
I was just wondering if there are any advantages of creating the global CompletableFuture and waiting for it to complete (when all the individual CompletableFuture complete) over directly waiting for the individual CompletableFutures to complete.
The main thread gets blocked either way.
static CompletableFuture<Void> getFailingCF() {
return CompletableFuture.runAsync(() -> {
System.out.println("getFailingCF :: Started getFailingCF.. ");
throw new RuntimeException("getFailingCF:: Failed");
});
}
static CompletableFuture<Void> getOkCF() {
return CompletableFuture.runAsync(() -> {
System.out.println("getOkCF :: Started getOkCF.. ");
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3));
System.out.println("getOkCF :: Completed getOkCF.. ");
});
}
public static void main(String[] args) {
List<CompletableFuture<Void>> futures = new ArrayList<>();
futures.add(getFailingCF());
futures.add(getOkCF());
// using CompletableFuture.allOf
var allOfCF = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
allOfCF.join();
// invoking join on individual CF
futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
In the code snippet above, the difference lies in handling exception: The CompletableFuture.allOf(..) wraps any exception thrown by any of the CompletableFutures while allowing rest of the threads (executing the CompletableFuture) continue their execution.
The list.stream.map(cf -> cf.join())... way immediately throws the exception and terminates the app (and all threads executing the CFs in the list).
Note that invoking join() on allOf throws the wrapped exception, too. It will also terminate the app. But, by this time, unlike list.stream.map(cf -> cf.join())..., the rest of the threads have completed their processing.
allOfCF.whenComplete(..) is one of the graceful ways to handle the execution result (normal or exceptional) of all the CFs:
allOfCF.whenComplete((v, ex) -> {
System.out.println("In whenComplete...");
System.out.println("----------- Exception Status ------------");
System.out.println(" 1: " + futures.get(0).isCompletedExceptionally());
System.out.println(" 2: " + futures.get(1).isCompletedExceptionally());
});
In the list.stream.map(cf -> cf.join())... way, one needs to wrap the join() call in try/catch.

How to add futures by for loop for CompletableFuture.allOf?

How can I add futures by CompletableFuture.allOf() when the futures are created within a for-loop? I want to create a bunch of futures which should be executed in parallel. And only when all futures are completed the method should return the result:
// Version 1: execute each task in async and return alls tasks when finished
public Set<Task> getTasks(){
var executor = Executors.newCachedThreadPool();
var tasks = new LinkedHashSet<Task>();
var futures = new ArrayList<CompletableFuture<Set<Task>>>();
for (var task : user.getTasks()) {
// all futures are executed in parallel
futures.add(CompletableFuture.supplyAsync(() -> execute(task), executor));
}
for (var f : futures) {
// this will block as long as each future is finished
tasks.addAll(f.join());
}
return tasks;
}
Or is there another alternative? I have also tried the following, but it also executes the futures one after another (instead of parallel):
// Version 2:
var executor = Executors.newCachedThreadPool();
var tasks = new LinkedHashSet<Task>();
for (var task : user.getTasks()) {
CompletableFuture.supplyAsync(() -> execute(task), executor)
.thenAccept(tasks::addAll).join();
}
EDIT: at the end I have two versions which come close the problem I would like to solve. However, I guess version A is not right because parallel threads will add elements to the LinkedHashSet in async mode (which could cause trouble, because LinkedHashSet is not thread safe):
VERSION A (it seems not thread safe):
var executor = Executors.newCachedThreadPool();
var tasks = new LinkedHashSet<Task>();
var futures = new ArrayList<CompletableFuture<Void>>();
for (var t : user.getTasks()) {
futures.add(CompletableFuture.supplyAsync(() -> execute(t), executor).thenAcceptAsync(tasks::addAll));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
and VERSION B (which could be better, but is a little complex):
var executor = Executors.newCachedThreadPool();
var futures = new ArrayList<CompletableFuture<Set<Task>>>();
for (var t : user.getTasks()) {
futures.add(CompletableFuture.supplyAsync(() -> execute(t), executor));
}
Set<Task> o = CompletableFuture
.allOf(futures.toArray(new CompletableFuture[0]))
.thenApplyAsync(v -> futures.stream().flatMap(future -> future.join().stream()))
.join().collect(Collectors.toSet());
I cannot find an easier approach..but for completness, I add the following code which is the shortest - however, it uses ForkJoinPool which should be avoided (?) for long running tasks:
// VERSION C: execute in parallel without suffering from CompletableApi:
return user.getTasks()
.parallelStream()
.flatMap(t -> execute(t).stream())
.collect(Collectors.toSet());
Your code should work as it is. That is, the for loop in your first example waits for the first future to complete before proceeding to the second future, but in the meantime all the other futures are concurrently running. They typically start to execute as soon as you've called supplyAsync. To prove this, here's a self-contained executable:
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Demo {
public static void main(String[] args) throws InterruptedException {
var executor = Executors.newCachedThreadPool();
var results = new ArrayList<String>();
var futures = new ArrayList<CompletableFuture<String>>();
futures.add(CompletableFuture.supplyAsync(() -> sleep(2), executor));
TimeUnit.MILLISECONDS.sleep(100);
futures.add(CompletableFuture.supplyAsync(() -> sleep(1), executor));
// All futures are executed in parallel
for (var f : futures) {
results.add(f.join());
}
results.forEach(System.out::println);
}
private static String sleep(int seconds) {
var start = LocalTime.now();
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
var end = LocalTime.now();
return String.format("Thread %s started at %s and finished at %s",
Thread.currentThread().getId(), start, end);
}
}
The output proves that the second future finished before the first, as expected:
Thread 14 started at 17:49:35.202673531 and finished at 17:49:37.206196631
Thread 15 started at 17:49:35.262183490 and finished at 17:49:36.262342704
CompletableFuture.allOf() is pretty simple here when using Stream API:
CompletableFuture.allOf(user.getTasks().stream()
.map(task -> CompletableFuture.supplyAsync(() -> execute(task), executor))
.toArray(CompletableFuture[]::new))
.join();
Of course your second variant will execute one after another:
CompletableFuture.supplyAsync(() -> execute(task), executor)
.thenAccept(tasks::addAll)
.join();
You join that, blocking the thread.
The second problem is the use of newCachedThreadPool. I'll explain that based on the jdk's HttpClient. In the early version it had that in the documentation that it will use a cached pool, later they removed it from the documentation. Currently it is left in the implementation, but that will be removed also, in time. The problem is that such a pool, when you use it incorrectly, will eat all your resources and kill your application. No more free threads? Sure, I will create a new one, and so on... Eventually this will hurt you. Use a pool with a limited numbers of threads.
To answer your question, you are looking for some kind of flatMap, that could do CompletableFuture<Set<X>> to Set<CompletableFuture<X>>. Such a non-blocking method does not exist. You need to call join, but you can delay the call to that join via a trick:
user.getTasks().stream()
.map(each -> CompletableFuture.supplyAsync(() -> execute(each), executor))
.flatMap(x -> Stream.of(x).map(CompletableFuture::join))
.flatMap(Set::stream)
.collect(Collectors.toSet());
After trying all those versions above I come to the conclustion that the following solution is the best:
// VERSION X is the best
public Set<Task> getTasks(){
var executor = Executors.newCachedThreadPool();
var futures = new ArrayList<Future<Set<Task>>>();
var tasks = new LinkedHashSet<Task>();
for (var t : user.getTasks()) {
futures.add(executor.submit(() -> executor(t)));
}
for (var f : futures) {
try {
tasks.addAll(f.get());
} catch (Exception e) {
e.printStackTrace();
}
}
return tasks;
}
}
It's the best because:
easy and fast code (no unneeded overhead, lambdas, completableFuture,..)
no exception is surpressed
does not stop the execution of further tasks if one task raises an exception
If anyone can convince me to use other versions, then please add arguments.

Emit first element on another thread in RxJava

Good time of day everyone.
I wonder if it's possible to somehow emit element of a Flowable on a different thread than next ones.
For example I have a hot in-memory cache of database objects and I don't want to go to io thread to get elements from there.
Whant I want to do is basically:
if (cache.contains(e)) {
emiter.emit(cache.get(e));
} else {
Io.post(() -> emiter.emit(db.get(e)));
}
I need the same Flowable to use different threads.
I haven't found a way to do this so far.Is it possible?
Consider following method:
private Flowable<String> getDbOnlyIfNotCached(String key) {
if (cache.contains(key)) {
return Flowable.just(cache.get(key));
} else {
return Flowable.fromCallable(() -> db.get(key))
.subscribeOn(Schedulers.io());
}
}
If cache.contains(key) is true, everything will run in the calling thread. If the value is not cached, db.get(key) will be called using the io scheduler.
Update: Examples in Android
You can use above method like this:
getDbOnlyIfNotCached("hit")
.subscribe(s -> {
// If "hit" is cached, this will be executed in the current thread.
Log.d(TAG, Thread.currentThread().getName());
});
getDbOnlyIfNotCached("miss")
.subscribe(s -> {
// If "miss" is cached, this will be executed in another thread.
Log.d(TAG, Thread.currentThread().getName());
});
Or you can use it in a Flowable chain using flatMap.
Flowable.just("hello")
./* some other operators */
.flatMap(s -> getDbOnlyIfNotCached(s))
// If "hit" is cached, chain still runs in the current thread.
.subscribe(s -> {
Log.d(TAG, s + " " + Thread.currentThread().getName());
});
Flowable.just("miss")
./* some other operators */
.flatMap(s -> getDbOnlyIfNotCached(s))
// If "miss" is cached, chain switches to another thread.
.subscribe(s -> {
Log.d(TAG, Thread.currentThread().getName());
});
If you want to observe on the main thread, then specify observeOn at the end of the chain.
Flowable.just("miss")
./* some other operators */
.flatMap(s -> getDbOnlyIfNotCached(s))
// if "miss" is cached, chain switches to another thread.
.observeOn(AndroidSchedulers.mainThread())
// Now switched to the main thread.
.subscribe(s -> {
Log.d(TAG, Thread.currentThread().getName());
});

Polling server for answer, return exception if fails after n times

I'm trying to create a server call using RxJava2 library that will try to poll server for answer and if receives exception 3 times in a row to return that exception
I've set up a basic call that fetches the response from the server
final Observable<ResponseValue> responseValueObservable = Observable
.fromCallable((Callable) (c) -> return getDispatcher().performSubmit(submitValue);
}
});
return responseValueObservable
.retry(3)
.subscribeOn(Schedulers.io()
.onError((t) -> { log.error(t); Observable.timer(2, SECONDS);}
.retryUntil(() -> { return retryIsEnabled }
so getDispatcher().performSubmit(submitValue) returns either SubmitException or ResponseValue object.
I need the code to retry 3 times, pausing after each exception for 2 seconds and return either ResponseValue or the last SubmitException
So after reading Dan Lew's Blog from previous answer I was able to put together this piece of code which does exactly what I wanted to. retryWhen() on re-subscribes automatically after waiting 2 seconds. With the first successful reply from server it stops.
Observable.fromCallable((d::performSubmit))
.subscribeOn(Schedulers.io())
.doOnSubscribe(subscription -> System.out.println("Subscribing"))
.retryWhen(errors -> {
AtomicInteger counter = new AtomicInteger();
return errors
.takeWhile(e -> counter.incrementAndGet() < 3)
.flatMap(e -> {
System.out.println("delay retry by 2 second(s)");
return Observable.timer(2, TimeUnit.SECONDS);
});
}).blockingSubscribe(res -> result = Optional.of(res), throwable -> t = Optional.of((Exception) throwable));
Use the retryWhen() operator to customize the response to errors. From the excellent overview at Dan Lew's Blog:
responseValueObservable
.retryWhen( errorObservable -> errorObservable
.zipWith(Observable.range(1, 3), (n, i) -> i)
.flatMap(retryCount -> Observable.timer(2, TimeUnit.SECONDS)))
...

Rxjava retryWhen called instantly

I'm having a very specific problem or misunderstanding with rxjava that someone hopefully can help with.
I'm running rxjava 2.1.5 and have the following code snippet:
public static void main(String[] args) {
final Observable<Object> observable = Observable.create(emitter -> {
// Code ...
});
observable.subscribeOn(Schedulers.io())
.retryWhen(error -> {
System.out.println("retryWhen");
return error.retry();
}).subscribe(next -> System.out.println("subscribeNext"),
error -> System.out.println("subscribeError"));
}
After executing this, the program prints:
retryWhen
Process finished with exit code 0
My question, and what I don't understand is: Why is retryWhen called instantly upon subscribing to an Observable? The observable does nothing.
What I want is retryWhen to be called when onError is called on the emitter. Am I misunderstanding how rx works?
Thanks!
Adding new snippet:
public static void main(String[] args) throws InterruptedException {
final Observable<Object> observable = Observable.create(emitter -> {
emitter.onNext("next");
emitter.onComplete();
});
final CountDownLatch latch = new CountDownLatch(1);
observable.subscribeOn(Schedulers.io())
.doOnError(error -> System.out.println("doOnError: " + error.getMessage()))
.retryWhen(error -> {
System.out.println("retryWhen: " + error.toString());
return error.retry();
}).subscribe(next -> System.out.println("subscribeNext"),
error -> System.out.println("subscribeError"),
() -> latch.countDown());
latch.await();
}
Emitter onNext and complete is called. DoOnError is never called. Output is:
retryWhen: io.reactivex.subjects.SerializedSubject#35fb3008
subscribeNext
Process finished with exit code 0
retryWhen calls the provided function when an Observer subscribes to it so you have a main sequence accompanied by a sequence that emits the Throwable the main sequence failed with. You should compose a logic onto the Observable you get in this Function so at the end, one Throwable will result in a value on the other end.
Observable.error(new IOException())
.retryWhen(e -> {
System.out.println("Setting up retryWhen");
int[] count = { 0 };
return e
.takeWhile(v -> ++count[0] < 3)
.doOnNext(v -> { System.out.println("Retrying"); });
})
.subscribe(System.out::println, Throwable::printStackTrace);
Since the e -> { } function body is executed for each individual subscriber, you can have a per subscriber state such as retry counter safely.
Using e -> e.retry() has no effect because the input error flow never gets its onError called.
One issue is, that you don't receive any more results because you'r creating a Thread using retryWhen() but your app seems to finish. To see that behaviour you may want to have a while loop to keep your app running.
That actually means that you need to add something like that to the end of your code:
while (true) {}
Another issue is that you dont emit any error in your sample. You need to emit at least one value to call onNext() else it wont repeat because it's waiting for it.
Here's a working example which a value, then it emits an error and repeat. you can use
.retryWhen(errors -> errors)
which is the same as
.retryWhen(errors -> errors.retry())
Working sample:
public static void main(String[] args) {
Observable
.create(e -> {
e.onNext("test");
e.onError(new Throwable("test"));
})
.retryWhen(errors -> errors.retry())
.subscribeOn(Schedulers.io())
.subscribe(
next -> System.out.println("subscribeNext"),
error -> System.out.println("subscribeError"),
() -> System.out.println("onCompleted")
);
while (true) {
}
}
The reason why you need to emit a result is, that Observable needs to emit a value, else it wait until it receives a new one.
This is because onError can only be called onec (in subscribe), but onNext emits 1..* values.
You can check this behaviour by using doOnError() which provides you the error everytime it retrys the Observable.
Observable
.create(e -> e.onError(new Exception("empty")))
.doOnError(e -> System.out.println("error received " + e))
.retryWhen(errors -> errors.retry())
.subscribeOn(Schedulers.io())
.subscribe(
nextOrSuccess -> System.out.println("nextOrSuccess " + nextOrSuccess),
error -> System.out.println("subscribeError")
);

Categories