SQLite transaction and RxJava - android-sqlite

There is how I save my data with RxJava:
override fun put(note: Note): Observable<Note> {
validateNote(note)
return Observable.just(note)
.doOnNext { dbKeeper.startTransaction() }
.doOnNext { storeHashtags(note) }
.doOnNext { storeImages(note) }
.flatMap { notesDataStore.put(notesMapper.transform(note)) }
.map { notesMapper.transform(it) }
.doOnNext { dbKeeper.setTransactionSuccessful() }
.doOnUnsubscribe { dbKeeper.endTransaction() }
}
And then I use this method like this:
notesManager.put(note)
.switchMap { notesManager.getHashtags() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {view.setHashtags(it) }
And doOnUnsubscribe never called as getHashtags() trying to SELECT from db that locked by startTransaction(). Deadlock, heh.
Okay. Let's replace doOnUnsubscribe(...) with doOnTerminate(...).
override fun put(note: Note): Observable<Note> {
validateNote(note)
return Observable.just(note)
.doOnNext { dbKeeper.startTransaction() }
.doOnNext { storeHashtags(note) }
.doOnNext { storeImages(note) }
.map { notesMapper.transform(note) }
.flatMap { notesDataStore.put(it) }
.map { notesMapper.transform(it) }
.doOnNext { dbKeeper.setTransactionSuccessful() }
.doOnTerminate { dbKeeper.endTransaction() }
}
But now transaction won't close if Observable will be interrupted by subscriber.unsubscribe().
What can you recommend to solve my situation?
Additional information:
I use one writableDb instance to write/read data.

I'd say this isn't a good fit for Rx; My recommendation would be to do the transaction using the usual try-finally (pardon the Java):
Observable<Note> put(Note note) {
return just(note)
.doOnNext(n -> {
validateNote(n);
dbKeeper.startTransaction();
try {
storeHashtags(n);
storeImages(n);
notesDataStore.put(notesMapper.transform(n));
dbKeeper.setTransactionSuccessful();
} finally {
dbKeeper.endTransaction();
}
});
}
Edit: Maybe this is more useful:
fun <T> withTransaction(sourceObservable: Observable<T>): Observable<T> {
val latch = AtomicInteger(1)
val maybeEndTransaction = Action0 {
if (latch.decrementAndGet() == 0) {
endTransaction()
}
}
return Observable.empty<T>()
.doOnCompleted { startTransaction() }
.mergeWith(sourceObservable)
.doOnNext { setTransactionSuccessful() }
.doOnTerminate(maybeEndTransaction)
.doOnUnsubscribe(maybeEndTransaction)
}
Use it like this:
override fun put(note: Note): Observable<Note> {
return Observable.just(note)
.doOnNext { validateNote(note) }
.doOnNext { storeHashtags(note) }
.doOnNext { storeImages(note) }
.flatMap { notesDataStore.put(notesMapper.transform(note)) }
.map { notesMapper.transform(it) }
.compose { withTransaction }
}
It will ensure that the transaction ends exactly once; just be careful that you don't switch threads within your original observable chain (unless your transaction amanger can handle that, or you modified your schedulers to associate new threads with existing transactions).

Related

How to call recursive function with Promise Kit?

I am stuck with somewhere to call same function again in promise and because of calling multiple time it's deallocate promise. Actually in my case I have API with multiple page request and I want to call it with promise. I was implemented it as below.
func fetchContacts() -> Promise<FPGetContactResponse?> {
return Promise { seal in
let contactrequest = FPGetContactRequest()
contactrequest.pageNo = getAPICurrentPageNo(Api.API_CONTACTS) + 1
contactrequest.pageSize = SMALL_PAGE_SIZE
contactrequest.doGetContacts(parameter: [:], response: { (response) in
print("Contacts Count : \(response.Contacts?.count ?? 0)")
if(response.Contacts?.count ?? 0 != 0){
_ = self.fetchContacts()
}else{
seal.fulfill(response)
}
})
{ (error) in
print(error.localizedDescription)
seal.reject(error)
}
}
}
In above function I check for contact count != 0 then I need to call same function again. But unfortunately it's deallocate promise.
I call promise sequence like below.
func startSyncData(handler:#escaping SyncAPIHandler){
firstly {
self.fetchContacts().ensure {
handler(false,0.5,nil)
}
}.then { data in
self.fetchInteractions().ensure {
handler(false,0.7,nil)
}
}.then { data in
self.fetchAddresses().ensure {
handler(false,0.8,nil)
}
}.then { data in
self.fetchLookupQuery().ensure {
}
}
.done { contacts -> Void in
//Do something with the JSON info
print("Contacts Done")
handler(true,0.8,nil)
}
.catch(policy: .allErrors) { error in
print(error.localizedDescription)
}
}
Please provide me the right way to call same function again in promise.
Instead of using recursion you should return a response within your promise and check it inside next .then and call fetchContacts again if it's needed:
fetchContacts()
.then { response -> Promise<FPGetContactResponse> in
if (response.Contacts?.count ?? 0 != 0) {
return fetchContacts() // Make the second call
}
return .value(response) // Return fullfilled promise
}
.then {
...
}
Also you can make a special wrapper for your case using the next approach - https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#retry--polling
I implemented things with following solution.
func syncContacts() -> Promise<FPGetContactResponse?> {
return fetchContacts().then{ seal -> Promise<FPGetContactResponse?> in
if(seal?.Contacts?.count ?? 0 != 0){
return self.syncContacts()
}else{
return Promise.value(seal)
}
}
}
Now just call syncContacts() method in promise sequence, like below.
func startSyncData(handler:#escaping SyncAPIHandler){
firstly {
self.syncContacts().ensure {
handler(false,0.5,nil)
}
}.then { data in
self.syncInterections().ensure {
handler(false,0.7,nil)
}
}.then { data in
self.syncAddresses().ensure {
handler(false,0.8,nil)
}
}.then { data in
self.syncLookupQuery().ensure {
}
}
.done { contacts -> Void in
//Do something with the JSON info
print("Contacts Done")
handler(true,0.8,nil)
}
.catch(policy: .allErrors) { error in
print(error.localizedDescription)
}
}

Return Mono.empty() if a value is found but without executing other steps

This problem is very hard to describe as text, so if the title doesn't fit the requirement, sorry for that.
I would like to achieve a specific goal with Project Reactor flux and mono, which seems to be pretty simple at first look.
A code example, in the "blocking-way" will be better than a long description:
fun findGroupToCreateBlocking(userId: UUID, groupLabel: String): Optional<LinkUserToGroup> {
val group = lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.blockOptional()
if(group.isPresent) {
return Optional.empty()
}
return lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
.blockOptional()
}
I try to achieve the same thing without the block part of course. I ended up with the following code:
fun findGroupToCreateReactive(userId: UUID, groupLabel: String): Mono<LinkUserToGroup> =
lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.map { Optional.of(it) }
.defaultIfEmpty(Optional.empty())
.filter { g -> g.isEmpty }
.flatMap { lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
}
I think (and I'm not the only one 😇) we can do better and not rely on the Optional usage in the middle of the stream... but I didn't find any other solution.
This is the fourth time I fight against this "pattern", so some help would be welcomed!
I've generated a demo project on Gitlab (here) with unit tests for bot reactive and blocking implementation to see if the proposition match the requirement. If you want, you can fork and use the project.
Instead of using a Mono.empty, I used the Flux.hasElement method (like #yossarian), but adding a negation filter. It seems to work as the unit test still pass.
fun findGroupToCreateReactive(userId: UUID, groupLabel: String): Mono<LinkUserToGroup> =
lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.map { it.label }
.hasElement(groupLabel)
.filter { g -> !g }
.flatMap { lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
}
Since we want to search groups only if the user doesn't belong to a group, then this is made more explicit with the negation filter.
I didn't find a nice solution with Reactor. However, since you are using Kotlin you might embrace the pattern and create an extension function for it:
fun findGroupToCreateReactive(userId: UUID, groupLabel: String): Mono<LinkUserToGroup> =
lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.switchIfEmptyOrEmpty {
lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
}
fun <T> Mono<*>.switchIfEmptyOrEmpty(monoIfEmpty: () -> Mono<T>): Mono<T> =
this.map { Optional.of(it) }
.defaultIfEmpty(Optional.empty())
.filter { g -> g.isEmpty }
.flatMap { monoIfEmpty.invoke() }
Another alternative with hasElement operator:
fun findGroupToCreateReactive(userId: UUID, groupLabel: String): Mono<LinkUserToGroup> =
lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.hasElement()
.flatMap { hasElement ->
if (hasElement)
{
return#flatMap Mono.empty<LinkUserToGroup>()
} else
{
lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
}
}
PS: Thanks for the sample repo. That really helped trying out different things!

How do I reverse a promise?

I'm using PromiseKit to handle flow through a process.
Prior, I did a similar app without promises but decided frick it I'm gonna try promises just because, well, why not?
So I'm throwing a back button in the mix as I did in the prior app. Only problem is, I'm not exactly sure how to handle "reversing" if you want to call it that.
So say I have a flow of
doSomething().then {
// do something else
}.then {
// do something else
}.done {
// wrap it up, boss
}.catch {
// you're an idiot, bud
}
Say I'm in the first or second part of the chain then and I want to go back up the chain - is this possible?
Is there a link y'all can give me that I can use to read up on how to do that?
I'm thinking I might have to restart the "chain", but then how would I step through the flow....WAIT (light bulb), I can programmatically fulfill the necessary promises with whatever the data is that initially was fulfilled with until I get to the point in the "chain" where I needed to go back to, right?
Advice D:?
You can always have a catch and a then on the same promise.
var somePromise = doSomething()
// first chain
somePromise.catch { error in
// handle error
}
// second chain from the same starting point
somePromise.then {
// do something else
}.then {
// do something else
}.catch {
// you can still catch the error here too
}
You're basically creating two promise chains from the same original promise.
No, you can not do that. Once you commit a promise, you can not reverse that. Because the chain is supposed to finish in the descending order, it's cumbersome to track the order in each .then block.
What you can do is, handle the internal logic responsible to fulfill or reject a promise and start the chain from the beginning.
func executeChain() {
doSomething().then {
// do something else
}.then {
// do something else
}.done {
// condition to
executeChain()
}.catch {
// you're an idiot, bud
}
}
func doSomething() -> Promise<SomeThing>{
if (condition to bypass for reversing) {
return .value(something)
}
// Normal execution
}
But if you can improve your question with an actual use case and code then it could help providing more suitable explanation.
No you can't but you can set order in array.
bar(promises: [foo1(), foo2(), foo3()])
func bar<T>(promises: [Promise<T>]) {
when(fulfilled: promises)
.done { _ in
// TODO
}
.catch { error in
// When get error reverse array and call it again
self.bar(promises: promises.reversed())
}
}
func foo1() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo2() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo3() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
or alternatively
bar(foo1, foo2, foo3)
.done { _ in
// TODO
}
.catch { error in
print(error.localizedDescription)
self.bar(self.foo3, self.foo2, self.foo1)
.done { _ in
// TODO
}
.catch { error2 in
print(error2.localizedDescription)
}
}
func bar<T>(_ promise1: () -> Promise<T>,
_ promise2: #escaping () -> Promise<T>,
_ promise3: #escaping () -> Promise<T>) -> Promise<T> {
return Promise { seal in
promise1()
.then { _ in return promise2() }
.then { _ in return promise3() }
.done { model in
seal.fulfill(model)
}
.catch {
seal.reject($0)
}
}
}
func foo1() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo2() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo3() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}

PromiseKit wrapping external closure in Promises

I am using an external library in Swift so I cannot control the return statements. My understanding is that I should wrap these returns in promises in order to use PromiseKit. Is this correct?
Assuming so, I have working code as follows:
private func getChannelImage(for channel: TCHChannel, completion: #escaping (UIImage?, CAProfileError?) -> Void) {
if let members = channel.members {
members.members(completion: { (result, paginator) in
if result.isSuccessful() {
// ... do something
}
else {
completion(nil, CAProfileError.UnknownError)
}
})
}
}
This can be difficult to read. I am trying to simplify this using PromiseKit. First, I want to simplify members.members(completion: { (result, paginator) in to a promise that I can call with the firstly { ... } syntax. I thus try and do as follows:
private func asPromise(members: TCHMembers) -> Promise<TCHMemberPaginator> {
return Promise<TCHMemberPaginator> { fulfill, reject in
members.members(completion: { (result, paginator) in
if result.isSuccesful() {
fulfill(paginator)
} else {
reject()
}
})
}
}
But this approach does not work and I get "Unable to infer closure type in the current context". I'm trying to find a good example of this use case done online but am having trouble. Any thoughts on how to properly return promises?
Assuming the TCHMemberPaginator and TCHMembers as below,
class TCHMemberPaginator {}
class TCHMembers {
func members(completion: (Bool, TCHMemberPaginator?) -> Void) {}
}
Here is the method to return a Promise,
private func asPromise(members: TCHMembers) -> Promise<TCHMemberPaginator> {
return Promise { seal in
members.members(completion: { (result, paginator) in
if result == true, let p = paginator {
seal.fulfill(p)
} else {
seal.reject(NSError())
}
})
}
}

RxSwift Chaining requests that depend from each other

I'm still learning Rx and I'm trying to use RxSwift to make 3 requests that return depend on each other.
* DocumentsA
- CollectionA
* DocumentsB
- CollectionB
* DocumentsC
- CollectionC
The models are something like this:
struct DocumentA {
let documentsB: [DocumentB]
}
struct DocumentB {
let documentsC: [DocumentC]
}
struct DocumentsC {
let name: String
}
So using RxSwift, I'm trying to request each level of the document using separate methods for each document:
func fetchDocumentsA() -> Observable<DocumentA> {
return Observable.create { observer in
fetchDocumentsB().subscribe(onNext: { documentB in
if let documentA = DocumentA(documentB: documentB) {
observer.onNext(documentA)
}
}, onError: nil, onCompleted: {
observer.onCompleted()
}, onDisposed: nil).disposed(by: self.disposeBag)
return Disposables.create()
}
}
func fetchDocumentsB() -> Observable<DocumentB> {
return Observable.create { observer in
fetchDocumentsC().subscribe(onNext: { documentC in
if let documentB = DocumentB(documentC: documentC) {
observer.onNext(documentB)
}
}, onError: nil, onCompleted: {
observer.onCompleted()
}, onDisposed: nil).disposed(by: self.disposeBag)
return Disposables.create()
}
}
func fetchDocumentsC() -> Observable<DocumentC> {
return Observable.create { observer in
fetchName().subscribe(onNext: { name in
observer.onNext(DocumentC(name: name))
}, onError: nil, onCompleted: {
observer.onCompleted()
}, onDisposed: nil).disposed(by: self.disposeBag)
return Disposables.create()
}
}
Is there a better way to do this? It seems very convoluted.
In this case you can make this much nicer with map function, like in example bellow.
The Map operator applies a function of your choosing to each item emitted by the source Observable,
and returns an Observable that emits the results of these function
applications.
ReactiveX
func fetchDocumentsA() -> Observable<DocumentA> {
return fetchDocumentsB().map { (documentB) -> DocumentA in
DocumentA(documentB: documentB)
}
}
func fetchDocumentsB() -> Observable<DocumentB> {
return fetchDocumentsC().map { (documentC) -> DocumentB in
DocumentB(documentC: documentC)
}
}
func fetchDocumentsC() -> Observable<DocumentC> {
return fetchName().map { (name) -> DocumentC in
return DocumentC(name: name)
}
}