Skip to content

Commit f406bca

Browse files
authored
Add memoryCacheOnly (#6049)
* Add memoryCacheOnly * Move tests to concurrentTest
1 parent 78b466e commit f406bca

File tree

10 files changed

+103
-3
lines changed

10 files changed

+103
-3
lines changed

libraries/apollo-normalized-cache-api/api/apollo-normalized-cache-api.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ public final class com/apollographql/apollo/cache/normalized/api/ApolloCacheHead
22
public static final field DO_NOT_STORE Ljava/lang/String;
33
public static final field EVICT_AFTER_READ Ljava/lang/String;
44
public static final field INSTANCE Lcom/apollographql/apollo/cache/normalized/api/ApolloCacheHeaders;
5+
public static final field MEMORY_CACHE_ONLY Ljava/lang/String;
56
}
67

78
public final class com/apollographql/apollo/cache/normalized/api/CacheHeaders {

libraries/apollo-normalized-cache-api/api/apollo-normalized-cache-api.klib.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ final object com.apollographql.apollo.cache.normalized.api/ApolloCacheHeaders {
142142
final fun <get-DO_NOT_STORE>(): kotlin/String // com.apollographql.apollo.cache.normalized.api/ApolloCacheHeaders.DO_NOT_STORE.<get-DO_NOT_STORE>|<get-DO_NOT_STORE>(){}[0]
143143
final const val EVICT_AFTER_READ // com.apollographql.apollo.cache.normalized.api/ApolloCacheHeaders.EVICT_AFTER_READ|{}EVICT_AFTER_READ[0]
144144
final fun <get-EVICT_AFTER_READ>(): kotlin/String // com.apollographql.apollo.cache.normalized.api/ApolloCacheHeaders.EVICT_AFTER_READ.<get-EVICT_AFTER_READ>|<get-EVICT_AFTER_READ>(){}[0]
145+
final const val MEMORY_CACHE_ONLY // com.apollographql.apollo.cache.normalized.api/ApolloCacheHeaders.MEMORY_CACHE_ONLY|{}MEMORY_CACHE_ONLY[0]
146+
final fun <get-MEMORY_CACHE_ONLY>(): kotlin/String // com.apollographql.apollo.cache.normalized.api/ApolloCacheHeaders.MEMORY_CACHE_ONLY.<get-MEMORY_CACHE_ONLY>|<get-MEMORY_CACHE_ONLY>(){}[0]
145147
}
146148
final object com.apollographql.apollo.cache.normalized.api/DefaultCacheResolver : com.apollographql.apollo.cache.normalized.api/CacheResolver { // com.apollographql.apollo.cache.normalized.api/DefaultCacheResolver|null[0]
147149
final fun resolveField(com.apollographql.apollo.api/CompiledField, com.apollographql.apollo.api/Executable.Variables, kotlin.collections/Map<kotlin/String, kotlin/Any?>, kotlin/String): kotlin/Any? // com.apollographql.apollo.cache.normalized.api/DefaultCacheResolver.resolveField|resolveField(com.apollographql.apollo.api.CompiledField;com.apollographql.apollo.api.Executable.Variables;kotlin.collections.Map<kotlin.String,kotlin.Any?>;kotlin.String){}[0]

libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo/cache/normalized/api/ApolloCacheHeaders.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ object ApolloCacheHeaders {
1111
*/
1212
const val DO_NOT_STORE = "do-not-store"
1313

14+
15+
/**
16+
* Records should be stored and read from the [MemoryCache] only.
17+
*/
18+
const val MEMORY_CACHE_ONLY = "memory-cache-only"
19+
1420
/**
1521
* Records from this request should be evicted after being read.
1622
*/

libraries/apollo-normalized-cache-sqlite/src/commonMain/kotlin/com/apollographql/apollo/cache/normalized/sql/SqlNormalizedCache.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ class SqlNormalizedCache internal constructor(
1515
) : NormalizedCache() {
1616

1717
override fun loadRecord(key: String, cacheHeaders: CacheHeaders): Record? {
18+
if (cacheHeaders.hasHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY)) {
19+
return null
20+
}
1821
val record = try {
1922
recordDatabase.select(key)
2023
} catch (e: Exception) {
@@ -32,6 +35,9 @@ class SqlNormalizedCache internal constructor(
3235
}
3336

3437
override fun loadRecords(keys: Collection<String>, cacheHeaders: CacheHeaders): Collection<Record> {
38+
if (cacheHeaders.hasHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY)) {
39+
return emptyList()
40+
}
3541
val records = try {
3642
internalGetRecords(keys)
3743
} catch (e: Exception) {
@@ -77,7 +83,7 @@ class SqlNormalizedCache internal constructor(
7783
}
7884

7985
override fun merge(records: Collection<Record>, cacheHeaders: CacheHeaders): Set<String> {
80-
if (cacheHeaders.hasHeader(ApolloCacheHeaders.DO_NOT_STORE)) {
86+
if (cacheHeaders.hasHeader(ApolloCacheHeaders.DO_NOT_STORE) || cacheHeaders.hasHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY)) {
8187
return emptySet()
8288
}
8389
return try {
@@ -94,7 +100,7 @@ class SqlNormalizedCache internal constructor(
94100
}
95101

96102
override fun merge(record: Record, cacheHeaders: CacheHeaders): Set<String> {
97-
if (cacheHeaders.hasHeader(ApolloCacheHeaders.DO_NOT_STORE)) {
103+
if (cacheHeaders.hasHeader(ApolloCacheHeaders.DO_NOT_STORE) || cacheHeaders.hasHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY)) {
98104
return emptySet()
99105
}
100106
return try {

libraries/apollo-normalized-cache/api/apollo-normalized-cache.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public final class com/apollographql/apollo/cache/normalized/NormalizedCache {
114114
public static final fun getCacheHeaders (Lcom/apollographql/apollo/api/ApolloResponse;)Lcom/apollographql/apollo/cache/normalized/api/CacheHeaders;
115115
public static final fun getCacheInfo (Lcom/apollographql/apollo/api/ApolloResponse;)Lcom/apollographql/apollo/cache/normalized/CacheInfo;
116116
public static final fun isFromCache (Lcom/apollographql/apollo/api/ApolloResponse;)Z
117+
public static final fun memoryCacheOnly (Lcom/apollographql/apollo/api/MutableExecutionOptions;Z)Ljava/lang/Object;
117118
public static final fun optimisticUpdates (Lcom/apollographql/apollo/ApolloCall;Lcom/apollographql/apollo/api/Mutation$Data;)Lcom/apollographql/apollo/ApolloCall;
118119
public static final fun optimisticUpdates (Lcom/apollographql/apollo/api/ApolloRequest$Builder;Lcom/apollographql/apollo/api/Mutation$Data;)Lcom/apollographql/apollo/api/ApolloRequest$Builder;
119120
public static final fun refetchPolicy (Lcom/apollographql/apollo/api/MutableExecutionOptions;Lcom/apollographql/apollo/cache/normalized/FetchPolicy;)Ljava/lang/Object;

libraries/apollo-normalized-cache/api/apollo-normalized-cache.klib.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOption
101101
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.apollo.cache.normalized/emitCacheMisses(kotlin/Boolean): com.apollographql.apollo.api/MutableExecutionOptions<#A> // com.apollographql.apollo.cache.normalized/emitCacheMisses|emitCacheMisses@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§<kotlin.Any?>}[0]
102102
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.apollo.cache.normalized/fetchPolicy(com.apollographql.apollo.cache.normalized/FetchPolicy): #A // com.apollographql.apollo.cache.normalized/fetchPolicy|[email protected]<0:0>(com.apollographql.apollo.cache.normalized.FetchPolicy){0§<kotlin.Any?>}[0]
103103
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.apollo.cache.normalized/fetchPolicyInterceptor(com.apollographql.apollo.interceptor/ApolloInterceptor): #A // com.apollographql.apollo.cache.normalized/fetchPolicyInterceptor|fetchPolicyInterceptor@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(com.apollographql.apollo.interceptor.ApolloInterceptor){0§<kotlin.Any?>}[0]
104+
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.apollo.cache.normalized/memoryCacheOnly(kotlin/Boolean): #A // com.apollographql.apollo.cache.normalized/memoryCacheOnly|memoryCacheOnly@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§<kotlin.Any?>}[0]
104105
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.apollo.cache.normalized/refetchPolicy(com.apollographql.apollo.cache.normalized/FetchPolicy): #A // com.apollographql.apollo.cache.normalized/refetchPolicy|refetchPolicy@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(com.apollographql.apollo.cache.normalized.FetchPolicy){0§<kotlin.Any?>}[0]
105106
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.apollo.cache.normalized/refetchPolicyInterceptor(com.apollographql.apollo.interceptor/ApolloInterceptor): #A // com.apollographql.apollo.cache.normalized/refetchPolicyInterceptor|refetchPolicyInterceptor@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(com.apollographql.apollo.interceptor.ApolloInterceptor){0§<kotlin.Any?>}[0]
106107
final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.apollo.cache.normalized/storePartialResponses(kotlin/Boolean): #A // com.apollographql.apollo.cache.normalized/storePartialResponses|storePartialResponses@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§<kotlin.Any?>}[0]

libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo/cache/normalized/ClientCacheExtensions.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,15 @@ fun <T> MutableExecutionOptions<T>.doNotStore(doNotStore: Boolean) = addExecutio
278278
DoNotStoreContext(doNotStore)
279279
)
280280

281+
/**
282+
* @param memoryOnly Whether to store and read from a memory cache only.
283+
*
284+
* Default: false
285+
*/
286+
fun <T> MutableExecutionOptions<T>.memoryCacheOnly(memoryOnly: Boolean) = addExecutionContext(
287+
MemoryCacheOnlyContext(memoryOnly)
288+
)
289+
281290
@Deprecated("Emitting cache misses is now the default behavior, this method is a no-op", replaceWith = ReplaceWith(""))
282291
@ApolloDeprecatedSince(v4_0_0)
283292
@Suppress("UNUSED_PARAMETER")
@@ -405,6 +414,9 @@ private val <T> MutableExecutionOptions<T>.refetchPolicyInterceptor
405414
internal val <D : Operation.Data> ApolloRequest<D>.doNotStore
406415
get() = executionContext[DoNotStoreContext]?.value ?: false
407416

417+
internal val <D : Operation.Data> ApolloRequest<D>.memoryCacheOnly
418+
get() = executionContext[MemoryCacheOnlyContext]?.value ?: false
419+
408420
internal val <D : Operation.Data> ApolloRequest<D>.storePartialResponses
409421
get() = executionContext[StorePartialResponsesContext]?.value ?: false
410422

@@ -578,6 +590,13 @@ internal class DoNotStoreContext(val value: Boolean) : ExecutionContext.Element
578590
companion object Key : ExecutionContext.Key<DoNotStoreContext>
579591
}
580592

593+
internal class MemoryCacheOnlyContext(val value: Boolean) : ExecutionContext.Element {
594+
override val key: ExecutionContext.Key<*>
595+
get() = Key
596+
597+
companion object Key : ExecutionContext.Key<MemoryCacheOnlyContext>
598+
}
599+
581600
internal class StorePartialResponsesContext(val value: Boolean) : ExecutionContext.Element {
582601
override val key: ExecutionContext.Key<*>
583602
get() = Key

libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo/cache/normalized/internal/ApolloCacheInterceptor.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import com.apollographql.apollo.cache.normalized.cacheHeaders
1717
import com.apollographql.apollo.cache.normalized.cacheInfo
1818
import com.apollographql.apollo.cache.normalized.doNotStore
1919
import com.apollographql.apollo.cache.normalized.fetchFromCache
20+
import com.apollographql.apollo.cache.normalized.memoryCacheOnly
2021
import com.apollographql.apollo.cache.normalized.optimisticData
2122
import com.apollographql.apollo.cache.normalized.storePartialResponses
2223
import com.apollographql.apollo.cache.normalized.storeReceiveDate
@@ -78,6 +79,9 @@ internal class ApolloCacheInterceptor(
7879
if (request.storeReceiveDate) {
7980
cacheHeaders += nowDateCacheHeaders()
8081
}
82+
if (request.memoryCacheOnly) {
83+
cacheHeaders += CacheHeaders.Builder().addHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY, "true").build()
84+
}
8185
store.writeOperation(request.operation, response.data!!, customScalarAdapters, cacheHeaders, publish = false)
8286
} else {
8387
emptySet()
@@ -206,10 +210,14 @@ internal class ApolloCacheInterceptor(
206210
val startMillis = currentTimeMillis()
207211

208212
val data = try {
213+
var cacheHeaders = request.cacheHeaders
214+
if (request.memoryCacheOnly) {
215+
cacheHeaders += CacheHeaders.Builder().addHeader(ApolloCacheHeaders.MEMORY_CACHE_ONLY, "true").build()
216+
}
209217
store.readOperation(
210218
operation = operation,
211219
customScalarAdapters = customScalarAdapters,
212-
cacheHeaders = request.cacheHeaders
220+
cacheHeaders = cacheHeaders
213221
)
214222
} catch (e: CacheMissException) {
215223
return ApolloResponse.Builder(

tests/integration-tests/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ kotlin {
2828
}
2929
}
3030

31+
findByName("concurrentTest")?.apply {
32+
dependencies {
33+
implementation(libs.apollo.normalizedcache.sqlite)
34+
}
35+
}
36+
3137
findByName("jvmTest")?.apply {
3238
dependencies {
3339
implementation(libs.okhttp.logging)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package test
2+
3+
import com.apollographql.apollo.ApolloClient
4+
import com.apollographql.apollo.cache.normalized.ApolloStore
5+
import com.apollographql.apollo.cache.normalized.FetchPolicy
6+
import com.apollographql.apollo.cache.normalized.api.MemoryCache
7+
import com.apollographql.apollo.cache.normalized.api.MemoryCacheFactory
8+
import com.apollographql.apollo.cache.normalized.api.Record
9+
import com.apollographql.apollo.cache.normalized.fetchPolicy
10+
import com.apollographql.apollo.cache.normalized.memoryCacheOnly
11+
import com.apollographql.apollo.cache.normalized.sql.SqlNormalizedCache
12+
import com.apollographql.apollo.cache.normalized.sql.SqlNormalizedCacheFactory
13+
import com.apollographql.apollo.cache.normalized.store
14+
import com.apollographql.apollo.exception.CacheMissException
15+
import com.apollographql.apollo.integration.normalizer.HeroNameQuery
16+
import com.apollographql.apollo.testing.QueueTestNetworkTransport
17+
import com.apollographql.apollo.testing.enqueueTestResponse
18+
import com.apollographql.apollo.testing.internal.runTest
19+
import kotlin.reflect.KClass
20+
import kotlin.test.Test
21+
import kotlin.test.assertEquals
22+
import kotlin.test.assertIs
23+
24+
class MemoryCacheOnlyTest {
25+
@Test
26+
fun memoryCacheOnlyDoesNotStoreInSqlCache() = runTest {
27+
val store = ApolloStore(MemoryCacheFactory().chain(SqlNormalizedCacheFactory())).also { it.clearAll() }
28+
val apolloClient = ApolloClient.Builder().networkTransport(QueueTestNetworkTransport()).store(store).build()
29+
val query = HeroNameQuery()
30+
apolloClient.enqueueTestResponse(query, HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")))
31+
apolloClient.query(query).memoryCacheOnly(true).execute()
32+
val dump: Map<KClass<*>, Map<String, Record>> = store.dump()
33+
assertEquals(2, dump[MemoryCache::class]!!.size)
34+
assertEquals(0, dump[SqlNormalizedCache::class]!!.size)
35+
}
36+
37+
@Test
38+
fun memoryCacheOnlyDoesNotReadFromSqlCache() = runTest {
39+
val store = ApolloStore(MemoryCacheFactory().chain(SqlNormalizedCacheFactory())).also { it.clearAll() }
40+
val query = HeroNameQuery()
41+
store.writeOperation(query, HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")))
42+
43+
val store2 = ApolloStore(MemoryCacheFactory().chain(SqlNormalizedCacheFactory()))
44+
val apolloClient = ApolloClient.Builder().serverUrl("unused").store(store2).build()
45+
// The record in is in the SQL cache, but we request not to access it
46+
assertIs<CacheMissException>(
47+
apolloClient.query(query).fetchPolicy(FetchPolicy.CacheOnly).memoryCacheOnly(true).execute().exception
48+
)
49+
}
50+
}

0 commit comments

Comments
 (0)