Store icon indicating copy to clipboard operation
Store copied to clipboard

"The MaybeSource is empty" error occurs with persister

Open jeonginc opened this issue 7 years ago • 6 comments

In README.md, there's a sample code contains:

           .persister(new Persister<BufferedSource>() {
             @Override
             public Maybe<BufferedSource> read(Integer key) {
               if (dataIsCached) {
                 return Observable.fromCallable(() -> userImplementedCache.get(key));
               } else {
                 return Observable.**empty**();
               }
             }

With this reference code, I wrote my own persister like:

StoreBuilder.
    .key<ApiKey<API, Any>, Any>()
    .persister(object : Persister<Any, ApiKey<API, Any>> {
        override fun read(key: ApiKey<API, Any>): Maybe<Any> {
            if (key.haveLocal()) {
                return key.local()
            }
            // Nothing is saved for the key, returns empty
            return Maybe.empty()
        }
        ....
    })
    .fetcher({ it.request(api).computation() })
    .open()

and then i made a request like store.fetch(key)

But it occurs java.util.NoSuchElementException: The MaybeSource is empty

I followed inside RealInternalStore.java, there's weird code in RealInternalStore.response()

    @Nonnull
    Single<Parsed> response(@Nonnull final Key key) {
        return fetcher()
                ...
                        .flatMap(aBoolean -> readDisk(key).toSingle()))
                ...

readDisk() doesn't have code for handling empty state, readDisk(key).toSingle() can occurs NoSuchElementException

How can I return "empty, there's no saved response" state in Persister.read()?

jeonginc avatar Jan 15 '18 06:01 jeonginc

That's what confuses me most, why return Maybe when you should convert it to Single?

Xiadalei avatar Jan 16 '18 09:01 Xiadalei

I'm confused how it can be empty when you do store.fetch. Fetch should get a new element from your fetcher save it to disk and then return. Could you post a failing test or sample please.

digitalbuddha avatar Mar 29 '18 11:03 digitalbuddha

@digitalbuddha What if what the fetch result is empty from very beginning?

What we normally would do is get and the get would fetch when it gets nothing (that's Maybe.empty()) from persister. That what the API designed for.

Now, if we could fetch nothing from network etc. (of course there's a response but nothing we could write into persister and use case like this is not rare), the fetch would let persister write (which would write nothing) and read( also read nothing just like when it get) and error is coming?

One may say hey, why don't you return a empty object (not null) from persister for the situation like this. If we do that, when we get, we get a empty object and that's all, we have to do the "get then fetch" ourselves which should be done by store itself.

So the problem is why should we assume that fetch could always get new stuff and not empty?

Xiadalei avatar Mar 30 '18 05:03 Xiadalei

Using Maybe.just(Unit) for empty state inside Persister will remove Fetcher's result. So, i have no choice but to implement my own persister on Fetcher like

StoreBuilder.
    .key<ApiKey<API, Any>, Any>()
    .fetcher({ key ->
        key.load(context)
                .switchIfEmpty(key.fetch(api))
                .switchIfEmpty(Maybe.error<Any>(Exception("No response")))
                .doOnSuccess { key.save(context, it).subscribe() }
                .toSingle()
    })
    .open()

jeonginc avatar Apr 04 '18 07:04 jeonginc

I have same problem - how to distinguish between "not found in cache" and "not found on API" (both should return list of items) in persister's read method without throwing this excepion?

LukasVykuka avatar Apr 28 '18 18:04 LukasVykuka

Thanks for explaining! We are working on store4 and will make an elegant solution. I'll post possible solutions here. Open to suggestions as well

digitalbuddha avatar Apr 28 '18 19:04 digitalbuddha