Skip to content

Add memoryCacheOnly #18

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

Merged
merged 1 commit into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public final class com/apollographql/cache/normalized/NormalizedCache {
public static final fun getCacheHeaders (Lcom/apollographql/apollo/api/ApolloResponse;)Lcom/apollographql/cache/normalized/api/CacheHeaders;
public static final fun getCacheInfo (Lcom/apollographql/apollo/api/ApolloResponse;)Lcom/apollographql/cache/normalized/CacheInfo;
public static final fun isFromCache (Lcom/apollographql/apollo/api/ApolloResponse;)Z
public static final fun memoryCacheOnly (Lcom/apollographql/apollo/api/MutableExecutionOptions;Z)Ljava/lang/Object;
public static final fun optimisticUpdates (Lcom/apollographql/apollo/ApolloCall;Lcom/apollographql/apollo/api/Mutation$Data;)Lcom/apollographql/apollo/ApolloCall;
public static final fun optimisticUpdates (Lcom/apollographql/apollo/api/ApolloRequest$Builder;Lcom/apollographql/apollo/api/Mutation$Data;)Lcom/apollographql/apollo/api/ApolloRequest$Builder;
public static final fun refetchPolicy (Lcom/apollographql/apollo/api/MutableExecutionOptions;Lcom/apollographql/cache/normalized/FetchPolicy;)Ljava/lang/Object;
Expand All @@ -146,6 +147,7 @@ public final class com/apollographql/cache/normalized/api/ApolloCacheHeaders {
public static final field EVICT_AFTER_READ Ljava/lang/String;
public static final field INSTANCE Lcom/apollographql/cache/normalized/api/ApolloCacheHeaders;
public static final field MAX_STALE Ljava/lang/String;
public static final field MEMORY_CACHE_ONLY Ljava/lang/String;
}

public abstract interface class com/apollographql/cache/normalized/api/ApolloResolver {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOption
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/emitCacheMisses(kotlin/Boolean): com.apollographql.apollo.api/MutableExecutionOptions<#A> // com.apollographql.cache.normalized/emitCacheMisses|emitCacheMisses@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/fetchPolicy(com.apollographql.cache.normalized/FetchPolicy): #A // com.apollographql.cache.normalized/fetchPolicy|fetchPolicy@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(com.apollographql.cache.normalized.FetchPolicy){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/fetchPolicyInterceptor(com.apollographql.apollo.interceptor/ApolloInterceptor): #A // com.apollographql.cache.normalized/fetchPolicyInterceptor|fetchPolicyInterceptor@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(com.apollographql.apollo.interceptor.ApolloInterceptor){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/memoryCacheOnly(kotlin/Boolean): #A // com.apollographql.cache.normalized/memoryCacheOnly|memoryCacheOnly@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/refetchPolicy(com.apollographql.cache.normalized/FetchPolicy): #A // com.apollographql.cache.normalized/refetchPolicy|refetchPolicy@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(com.apollographql.cache.normalized.FetchPolicy){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/refetchPolicyInterceptor(com.apollographql.apollo.interceptor/ApolloInterceptor): #A // com.apollographql.cache.normalized/refetchPolicyInterceptor|refetchPolicyInterceptor@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(com.apollographql.apollo.interceptor.ApolloInterceptor){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/storeExpirationDate(kotlin/Boolean): #A // com.apollographql.cache.normalized/storeExpirationDate|storeExpirationDate@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§<kotlin.Any?>}[0]
Expand All @@ -408,6 +409,8 @@ final object com.apollographql.cache.normalized.api/ApolloCacheHeaders { // com.
final fun <get-EVICT_AFTER_READ>(): kotlin/String // com.apollographql.cache.normalized.api/ApolloCacheHeaders.EVICT_AFTER_READ.<get-EVICT_AFTER_READ>|<get-EVICT_AFTER_READ>(){}[0]
final const val MAX_STALE // com.apollographql.cache.normalized.api/ApolloCacheHeaders.MAX_STALE|{}MAX_STALE[0]
final fun <get-MAX_STALE>(): kotlin/String // com.apollographql.cache.normalized.api/ApolloCacheHeaders.MAX_STALE.<get-MAX_STALE>|<get-MAX_STALE>(){}[0]
final const val MEMORY_CACHE_ONLY // com.apollographql.cache.normalized.api/ApolloCacheHeaders.MEMORY_CACHE_ONLY|{}MEMORY_CACHE_ONLY[0]
final fun <get-MEMORY_CACHE_ONLY>(): kotlin/String // com.apollographql.cache.normalized.api/ApolloCacheHeaders.MEMORY_CACHE_ONLY.<get-MEMORY_CACHE_ONLY>|<get-MEMORY_CACHE_ONLY>(){}[0]
}
final object com.apollographql.cache.normalized.api/DefaultApolloResolver : com.apollographql.cache.normalized.api/ApolloResolver { // com.apollographql.cache.normalized.api/DefaultApolloResolver|null[0]
final fun resolveField(com.apollographql.cache.normalized.api/ResolverContext): kotlin/Any? // com.apollographql.cache.normalized.api/DefaultApolloResolver.resolveField|resolveField(com.apollographql.cache.normalized.api.ResolverContext){}[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,15 @@ fun <T> MutableExecutionOptions<T>.doNotStore(doNotStore: Boolean) = addExecutio
DoNotStoreContext(doNotStore)
)

/**
* @param memoryCacheOnly Whether to store and read from a memory cache only.
*
* Default: false
*/
fun <T> MutableExecutionOptions<T>.memoryCacheOnly(memoryCacheOnly: Boolean) = addExecutionContext(
MemoryCacheOnlyContext(memoryCacheOnly)
)

@Deprecated("Emitting cache misses is now the default behavior, this method is a no-op", replaceWith = ReplaceWith(""))
@ApolloDeprecatedSince(v4_0_0)
@Suppress("UNUSED_PARAMETER")
Expand Down Expand Up @@ -443,6 +452,9 @@ private val <T> MutableExecutionOptions<T>.refetchPolicyInterceptor
internal val <D : Operation.Data> ApolloRequest<D>.doNotStore
get() = executionContext[DoNotStoreContext]?.value ?: false

internal val <D : Operation.Data> ApolloRequest<D>.memoryCacheOnly
get() = executionContext[MemoryCacheOnlyContext]?.value ?: false

internal val <D : Operation.Data> ApolloRequest<D>.storePartialResponses
get() = executionContext[StorePartialResponsesContext]?.value ?: false

Expand Down Expand Up @@ -618,6 +630,13 @@ internal class DoNotStoreContext(val value: Boolean) : ExecutionContext.Element
companion object Key : ExecutionContext.Key<DoNotStoreContext>
}

internal class MemoryCacheOnlyContext(val value: Boolean) : ExecutionContext.Element {
override val key: ExecutionContext.Key<*>
get() = Key

companion object Key : ExecutionContext.Key<MemoryCacheOnlyContext>
}

internal class StorePartialResponsesContext(val value: Boolean) : ExecutionContext.Element {
override val key: ExecutionContext.Key<*>
get() = Key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ object ApolloCacheHeaders {
*/
const val DO_NOT_STORE = "do-not-store"

/**
* Records should be stored and read from the [MemoryCache] only.
*/
const val MEMORY_CACHE_ONLY = "memory-cache-only"

/**
* Records from this request should be evicted after being read.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.apollographql.cache.normalized.cacheHeaders
import com.apollographql.cache.normalized.cacheInfo
import com.apollographql.cache.normalized.doNotStore
import com.apollographql.cache.normalized.fetchFromCache
import com.apollographql.cache.normalized.memoryCacheOnly
import com.apollographql.cache.normalized.optimisticData
import com.apollographql.cache.normalized.storePartialResponses
import com.apollographql.cache.normalized.storeReceiveDate
Expand Down Expand Up @@ -78,6 +79,9 @@ internal class ApolloCacheInterceptor(
if (request.storeReceiveDate) {
cacheHeaders += nowDateCacheHeaders()
}
if (request.memoryCacheOnly) {
cacheHeaders += CacheHeaders.Builder().addHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY, "true").build()
}
store.writeOperation(request.operation, response.data!!, customScalarAdapters, cacheHeaders)
} else {
emptySet()
Expand Down Expand Up @@ -205,10 +209,14 @@ internal class ApolloCacheInterceptor(
val startMillis = currentTimeMillis()

val data = try {
var cacheHeaders = request.cacheHeaders
if (request.memoryCacheOnly) {
cacheHeaders += CacheHeaders.Builder().addHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY, "true").build()
}
store.readOperation(
operation = operation,
customScalarAdapters = customScalarAdapters,
cacheHeaders = request.cacheHeaders
cacheHeaders = cacheHeaders,
)
} catch (e: CacheMissException) {
return ApolloResponse.Builder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class SqlNormalizedCache internal constructor(
}

override fun loadRecord(key: String, cacheHeaders: CacheHeaders): Record? {
if (cacheHeaders.hasHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY)) {
return null
}
val evictAfterRead = cacheHeaders.hasHeader(EVICT_AFTER_READ)
return maybeTransaction(evictAfterRead) {
try {
Expand All @@ -44,6 +47,9 @@ class SqlNormalizedCache internal constructor(
}

override fun loadRecords(keys: Collection<String>, cacheHeaders: CacheHeaders): Collection<Record> {
if (cacheHeaders.hasHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY)) {
return emptyList()
}
val evictAfterRead = cacheHeaders.hasHeader(EVICT_AFTER_READ)
return maybeTransaction(evictAfterRead) {
try {
Expand Down Expand Up @@ -88,7 +94,7 @@ class SqlNormalizedCache internal constructor(

@ApolloExperimental
override fun merge(record: Record, cacheHeaders: CacheHeaders, recordMerger: RecordMerger): Set<String> {
if (cacheHeaders.hasHeader(ApolloCacheHeaders.DO_NOT_STORE)) {
if (cacheHeaders.hasHeader(ApolloCacheHeaders.DO_NOT_STORE) || cacheHeaders.hasHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY)) {
return emptySet()
}
return try {
Expand All @@ -102,7 +108,7 @@ class SqlNormalizedCache internal constructor(

@ApolloExperimental
override fun merge(records: Collection<Record>, cacheHeaders: CacheHeaders, recordMerger: RecordMerger): Set<String> {
if (cacheHeaders.hasHeader(ApolloCacheHeaders.DO_NOT_STORE)) {
if (cacheHeaders.hasHeader(ApolloCacheHeaders.DO_NOT_STORE) || cacheHeaders.hasHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY)) {
return emptySet()
}
return try {
Expand Down
69 changes: 69 additions & 0 deletions tests/normalized-cache/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

plugins {
id("org.jetbrains.kotlin.multiplatform")
id("com.apollographql.apollo")
}

kotlin {
jvm()
macosX64()
macosArm64()
iosArm64()
iosX64()
iosSimulatorArm64()
watchosArm32()
watchosArm64()
watchosSimulatorArm64()
tvosArm64()
tvosX64()
tvosSimulatorArm64()

@OptIn(ExperimentalKotlinGradlePluginApi::class)
applyDefaultHierarchyTemplate {
group("common") {
group("concurrent") {
group("native") {
group("apple")
}
group("jvmCommon") {
withJvm()
}
}
}
}

sourceSets {
getByName("commonMain") {
dependencies {
implementation(libs.apollo.runtime)
}
}

getByName("commonTest") {
dependencies {
implementation(libs.apollo.testing.support)
implementation(libs.apollo.mockserver)
implementation(libs.kotlin.test)
implementation("com.apollographql.cache:normalized-cache-sqlite-incubating")
}
}

getByName("jvmTest") {
dependencies {
implementation(libs.slf4j.nop)
}
}

configureEach {
languageSettings.optIn("com.apollographql.apollo.annotations.ApolloExperimental")
languageSettings.optIn("com.apollographql.apollo.annotations.ApolloInternal")
}
}
}

apollo {
service("service") {
packageName.set("sqlite")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
query GetUser {
user {
name
email
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type Query {
user: User
}

type User {
name: String!
email: String!
admin: Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package test

import com.apollographql.apollo.ApolloClient
import com.apollographql.apollo.exception.CacheMissException
import com.apollographql.apollo.testing.QueueTestNetworkTransport
import com.apollographql.apollo.testing.enqueueTestResponse
import com.apollographql.apollo.testing.internal.runTest
import com.apollographql.cache.normalized.ApolloStore
import com.apollographql.cache.normalized.FetchPolicy
import com.apollographql.cache.normalized.api.MemoryCache
import com.apollographql.cache.normalized.api.MemoryCacheFactory
import com.apollographql.cache.normalized.api.Record
import com.apollographql.cache.normalized.fetchPolicy
import com.apollographql.cache.normalized.memoryCacheOnly
import com.apollographql.cache.normalized.sql.SqlNormalizedCache
import com.apollographql.cache.normalized.sql.SqlNormalizedCacheFactory
import com.apollographql.cache.normalized.store
import sqlite.GetUserQuery
import kotlin.reflect.KClass
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs

class MemoryCacheOnlyTest {
@Test
fun memoryCacheOnlyDoesNotStoreInSqlCache() = runTest {
val store = ApolloStore(MemoryCacheFactory().chain(SqlNormalizedCacheFactory())).also { it.clearAll() }
val apolloClient = ApolloClient.Builder().networkTransport(QueueTestNetworkTransport()).store(store).build()
val query = GetUserQuery()
apolloClient.enqueueTestResponse(query, GetUserQuery.Data(GetUserQuery.User("John", "a@a.com")))
apolloClient.query(query).memoryCacheOnly(true).execute()
val dump: Map<KClass<*>, Map<String, Record>> = store.dump()
assertEquals(2, dump[MemoryCache::class]!!.size)
assertEquals(0, dump[SqlNormalizedCache::class]!!.size)
}

@Test
fun memoryCacheOnlyDoesNotReadFromSqlCache() = runTest {
val store = ApolloStore(MemoryCacheFactory().chain(SqlNormalizedCacheFactory())).also { it.clearAll() }
val query = GetUserQuery()
store.writeOperation(query, GetUserQuery.Data(GetUserQuery.User("John", "a@a.com")))

val store2 = ApolloStore(MemoryCacheFactory().chain(SqlNormalizedCacheFactory()))
val apolloClient = ApolloClient.Builder().serverUrl("unused").store(store2).build()
// The record in is in the SQL cache, but we request not to access it
assertIs<CacheMissException>(
apolloClient.query(query).fetchPolicy(FetchPolicy.CacheOnly).memoryCacheOnly(true).execute().exception
)
}
}
4 changes: 0 additions & 4 deletions tests/pagination/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,4 @@ apollo {
@OptIn(ApolloExperimental::class)
generateDataBuilders.set(true)
}

// Shouldn't be needed after https://github.com/apollographql/apollo-kotlin/blob/e6dfb1a0ba963080b088660ed80691b91b66e54d/libraries/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/DefaultApolloExtension.kt#L279
// is released in the Apollo Gradle plugin.
linkSqlite.set(true)
}
4 changes: 0 additions & 4 deletions tests/sqlite/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,4 @@ apollo {
service("service") {
packageName.set("sqlite")
}

// Shouldn't be needed after https://github.com/apollographql/apollo-kotlin/blob/e6dfb1a0ba963080b088660ed80691b91b66e54d/libraries/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo3/gradle/internal/DefaultApolloExtension.kt#L279
// is released in the Apollo Gradle plugin.
linkSqlite.set(true)
}