Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpCache configuration #5878

Open
kapilsukhyani opened this issue May 7, 2024 · 5 comments
Open

HttpCache configuration #5878

kapilsukhyani opened this issue May 7, 2024 · 5 comments

Comments

@kapilsukhyani
Copy link

kapilsukhyani commented May 7, 2024

Question

Can an HTTP cache be shared for multiple ApolloClient instances?

Although the cache policy that we have for now is NetworkOnly, it seems like the queries do get written to the cache respectively here.

Right now we have two clients using the same cache with same cache policy of NetworkOnly but we do see issues with cache where the tmp cache file is sometimes not found, and my assumption here is, that it probably is because of the same cache being used, where a query execution through one client might delete the same file as being used by the other client.

java.io.FileNotFoundException: ..../cache/apolloCache/journal.tmp -> ..../cache/apolloCache/journal
    at okio.NioSystemFileSystem.atomicMove(NioSystemFileSystem.kt:80)
    at com.apollographql.apollo3.cache.http.internal.DiskLruCacheKt.rename(DiskLruCache.kt:1019)
    at com.apollographql.apollo3.cache.http.internal.DiskLruCacheKt.access$rename(DiskLruCache.kt:1)
    at com.apollographql.apollo3.cache.http.internal.DiskLruCache.rebuildJournal(DiskLruCache.kt:371)
    at com.apollographql.apollo3.cache.http.internal.DiskLruCache.initialize(DiskLruCache.kt:225)
    at com.apollographql.apollo3.cache.http.internal.DiskLruCache.edit(DiskLruCache.kt:410)
    at com.apollographql.apollo3.cache.http.internal.DiskLruCache.edit(DiskLruCache.kt:404)
    at com.apollographql.apollo3.cache.http.DiskLruHttpCache.write(DiskLruHttpCache.kt:58)
    at com.apollographql.apollo3.cache.http.CachingHttpInterceptor.networkMightThrow(CachingHttpInterceptor.kt:112)
    at com.apollographql.apollo3.cache.http.CachingHttpInterceptor.intercept(CachingHttpInterceptor.kt:57)
    at com.apollographql.apollo3.network.http.DefaultHttpInterceptorChain.proceed(HttpInterceptor.kt:22)
    at com.apollographql.apollo3.cache.http.HttpCache$httpCache$1.intercept(HttpCacheExtensions.kt:87)
    at com.apollographql.apollo3.network.http.DefaultHttpInterceptorChain.proceed(HttpInterceptor.kt:22)
    at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invokeSuspend(HttpNetworkTransport.kt:65)
    at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invoke(HttpNetworkTransport.kt:0)
    at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invoke(HttpNetworkTransport.kt:0)
    at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invoke(HttpNetworkTransport.kt:0)
    at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invoke(HttpNetworkTransport.kt:0)
@martinbonnin
Copy link
Contributor

Hi 👋

Can you share how you're setting up the HTTP cache?

DiskLruCache accesses are synchronized so I would expect that part to write concurrently as long as it's the same instance of DiskLruCache being used.

@kapilsukhyani
Copy link
Author

Here is what it looks like

@[Provides AccountScope]
 fun provideApolloClient(
        site: Site,
        @Authenticated okHttpClient: OkHttpClient,
        @CacheDir cacheDir: File,
        @SentryApolloIntegration sentryApolloIntegration: Boolean,
    ): ApolloClient {
        return buildApolloClient(
            site.graphQLUrl,
            okHttpClient,
            cacheDir
            ApolloBreadcrumbInterceptor("cc-gql"),
            sentryApolloIntegration = sentryApolloIntegration,
        )
    }

    @[Provides AccountScope Agg]
    fun provideAggApolloClient(
        site: Site,
        @Authenticated okHttpClient: OkHttpClient,
        @CacheDir cacheDir: File,
        @SentryApolloIntegration sentryApolloIntegration: Boolean,
    ): ApolloClient {
        return buildApolloClient(
            site.aggGraphQLUrl,
            okHttpClient,
            cacheDir,
            ApolloBreadcrumbInterceptor("AGG"),
            sentryApolloIntegration = sentryApolloIntegration,
        )
    }

    @[Provides CacheDir]
    fun provideCacheDir(application: Application): File = application.cacheDir
    
    
    fun buildApolloClient(
    graphQLUrl: HttpUrl,
    okHttpClient: OkHttpClient,
    cacheDir: File
    interceptor: ApolloInterceptor,
    sentryApolloIntegration: Boolean = false,
    additionalInterceptors: List<ApolloInterceptor> = emptyList()
): ApolloClient {
    val builder = ApolloClient.Builder()
        .serverUrl(graphQLUrl.toString())
        .httpCache(File(cacheDir, "apolloCache"), HTTP_CACHE_SIZE)
        .httpFetchPolicy(HttpFetchPolicy.NetworkOnly)
        .okHttpClient(okHttpClient)
        .addInterceptor(interceptor)
        .run {
            if (sentryApolloIntegration) {
                addInterceptor(SentryApollo3Interceptor())
            } else {
                this
            }
        }
        .addHttpInterceptor(LoggingApollo3HttpInterceptor())
    additionalInterceptors.forEach {
        builder.addInterceptor(it)
    }
    return builder.build()
}

@martinbonnin
Copy link
Contributor

Thanks for sending this! My DI skills are a bit lacking (what is @[Provides CacheDir]? Dagger? Something else?) but my guess is that you're sharing the CacheDir which isn't locked. Instead, you should share the DiskLruHttpCache and use the httpCache() overload that takes an ApolloHttpCache.

@kapilsukhyani
Copy link
Author

kapilsukhyani commented May 13, 2024

provideCacheDir provides a new instance of a File, but essentially points to the same location, and also the said overload is not available on 3.8.2, the version we are using.

3.8.2, would essentially an instance of cache for each client configured separately, which could then produce the issue as mentioned in the description

For now though, I am choosing to use different sub directory for each client, which should also prevent such issues

@martinbonnin
Copy link
Contributor

I think 3.x should have the overload? See https://github.com/apollographql/apollo-kotlin/blob/release-3.x/libraries/apollo-http-cache/src/main/kotlin/com/apollographql/apollo3/cache/http/HttpCacheExtensions.kt#L82. I would say something like so:

   val sharedCache = DiskLruHttpCache(FileSystem.SYSTEM, File("someDirectory"), HTTP_CACHE_SIZE)
   ApolloClient.Builder()
    .serverUrl("https://...")
    .httpCache(sharedCache)
    ...
    .build()

  // Same with other clients

But using different directories work too 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants