RxJava - flatMap on the same thread - rx-java2

I have nested items in my database like e.g. house - rooms. If I delete a house I also want to delete all rooms, but I do not want to think about this, so the house must know this. So my setup would look something like this:
fun deleteHouse(item: House): Single<House> {
houseObservable // emits List<House>
.take(1)
.map {
DBManager.beginTransaction()
it
}
.flatMapIterable { it }
.flatMapSingle { deleteRoom(it) }
.toList()
.map {
DBManager.deleteWithoutDependencies(item)
DBManager.endTransaction()
item
}
}
fun deleteRoom(item: Room): Single<Room> {
roomObservables // emits List<Room>
.take(1)
.map {
DBManager.beginTransaction()
it
}
.flatMapIterable { it }
.flatMapSingle { DBManager.deleteWithoutDependencies(item) }
.toList()
.map {
DBManager.endTransaction()
item
}
}
Problem
Every item is deleted on RxComputationThread-1 and every beginTransaction and endTransaction as well, BUT the last endTransaction that finished the deletion of the house.
I need to run all database actions on the same thread, otherwise the database will lock itself out...
Any ideas how to solve this?
Idea
Pass the used Schedular to the functions and use the same in the flatMaps, but is this really necessary to solve this problem?

Related

Multiple finally clauses

Assume you have two different objects requiring cleanup after execution using a call to method cleanup(). I would do this using a try-finally construct.
val objA = new ObjA()
val objB = new ObjB()
try {
// do failure prone important stuff
} finally {
objA.cleanup()
objB.cleanup()
}
However, the cleanup function might also throw an exception. Is there a more elegant way of solving this than having another try-finally in the finally block?
// cluttering working solution:
val objA = new ObjA()
val objB = new ObjB()
try {
// do failure prone important stuff
} finally {
try {
objA.cleanup()
} finally {
objB.cleanup()
}
}
// Is there an option to do something more readable like:
try {
//do failure prone important stuff
} finally {
objA.cleanup()
} finally {
objB.cleanup()
}
I concede that having a cleanup function that may throw exceptions breaks the contract of a cleanup function. However, you do not always control external APIs.
How about using scala.util.Try to wrap the cleanup calls ?
import scala.util.Try
try {
// do failure prone important stuff
} finally {
Try(objA.cleanup())
Try(objB.cleanup())
}
How about this - two blocks, outer dealing with objA, inner dealing with objB, and exceptions, no matter where they get thrown ('important stuff' or any of two cleanup calls), can be caught 'outside' of both of them:
val objA = new ObjA()
try {
val objB = new ObjB()
try {
// do failure prone important stuff
} finally {
objB.cleanup()
}
} finally {
objA.cleanup()
}

Why different behavior of Single when using just or fromCallable in startWith?

I'm starting a PublishSubject with testPublishSubject.startWith(createSingle().toObservable()).
If I subscribe to this observable, dispose and subscribe again, it will emit a different item depending on how I created the Single. If I create it with just, it emits the same item as the first time (item1), if I create it with fromCallable, it emits the updated item (item2). Why is the behavior different? Is there a way to use just and have it behave like fromCallable?
Edit: Ok, I think I know why it behaves differently. It's because it's not re-creating the Single. fromCallable works only because of the closure, which is executed again with the updated counter.
My updated question would be: Is there a way to have the subject re-create the Single? The reason I want this, is because the Single is fetching a value, which may have been updated, and I need to fetch it again.
var counter = 1
// With this, it works as expected
// fun createSingle(): Single<String> = Single.fromCallable {
// "item-${counter++}"
// }
// With this, the second subscription still shows "item-1
fun createSingle(): Single<String> = Single.just("item-${counter++}")
val testPublishSubject = PublishSubject.create<String>()
val observable = testPublishSubject.startWith(createSingle().toObservable().doOnNext {
log(">>> single on next: $it")
}).doOnNext {
log(">>> publish subject on next: $it")
}
log(">>> subscribing1")
val disposable1 = observable.subscribe {
log(">>> value subscription 1: $it")
}
log(">>> pushing random item")
testPublishSubject.onNext("random item")
log(">>> disposing subscription1")
disposable1.dispose()
log(">>> subscribing2")
val disposable2 = observable.subscribe {
log(">>> value subscription 2: $it")
}

