so first I will show you what I have and what I think happens there:
I have a BehavourSubject<DataObject>:
private BehaviorSubject<DataObject> dataSubject = BehaviorSubject.create();
I give it back in a certain function that looks like this:
public Observable<DataObject> pendingData() {
return this.dataSubject.asObservable()
.doOnNext(data -> {
// do something with this data that has to be thread save.
})
.observeOn(AndroidSchedulers.mainThread());
}
What I assume that happens is, that the doOnNext part will be run in the same Thread, in that this.dataSubject.onNext(data); is called. But as I do something that has to be thread save in this lambda, I should either put it into a semaphore or run all the doOnNext actions in a certain thread.
My first Idea is the "normal way to handle threads in rx" but I donĀ“t know as it works.
I thought to add a subscribeOn(certainBackgroundScheduler) to the observable like this:
public Observable<DataObject> pendingData() {
return this.dataSubject.asObservable()
.doOnNext(data -> {
// do something with this data that has to be thread save.
})
.subscribeOn(certainBackgroundScheduler)
.observeOn(AndroidSchedulers.mainThread());
}
But when I create an Observable with an subscription block, then this block is running in that backgroundScheduler. When I call onNext on the subscriber i call it in that thread which is logical, but is it the same in the BehaviorSubject?
Is it really that simple? If not, how can I force the subject to run the doOnNext block in my certainThread?
You are allowed to have multiple observeOn in your chain which let's you route values between different execution "locations".
dataSubject
.observeOn(backgroundScheduler)
.doOnNext(v -> /* this will run on another scheduler. */)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(v -> /* this will run on main after the previous */)
Is it really that simple? If not, how can I force the subject to run the doOnNext block in my certainThread?
Yes, it is. Read the docs about subscribeOn and observeOn for further clarification.
Related
I'm adding some code to an existing endpoint to send an email. We don't need the result of sending an email to return a response to the user, so I'm adding a .whenComplete() at the end of the chain of futures, calling our email service from within. The call to the email service is also async, returning a CompletionStage<Void>.
CompletionStage<SomeResponse> someEndpoint() {
return doThings()
.thenApply(things -> {
return someResponseFormat(things);
})
.whenComplete((someResponse, ex) -> {
if (ex == null) {
emailClient.sendEmail(someResponse); // CompletionStage<Void>
}
});
}
As I understand, that task will be scheduled and executed. Do I need to call join() on sendEmail(...)? Would doing so have a different behavior than not calling them? What is the best practice?
Edit: Originally I asked if I need to call join() or get(), which was misunderstood as "which do I need to call," when I meant, "do I need to call either at all."
The operation associated with emailClient.sendEmail(someResponse) will be scheduled regardless of whether you wait for its completion, so unless the JVM terminates in the meanwhile, it will complete. But
Nobody will notice when the operation completed or be able to wait for its completion.
Nobody will notice when the operation fails with an exception.
So what you probably want to do, is
CompletionStage<SomeResponse> someEndpoint() {
return doThings()
.thenApply(things -> someResponseFormat(things))
.thenCompose(someResponse -> emailClient.sendEmail(someResponse)
.thenApply(_void -> someResponse));
}
Then, when the caller of someEndpoint() invokes join() on it, the join() would wait for the completion of the sendEmail and also report errors when sendEmail fails. Likewise, when the caller of someEndpoint() chains dependent operations, they would start after the completion of sendEmail.
SpringBoot v2.5.1
There is an endpoint requesting a long running process result and it is created somehow
(for simplicity it is Mono.fromCallable( ... long running ... ).
Client make a request and triggers the publisher to do the work, but after several seconds client aborts the request (i.e. connection is lost). And the process still continues to utilize resources for computation of a result to throw away.
What is a mechanism of notifying Project Reactor's event loop about unnecessary work in progress that should be cancelled?
#RestController
class EndpointSpin {
#GetMapping("/spin")
Mono<Long> spin() {
AtomicLong counter = new AtomicLong(0);
Instant stopTime = Instant.now().plus(Duration.of(1, ChronoUnit.HOURS));
return Mono.fromCallable(() -> {
while (Instant.now().isBefore(stopTime)) {
counter.incrementAndGet();
if (counter.get() % 10_000_000 == 0) {
System.out.println(counter.get());
}
// of course this does not work
if (Thread.currentThread().isInterrupted()){
break;
}
}
return counter.get();
});
}
}
fromCallable doesn't shield you from blocking computation inside the Callable, which your example demonstrates.
The primary mean of cancellation in Reactive Streams is the cancel() signal propagated from downstream via the Subscription.
Even with that, the fundamental requirement of avoiding blocking code inside reactive code still holds, because if the operators are simple enough (ie. synchronous), a blocking step could even prevent the propagation of the cancel() signal...
A way to adapt non-reactive code while still getting notified about cancellation is Mono.create: it exposes a MonoSink (via a Consumer<MonoSink>) which can be used to push elements to downstream, and at the same time it has a onCancel handler.
You would need to rewrite your code to eg. check an AtomicBoolean on each iteration of the loop, and have that AtomicBoolean flipped in the sink's onCancel handler:
Mono.create(sink -> {
AtomicBoolean isCancelled = new AtomicBoolean();
sink.onCancel(() -> isCancelled.set(true));
while (...) {
...
if (isCancelled.get()) break;
}
});
Another thing that is important to note in your example: the AtomicInteger is shared state. If you subscribe a second time to the returned Mono, both subscriptions will share the counter and increment it / check it in parallel, which is probably not good.
Creating these state variables inside the Consumer<MonoSink> of Mono.create ensures that each subscription gets its own separate state.
I need to send some data after user registered. I want to do first attempt in main thread, but if there are any errors, I want to retry 5 times with 10 minutes interval.
#Override
public void sendRegisterInfo(MailData data) {
Mono.just(data)
.doOnNext(this::send)
.doOnError(ex -> logger.warn("Main queue {}", ex.getMessage()))
.doOnSuccess(d -> logger.info("Send mail to {}", d.getRecipient()))
.onErrorResume(ex -> retryQueue(data))
.subscribe();
}
private Mono<MailData> retryQueue(MailData data) {
return Mono.just(data)
.delayElement(Duration.of(10, ChronoUnit.MINUTES))
.doOnNext(this::send)
.doOnError(ex -> logger.warn("Retry queue {}", ex.getMessage()))
.doOnSuccess(d -> logger.info("Send mail to {}", d.getRecipient()))
.retry(5)
.subscribe();
}
It works.
But I've got some questions:
Did I correct to make operation in doOnNext function?
Is it correct to use delayElement to make a delay between executions?
Did the thread blocked when waiting for delay?
And what the best practice to make a retries on error and make a delay between it?
doOnXXX for logging is fine. But for the actual element processing, you must prefer using flatMap rather than doOnNext (assuming your processing is asynchronous / can be converted to returning a Flux/Mono).
This is correct. Another way is to turn the code around and start from a Flux.interval, but here delayElement is better IMO.
The delay runs on a separate thread/scheduler (by default, Schedulers.parallel()), so not blocking the main thread.
There's actually a Retry builder dedicated to that kind of use case in the reactor-extra addon: https://github.com/reactor/reactor-addons/blob/master/reactor-extra/src/main/java/reactor/retry/Retry.java
I'm pretty new to RxJava and have some questions on patterns etc.
I'm creating an observable using the code below:
public Observable<Volume> getVolumeObservable(Epic epic) {
return Observable.create(event -> {
try {
listeners.add(streamingAPI.subscribeForChartCandles(epic.getName(), MINUTE, new HandyTableListenerAdapter() {
#Override
public void onUpdate(int i, String s, UpdateInfo updateInfo) {
if (updateInfo.getNewValue(CONS_END).equals(ONE)) {
event.onNext(new Volume(Integer.parseInt(updateInfo.getNewValue(LAST_TRADED_VOLUME))));
}
}
}));
} catch (Exception e) {
LOG.error("Error from volume observable", e);
}
});
}
Everything is working as expected, but I have some questions on error handling.
If I understand correctly, this is to be viewed as a "hot observble", i.e. events will happen regardless of there being a subscription or not (onUpdate is a callback used by a remote server which I have no control over).
I've chosen not to call onError here since I don't want the observable to stop emitting events in case of a single exception. Is there a better pattern to be used? .retry() comes to mind, but I'm not sure that it makes sense for a hot observable?
Also, how is the observable represented when the subscriptions is created, but before the first onNext is called? Is it just an Observable.empty()
1) Your observable is not hot. The distinguishing factor is whether multiple subscribers share the same subscription. Observable.create() invokes subscribe function for every subscriber, i.e. it is cold.
It is easy to make it hot though. Just add share() operator. It will subscribe with first subscriber and unsubscribe with last one. Do not forget to implement unsubscribe functionality with something like this:
event.setCancellable(() -> listeners.remove(...));
2) Errors could be recoverable and not recoverable.
In case you consider an error to be self-recoverable (no action required from your side) you should not call onError as this will kill your observable (no further events would be emitted). You can possibly notify your subscribers by emitting special Volume message with error details attached.
In case an error is fatal, e.g. you have failed to add listener, so there could be no further messages, you should not silently ignore this. Emit onError as your observable is not functional anyway.
In case an error requires actions from you, typically retry, or retry with timeout, you can add one of retryXxx() operators. Do this after create() but before share().
3) Observable is an object with subscribe() method. How exactly it is represented depends on the method you created it with. See source code of create() for example.
I'm working an Android app that has to make server request and then perform actions when the request is completed. Here's some pseudo code to help explain the situation:
makeRequest(new SomeTask{
onDone() {
// Do actionB with queue
}
});
// Do actionA with queue. Must be execute first!!
Here's the implementation of makeRequest in pseudo code:
makeRequest(SomeTask task) {
if(canDoOptimization) { // if true, don't need to make request
// It's a bad idea to execute this immediately.
// Wish I could wait until the current thread of execution was done...
task.onDone();
return;
}
asyncTask = new AsyncTask<SomeTask, Void, Void>() {
doInBackground(SomeTask... task) {
// Make server request...
task.onDone();
}
}
asyncTask.execute(task);
}
Usually actionA happens before actionB as expected, but in cases where we can avoid a network requests, SomeTask.execute is called immediately. This causes actionB to occur before actionA, which is bad. Is there any way I can guarantee this doesn't happen?
I've run into this situation several times in javascript. In those cases, I would wrap the SomeTask.execute call with a setTimeout or setImmediate to maintain the proper async semantics.
For clarity, here's an example of the same bug in JavaScript: https://gist.github.com/xavi-/5882483
Any idea what I should do in Java/Android?
Welcome to world of synchronization. Mutex or lock objects are often used for that purpose. Is there a Mutex in Java?
your B task should wait on mutex which is to be signaled by task A upon its completion. That will ensure proper execution order where A task will finish before B.
Always put task.onDone() in the AsyncTask, even if it doesn't have to make a request.
makeRequest(SomeTask task) {
asyncTask = new AsyncTask<SomeTask, Void, Void>() {
void doInBackground(SomeTask... task) {
if(canDoOptimization) { // if true, don't need to make request
// It's a bad idea to execute this immediately.
// Wish I could wait until the current thread of was done...
task.onDone();
return;
} else {
// Make server request...
task.onDone();
}
}
}
asyncTask.execute(task);
}
Why can't you just switch the order of things?
// Do actionA with queue. Must be execute first!!
makeRequest(new SomeTask{
onDone() {
// Do actionB with queue
});
If actionA is asynchronous as well and performed on a separate AsyncTask, you can call makeRequest(...) on actionA's AsyncTasks's onPostExecute() method.
And btw, since Android Honeycomb version, AsyncTasks are ran on the same thread, meaning if you have several tasks they can block each other.
This is fixed by specifying that the AsyncTsak should run in a thread pool:
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else {
asyncTask.execute();
}