Hi! We’re observing some strange behaviour when us...
# apollo-kotlin
w
Hi! We’re observing some strange behaviour when using
cache_only
fetch strategy, namely our Flow with that strategy fails with a network exception. Is it possible that a query with cache_only fetch strategy makes, or triggers (by refetching some previous query), a network call?
m
Since it's a Flow, I'm assuming a
watcher
is involved somewhere?
not that it should really matter, it feels like
cache_only
should never reach the network but trying to understand
w
Yep, it’s
watcher.toFlow()
somewhere deeper
Basically we’re making the same query twice: first with
Cache_Only
strategy, and then with
Network_Only
. We expect the first one to just wait until the second one fetches data and emit the same thing. However, we’re observing two network requests being made
m
I remember some cases where the
fetchStrategy
was ignored because the return value was ignored (the API is a bit weird)
let me check if I can find the issue
Calling
ApolloQueryCall.responseFetcher()
returns a new
ApolloQueryCall
and if the return value isn't used, it's lost forever
w
I’m pretty sure we properly chain the calls always, but you did point me to one piece of our code which might be messing with things, let me check that
👍 1
It’s most certainly not that 😕 And I just verified that we watch the query with network_only strategy exactly once, and it still makes two network calls 🤔
m
😕
Any chance you can share some code or pseudo code?
w
We’re working on some repro but without luck 😕
m
Mmmm, could it be another watcher that trigger the network call?
When data is normalized, it's hard to keep track of who's listening to what key
w
could it be another watcher that trigger the network call?
But then should the network call be returned from the Flow started with cache_only fetch strategy?
it’s hard to keep track of who’s listening to what key
Definitely, we already fixed a bunch of places where we didn’t request normalized cache
id
for a given type, but only in some queries. This resolved some issues and now we’re pretty sure we’re requesting all `id`s. There is another query which fetches part of the same data that the one that’s failing though
m
But then should the network call be returned from the Flow started with cache_only fetch strategy?
It shouldn't. How do you know the data came from the network ?
isFromCache == false
?
w
I’m sorry I meant
network error
m
Ah right
What is the exception you're getting?
Maybe we're mapping to a network error for something else? (that wouldn't be great but you never know...)
w
Basically we have
Copy code
query Foo {
  foo { ...fooData }
  bar { ...barData }
}

query Bar {
  bar { ...barData }
}
Foo is started twice: with
cache_only
and with
network_only
strategy. Bar is started once, with
cache_and_network
. We observe the Foo/network_only query returning successfully with data, Foo/cache_only fails with network error. We’re investigating which
Bar
might be an issue now, because we do have something like 5-6 queries being made it seems 😕
Maybe we’re mapping to a network error for something else?
Unlikely, the exception is clearly a network error, it has cause with some OkHttp type with timeout specified etc.
👍 1
m
Is
Foo/network_only
a watcher as well or is it one-shot?
There's this issue about multiple watchers calling each other https://github.com/apollographql/apollo-android/issues/2226. But I have never been able to reproduce
w
Is 
Foo/network_only
 a watcher as well or is it one-shot?
It’s a watcher but we terminate it immediately with
first()
👍 1
m
So you're basically doing this:
Copy code
apolloClient.query(FooQuery()).responseFetcher(ApolloResponseFetchers.CACHE_ONLY).watcher().toFlow().collect {
  println(it.data)
}
And this throws a ApolloNetworkException?
That's weird, the
CACHE_ONLY
stuff is pretty explicit about bypassing the network
w
Yep, exactly. The flows are pretty much straightforward, we do have some mappers and stuff along the way but there’s no real chance we’d do something weird
m
It would still be interesting to see that exception stacktrace
If everything goes well, there shouldn't be any sign of
ApolloServerInterceptor
there
👍 1
w
We just isolated the change that triggers this issue, and it’s fetching a completely unrelated query, with
CACHE_FIRST
strategy. And it’s a query for extremely simple object, no ids no nothing
Just an update, this is part of the stack trace that’s particularly suspicious:
Copy code
at com.apollographql.apollo.internal.RealApolloQueryWatcher.refetch(RealApolloQueryWatcher.java:115)
at com.apollographql.apollo.internal.RealApolloQueryWatcher$1.onCacheRecordsChanged(RealApolloQueryWatcher.java:40)
at com.apollographql.apollo.internal.RealApolloStore.publish(RealApolloStore.java:117)
at com.apollographql.apollo.internal.interceptor.ApolloCacheInterceptor$7.run(ApolloCacheInterceptor.java:218)
at com.apollographql.apollo.internal.interceptor.ApolloCacheInterceptor.publishCacheKeys(ApolloCacheInterceptor.java:215)
at com.apollographql.apollo.internal.interceptor.ApolloCacheInterceptor.cacheResponseAndPublishSynchronously(ApolloCacheInterceptor.java:170)
at com.apollographql.apollo.internal.interceptor.ApolloCacheInterceptor.cacheResponseAndPublish(ApolloCacheInterceptor.java:159)
specifically the chain:
cacheResponseAndPublishSynchronously
->
onCacheRecordsChanged
->
refetch
m
Sorry for the late response. Good catch, looks like the default reftechResponseFetcher is
CACHE_FIRST
: https://github.com/apollographql/apollo-android/blob/3e122b4ff44fff3109911dd24177a[…]a/com/apollographql/apollo/internal/RealApolloQueryWatcher.java
So maybe something like below would do:
Copy code
apolloClient.query(FooQuery())
  .responseFetcher(ApolloResponseFetchers.CACHE_ONLY)
  .watcher()
  .refetchResponseFetcher(ApolloResponseFetchers.CACHE_ONLY)
  .toFlow()
  .collect {}
👍 1
I would have expected the default
refetchResponseFetcher
to be the same as
responseFetcher
but maybe there's a reason why it's not the case
w
Will check it out
If anyone stumbles on this thread in the future: it turned out that we were observing two separate issues: • https://github.com/apollographql/apollo-android/pull/2795 — query watchers would always use
CACHE_FIRST
refetch strategy, even if the query was specified as
CACHE_ONLY
(so a cache_only queries would still make a network call on a cache miss) • https://github.com/apollographql/apollo-android/issues/2798 — where queries that didn’t yet receive any response (
CACHE_ONLY
are the easiest ones) would trigger a refetch even when a completely unrelated query populated the normalized cache Many thanks to Martin for taking the time to figure this out! 🙂
👍 2