Summary
Hello,
I am experiencing problems using the http cache. We currently configure the http cache to use the CACHE_FIRST policy.
Based on the documentation, apollo will retrieve data from the cache if it exists and is not expired.
My server is returning the response to Cache control max-age 60. But the cache is never expiring, causing a problem in the application to never update the data again.
Version
We are using version 1.3.0
.builder()
.serverUrl(settings.environment())
.useHttpGetMethodForQueries(true)
.useHttpGetMethodForPersistedQueries(true)
.enableAutoPersistedQueries(true)
.addCustomTypeAdapter(CustomType.DATE, DateTypeAdapter())
.defaultHttpCachePolicy(
HttpCachePolicy.CACHE_FIRST
)
.httpCache(
ApolloHttpCache(
DiskLruHttpCacheStore(File(settings.cacheDir(), CACHE_APOLLO), CACHE_SIZE)
)
)
.defaultResponseFetcher(ApolloResponseFetchers.CACHE_FIRST)
.normalizedCache(
LruNormalizedCacheFactory(
EvictionPolicy.builder().maxSizeBytes(
CACHE_SIZE
).build()
)
)
.okHttpClient(
OkHttpClient()
.newBuilder()
.connectTimeout(settings.timeout(), TimeUnit.SECONDS)
.readTimeout(settings.timeout(), TimeUnit.SECONDS)
.writeTimeout(settings.timeout(), TimeUnit.SECONDS)
.callTimeout(settings.timeout(), TimeUnit.SECONDS)
.addNetworkInterceptor(
HttpLoggingInterceptor().setLevel(
if (DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
)
)
.addInterceptor(providerHeaderInterceptor())
.build()
)
.build()
My server is returning the response to Cache control max-age 60
Right now Apollo doesn't support server cache policy, only explicitly set by client is supported.
com.apollographql.apollo.api.cache.http.HttpCachePolicy.ExpirePolicy#expireAfter
Is there any way currently to always request the data and if the request fails or backend out, return the data from the cache?
@halysongoncalves what you need sounds very familiar with this feature we have: https://github.com/apollographql/apollo-android/blob/master/apollo-runtime/src/main/java/com/apollographql/apollo/fetcher/ApolloResponseFetchers.java#L46
Can you have a look?
I tried to update but it does not respect the cache control directives
+1
Closing this for now since this is already supported with responseFetcher API. Please open an issue with reproducible case if that is not working.
In my setup ApolloResponseFetchers.CACHE_FIRST (apollo-runtime 2.5.4) also does not pay heed to the Cache-Control headers, queries always reach the network.
These are example headers:
Cache-Control: max-age=30, must-revalidate
Last-Modified: Thu, 25 Feb 2021 16:40:52 GMT
Expires: Thu, 25 Feb 2021 16:41:22 GMT
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
vary: accept-encoding
Content-Encoding: gzip
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 25 Feb 2021 16:40:52 GMT
Keep-Alive: timeout=60
Connection: keep-alive
So requests with the header above should be cached for 30 seconds.
I can set the cache TTL via
_httpCachePolicy(HttpCachePolicy.CACHE_FIRST.expireAfter(..._
but I would prefer if the server controls it via the Cache-Control header it sends out.
Reopened and changed the title to make it explicit that this issue is about server-side cache control. I'm curious how much of that could maybe be achieved through OkHttp caching? https://square.github.io/okhttp/caching/
AFAIK OkHttp-Caching only works for GET-Requests; I also use a separate OkHttp-Cache layer apart from the Apollo-Cache and the former does not affect the Apollo queries.
Update
For my setup it would already be a big improvement if we could read the HTTP headers from _com.apollographql.apollo.api.Response_ - then I could use a custom solution for the caching. Unfortunately this solution is not generic enough to be part of your API.
@semaphore3000 Can you try something like this to read the HTTP headers?
val okHttpResponse = apolloResponse.executionContext[OkHttpExecutionContext]?.response
val value = okHttpResponse?.header("Cache-Control")
IIRC this was added for exactly that purpose
Thank you, @martinbonnin. This looks like if it could work for my use case. I will try it out later.