Apollo-android: [HTTP Cache] Support Server Cache-Control headers

Created on 20 Feb 2020  路  11Comments  路  Source: apollographql/apollo-android

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()
Feature http-cache

All 11 comments

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.

Was this page helpful?
0 / 5 - 0 ratings