How to retain state in Rx without the scan function

I'm working on porting some of my View Models into (rough) Finite State Machines as my UI tends to fit that pattern rather well (Mealy/Moore, don't care for the purpose of this question). Additionally, when done well - state machines really clean up testing - as they prohibit certain test permutations from ever happening.
My current view models use RxSwift (and RxKotlin - depending on the app), and the underlying use cases (database calls, network calls, etc) also use Rx (hence why I need to stay in that ecosystem).
What I've discovered is that Rx is awesome, State Machines are awesome --> Rx + State Machines seem to be a bit of a hash to do anything non-trivial. For example, I know I can use the .scan operator to retain some state, IF my state machine was entirely synchronous (for example, something roughly like this in Swift):
enum Event {
case event1
case event2
case event3
}
enum State {
case state1
case state2
case state3
func on(event: Event) -> State {
switch (self, event) {
case (.state1, .event1):
// Do something
return .state2
case (.state2, .event2):
// Do something
return .state3
default:
return self // (or nil, or something)
}
}
}
func foo() -> Observable<State> {
let events = Observable<Event>.of(.event1, .event2, .event3)
return events.scan(State.state1) { (currentState, event) -> State in
return currentState.on(event)
}
}
But, what can I do if the return from my State.on function is an Observable (like a network call or something that takes a long time, which is already in Rx)?
enum State {
case notLoggedIn
case loggingIn
case loggedIn
case error
func on(event: Event) -> Observable<State> {
switch (self, event) {
case (.notLoggedIn, .event1):
return api.login(credentials)
.map({ (isLoggedIn) -> State in
if isLoggedIn {
return .loggedIn
}
return .error
})
.startWith(.loggingIn)
... other code ...
default:
return self
}
}
}
I've tried making the .scan operator take in an Observable accumulator, but the result of this code is that the state machine is subscribed to or run too many times. I guess because it runs on each state in the observable that is accumulating.
return events.scan(Observable.just(State.state1)) { (currentState, event) -> Observable<State> in
currentState.flatMap({ (innerState) -> Observable<State> in
return innerState.on(event: event)
})
}.flatMap { (states) -> Observable<State> in
return states
}
I think, if I could manage to cleanly pull the state variable back in, the simplest implementation could look like this:
return events.flatMapLatest({ (event) -> Observable<State> in
return self.state.on(event: event)
.do(onNext: { (state) in
self.state = state
})
})
But, pulling from a private state variable into an observable stream, and updating it - well, not only is it ugly, I feel like I'm just waiting to be hit by a concurrency bug.
Edit: Based on feedback from Sereja Bogolubov - I've added a Relay and come up with this code - still not great, but getting there.
let relay = BehaviorRelay<State>(value: .initial)
...
func transition(from state: State, on event: Event) -> Observable<State> {
switch (state, event) {
case (.notLoggedIn, .event1):
return api.login(credentials)
.map({ (isLoggedIn) -> State in
if isLoggedIn {
return .loggedIn
}
return .error
})
.startWith(.loggingIn)
... other code ...
default:
return self
}
}
return events.withLatestFrom(relay.asObservable(), resultSelector: { (event, state) -> Observable<State> in
return self.transition(from: state, on: event)
.do(onNext: { (state) in
self.relay.accept(state)
})
}).flatMap({ (states) -> Observable<State> in
return states
})
The relay (or replay subject or whatever) is updated in a doOnNext from the result of the state transition... This still feels like it could cause a concurrency problem, but not sure what else would work.
No, you don't have to be entirely sync to maintain arbitrary complex state.
Yes, there are ways to achive needed behavior without scan. How about the withLatestFrom, where other is your current state (i.e. a separate Observable<MyState>, but you would need ReplaySubject<MyState> under the hood).
Let me know if you need more details.
Proof of concept, javascript:
const source = range(0, 10);
const state = new ReplaySubject(1);
const example = source.pipe(
withLatestFrom(state), // that's the way you read actual state
map(([n, currentState]) => {
state.next(n); // that's the way you change the state
return ...
})
);
Please be aware that more sophisticated cases (like race conditions risky) might require something at least as complex as combineLatest and approp. Scheduler's in place.
I think Elm's system can come in handy here. In Elm, the reducer that you pass into the system doesn't just return state, it also returns a "command" which in our case would be a Observable<Event> (not an RxSwift.Event, but your Event enum.) This command isn't stored in the scan's state, but rather it is subscribed to outside the scan and its output is fed back into the scan (through a Subject of some sort.) Tasks that require cancelling would observe the current state and start and stop operation based on the state.
There are several libraries in the RxSwift ecosystem that help simplify these sort of things. The two primary ones are, ReactorKit and RxFeedback. And there are several others...
For a simple example of what I'm talking about, check out this gist. This sort of system allows your Moore machine to fire off an action upon entering a state which could potentially cause 0..n new input events.

Repeat Single based on onSuccess() value

I want to repeat a Single based on the single value emitted in onSuccess(). Here is a working example
import org.reactivestreams.Publisher;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.functions.Function;
public class Temp {
void main() {
Job job = new Job();
Single.just(job)
.map(this::processJob)
.repeatWhen(new Function<Flowable<Object>, Publisher<?>>() {
#Override
public Publisher<?> apply(Flowable<Object> objectFlowable) throws Exception {
// TODO repeat when Single emits false
return null;
}
})
.subscribe();
}
/**
* returns true if process succeeded, false if failed
*/
boolean processJob(Job job) {
return true;
}
class Job {
}
}
I understand how repeatWhen works for Observables by relying on the "complete" notification. However since Single doesn't receive that notification I'm not sure what the Flowable<Object> is really giving me. Also why do I need to return a Publisher from this function?
Instead of relying on a boolean value, you could make your job throw an exception when it fails:
class Job {
var isSuccess: Boolean = false
}
fun processJob(job: Job): String {
if (job.isSuccess) {
return "job succeeds"
} else {
throw Exception("job failed")
}
}
val job = Job()
Single.just(job)
.map { processJob(it) }
.retry() // will resubscribe until your job succeeds
.subscribe(
{ value -> print(value) },
{ error -> print(error) }
)
i saw a small discrepancy in the latest docs and your code, so i did a little digging...
(side note - i think the semantics of retryWhen seem like the more appropriate operator for your case, so i've substituted it in for your usage of repeatWhen. but i think the root of your problem remains the same in either case).
the signature for retryWhen is:
retryWhen(Function<? super Flowable<Throwable>,? extends Publisher<?>> handler)
that parameter is a factory function whose input is a source that emits anytime onError is called upstream, giving you the ability to insert custom retry logic that may be influenced through interrogation of the underlying Throwable. this begins to answer your first question of "I'm not sure what the Flowable<Object> is really giving me" - it shouldn't be Flowable<Object> to begin with, it should be Flowable<Throwable> (for the reason i just described).
so where did Flowable<Object> come from? i managed to reproduce IntelliJ's generation of this code through it's auto-complete feature using RxJava version 2.1.17. upgrading to 2.2.0, however, produces the correct result of Flowable<Throwable>. so, see if upgrading to the latest version generates the correct result for you as well.
as for your second question of "Also why do I need to return a Publisher from this function?" - this is used to determine if re-subscription should happen. if the factory function returns a Publisher that emits a terminal state (ie calls onError() or onComplete()) re-subscription will not happen. however, if onNext() is called, it will. (this also explains why the Publisher isn't typed - the type doesn't matter. the only thing that does matter is what kind of notification it publishes).
another way to rewrite this, incorporating the above, might be as follows:
// just some type to use as a signal to retry
private class SpecialException extends RuntimeException {}
// job processing results in a Completable that either completes or
// doesn't (by way of an exception)
private Completable rxProcessJob(Job job) {
return Completable.complete();
// return Completable.error(new SpecialException());
}
...
rxProcessJob(new Job())
.retryWhen(errors -> {
return errors.flatMap(throwable -> {
if(throwable instanceof SpecialException) {
return PublishProcessor.just(1);
}
return PublishProcessor.error(throwable);
});
})
.subscribe(
() -> {
System.out.println("## onComplete()");
},
error -> {
System.out.println("## onError(" + error.getMessage() + ")");
}
);
i hope that helps!
The accepted answer would work, but is hackish. You don't need to throw an error; simply filter the output of processJob which converts the Single to a Maybe, and then use the repeatWhen handler to decide how many times, or with what delay, you may want to resubscribe. See Kotlin code below from a working example, you should be able to easily translate this to Java.
filter { it }
.repeatWhen { handler ->
handler.zipWith(1..3) { _, i -> i }
.flatMap { retryCount -> Flowable.timer(retryDelay.toDouble().pow(retryCount).toLong(), TimeUnit.SECONDS) }
.doOnNext { log.warn("Retrying...") }
}

Rxjava User-Retry observable with .cache operator?

i've an observable that I create with the following code.
Observable.create(new Observable.OnSubscribe<ReturnType>() {
#Override
public void call(Subscriber<? super ReturnType> subscriber) {
try {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(performRequest());
}
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
});
performRequest() will perform a long running task as you might expect.
Now, since i might be launching the same Observable twice or more in a very short amount of time, I decided to write such transformer:
protected Observable.Transformer<ReturnType, ReturnType> attachToRunningTaskIfAvailable() {
return origObservable -> {
synchronized (mapOfRunningTasks) {
// If not in maps
if ( ! mapOfRunningTasks.containsKey(getCacheKey()) ) {
Timber.d("Cache miss for %s", getCacheKey());
mapOfRunningTasks.put(
getCacheKey(),
origObservable
.doOnTerminate(() -> {
Timber.d("Removed from tasks %s", getCacheKey());
synchronized (mapOfRunningTasks) {
mapOfRunningTasks.remove(getCacheKey());
}
})
.cache()
);
} else {
Timber.d("Cache Hit for %s", getCacheKey());
}
return mapOfRunningTasks.get(getCacheKey());
}
};
}
Which basically puts the original .cache observable in a HashMap<String, Observable>.
This basically disallows multiple requests with the same getCacheKey() (Example login) to call performRequest() in parallel. Instead, if a second login request arrives while another is in progress, the second request observable gets "discarded" and the already-running will be used instead. => All the calls to onNext are going to be cached and sent to both subscribers actually hitting my backend only once.
Now, suppouse this code:
// Observable loginTask
public void doLogin(Observable<UserInfo> loginTask) {
loginTask.subscribe(
(userInfo) -> {},
(throwable) -> {
if (userWantsToRetry()) {
doLogin(loinTask);
}
}
);
}
Where loginTask was composed with the previous transformer. Well, when an error occurs (might be connectivity) and the userWantsToRetry() then i'll basically re-call the method with the same observable. Unfortunately that has been cached and I'll receive the same error without hitting performRequest() again since the sequence gets replayed.
Is there a way I could have both the "same requests grouping" behavior that the transformer provides me AND the retry button?
Your question has a lot going on and it's hard to put it into direct terms. I can make a couple recommendations though. Firstly your Observable.create can be simplified by using an Observable.defer(Func0<Observable<T>>). This will run the func every time a new subscriber is subscribed and catch and channel any exceptions to the subscriber's onError.
Observable.defer(() -> {
return Observable.just(performRequest());
});
Next, you can use observable.repeatWhen(Func1<Observable<Void>, Observable<?>>) to decide when you want to retry. Repeat operators will re-subscribe to the observable after an onComplete event. This particular overload will send an event to a subject when an onComplete event is received. The function you provide will receive this subject. Your function should call something like takeWhile(predicate) and onComplete when you do not want to retry again.
Observable.just(1,2,3).flatMap((Integer num) -> {
final AtomicInteger tryCount = new AtomicInteger(0);
return Observable.just(num)
.repeatWhen((Observable<? extends Void> notifications) ->
notifications.takeWhile((x) -> num == 2 && tryCount.incrementAndGet() != 3));
})
.subscribe(System.out::println);
Output:
1
2
2
2
3
The above example shows that retries are aloud when the event is not 2 and up to a max of 22 retries. If you switch to a repeatWhen then the flatMap would contain your decision as to use a cached observable or the realWork observable. Hope this helps!