Skip to content
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

PUT_CHANGELOG_HERE

- Add `ReadOnlyNormalizedCache.nextCache` and `ReadOnlyNormalizedCache.size()` (#280)

# v1.0.0-beta.1
_2025-12-09_

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ public final class com/apollographql/cache/normalized/sql/SqlNormalizedCache : c
public fun clearAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun dump (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getNextCache ()Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;
public fun loadRecord-7OwBRqc (Ljava/lang/String;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun loadRecords (Ljava/util/Collection;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun merge (Lcom/apollographql/cache/normalized/api/Record;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lcom/apollographql/cache/normalized/api/RecordMerger;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun merge (Ljava/util/Collection;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lcom/apollographql/cache/normalized/api/RecordMerger;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun remove (Ljava/util/Collection;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun remove-7OwBRqc (Ljava/lang/String;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun size (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun sizeOfRecord (Lcom/apollographql/cache/normalized/api/Record;)I
public fun trim (JFLkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
Expand Down
2 changes: 2 additions & 0 deletions normalized-cache-sqlite/api/jvm/normalized-cache-sqlite.api
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ public final class com/apollographql/cache/normalized/sql/SqlNormalizedCache : c
public fun clearAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun dump (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getNextCache ()Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;
public fun loadRecord-7OwBRqc (Ljava/lang/String;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun loadRecords (Ljava/util/Collection;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun merge (Lcom/apollographql/cache/normalized/api/Record;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lcom/apollographql/cache/normalized/api/RecordMerger;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun merge (Ljava/util/Collection;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lcom/apollographql/cache/normalized/api/RecordMerger;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun remove (Ljava/util/Collection;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun remove-7OwBRqc (Ljava/lang/String;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun size (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun sizeOfRecord (Lcom/apollographql/cache/normalized/api/Record;)I
public fun trim (JFLkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ final class com.apollographql.cache.normalized.sql/SqlNormalizedCache : com.apol
final suspend fun merge(kotlin.collections/Collection<com.apollographql.cache.normalized.api/Record>, com.apollographql.cache.normalized.api/CacheHeaders, com.apollographql.cache.normalized.api/RecordMerger): kotlin.collections/Set<kotlin/String> // com.apollographql.cache.normalized.sql/SqlNormalizedCache.merge|merge(kotlin.collections.Collection<com.apollographql.cache.normalized.api.Record>;com.apollographql.cache.normalized.api.CacheHeaders;com.apollographql.cache.normalized.api.RecordMerger){}[0]
final suspend fun remove(com.apollographql.cache.normalized.api/CacheKey, kotlin/Boolean): kotlin/Boolean // com.apollographql.cache.normalized.sql/SqlNormalizedCache.remove|remove(com.apollographql.cache.normalized.api.CacheKey;kotlin.Boolean){}[0]
final suspend fun remove(kotlin.collections/Collection<com.apollographql.cache.normalized.api/CacheKey>, kotlin/Boolean): kotlin/Int // com.apollographql.cache.normalized.sql/SqlNormalizedCache.remove|remove(kotlin.collections.Collection<com.apollographql.cache.normalized.api.CacheKey>;kotlin.Boolean){}[0]
final suspend fun size(): kotlin/Long // com.apollographql.cache.normalized.sql/SqlNormalizedCache.size|size(){}[0]
final suspend fun trim(kotlin/Long, kotlin/Float): kotlin/Long // com.apollographql.cache.normalized.sql/SqlNormalizedCache.trim|trim(kotlin.Long;kotlin.Float){}[0]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ class SqlNormalizedCache internal constructor(
return keySize + RecordSerializer.serialize(record).size
}

override suspend fun size(): Long {
return try {
recordDatabase.init()
recordDatabase.databaseSize()
} catch (e: Exception) {
// Unable to get the size of the database, it is possibly corrupted
apolloExceptionHandler(Exception("Unable to get the size of the database", e))
-1
}
}

private suspend fun getReferencedKeysRecursively(
keys: Collection<String>,
visited: MutableSet<String> = mutableSetOf(),
Expand Down Expand Up @@ -174,8 +185,8 @@ class SqlNormalizedCache internal constructor(

override suspend fun trim(maxSizeBytes: Long, trimFactor: Float): Long {
try {
recordDatabase.init()
val size = recordDatabase.databaseSize()
val size = size()
if (size == -1L) return -1
return if (size >= maxSizeBytes) {
val count = recordDatabase.count()
recordDatabase.trimByUpdatedDate((count * trimFactor).toLong())
Expand Down
9 changes: 9 additions & 0 deletions normalized-cache/api/normalized-cache.api
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,8 @@ public final class com/apollographql/cache/normalized/api/NormalizedCache$Compan

public final class com/apollographql/cache/normalized/api/NormalizedCache$DefaultImpls {
public static fun close (Lcom/apollographql/cache/normalized/api/NormalizedCache;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun getNextCache (Lcom/apollographql/cache/normalized/api/NormalizedCache;)Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;
public static fun size (Lcom/apollographql/cache/normalized/api/NormalizedCache;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun sizeOfRecord (Lcom/apollographql/cache/normalized/api/NormalizedCache;Lcom/apollographql/cache/normalized/api/Record;)I
public static fun trim (Lcom/apollographql/cache/normalized/api/NormalizedCache;JFLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun trim$default (Lcom/apollographql/cache/normalized/api/NormalizedCache;JFLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
Expand All @@ -631,13 +633,17 @@ public abstract class com/apollographql/cache/normalized/api/NormalizedCacheFact
public abstract interface class com/apollographql/cache/normalized/api/ReadOnlyNormalizedCache {
public abstract fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun dump (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getNextCache ()Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;
public abstract fun loadRecord-7OwBRqc (Ljava/lang/String;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun loadRecords (Ljava/util/Collection;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun size (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun sizeOfRecord (Lcom/apollographql/cache/normalized/api/Record;)I
}

public final class com/apollographql/cache/normalized/api/ReadOnlyNormalizedCache$DefaultImpls {
public static fun close (Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun getNextCache (Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;)Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;
public static fun size (Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun sizeOfRecord (Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;Lcom/apollographql/cache/normalized/api/Record;)I
}

Expand Down Expand Up @@ -767,12 +773,15 @@ public final class com/apollographql/cache/normalized/memory/MemoryCache : com/a
public fun clearAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun dump (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getNextCache ()Lcom/apollographql/cache/normalized/api/NormalizedCache;
public synthetic fun getNextCache ()Lcom/apollographql/cache/normalized/api/ReadOnlyNormalizedCache;
public fun loadRecord-7OwBRqc (Ljava/lang/String;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun loadRecords (Ljava/util/Collection;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun merge (Lcom/apollographql/cache/normalized/api/Record;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lcom/apollographql/cache/normalized/api/RecordMerger;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun merge (Ljava/util/Collection;Lcom/apollographql/cache/normalized/api/CacheHeaders;Lcom/apollographql/cache/normalized/api/RecordMerger;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun remove (Ljava/util/Collection;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun remove-7OwBRqc (Ljava/lang/String;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun size (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun sizeOfRecord (Lcom/apollographql/cache/normalized/api/Record;)I
public fun trim (JFLkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
Expand Down
8 changes: 8 additions & 0 deletions normalized-cache/api/normalized-cache.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,15 @@ abstract interface com.apollographql.cache.normalized.api/NormalizedCache : com.
}

abstract interface com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache { // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache|null[0]
open val nextCache // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache.nextCache|{}nextCache[0]
open fun <get-nextCache>(): com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache? // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache.nextCache.<get-nextCache>|<get-nextCache>(){}[0]

abstract suspend fun dump(): kotlin.collections/Map<kotlin.reflect/KClass<*>, kotlin.collections/Map<com.apollographql.cache.normalized.api/CacheKey, com.apollographql.cache.normalized.api/Record>> // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache.dump|dump(){}[0]
abstract suspend fun loadRecord(com.apollographql.cache.normalized.api/CacheKey, com.apollographql.cache.normalized.api/CacheHeaders): com.apollographql.cache.normalized.api/Record? // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache.loadRecord|loadRecord(com.apollographql.cache.normalized.api.CacheKey;com.apollographql.cache.normalized.api.CacheHeaders){}[0]
abstract suspend fun loadRecords(kotlin.collections/Collection<com.apollographql.cache.normalized.api/CacheKey>, com.apollographql.cache.normalized.api/CacheHeaders): kotlin.collections/Collection<com.apollographql.cache.normalized.api/Record> // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache.loadRecords|loadRecords(kotlin.collections.Collection<com.apollographql.cache.normalized.api.CacheKey>;com.apollographql.cache.normalized.api.CacheHeaders){}[0]
open fun sizeOfRecord(com.apollographql.cache.normalized.api/Record): kotlin/Int // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache.sizeOfRecord|sizeOfRecord(com.apollographql.cache.normalized.api.Record){}[0]
open suspend fun close() // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache.close|close(){}[0]
open suspend fun size(): kotlin/Long // com.apollographql.cache.normalized.api/ReadOnlyNormalizedCache.size|size(){}[0]
}

abstract interface com.apollographql.cache.normalized.api/RecordMerger { // com.apollographql.cache.normalized.api/RecordMerger|null[0]
Expand Down Expand Up @@ -402,6 +406,9 @@ final class com.apollographql.cache.normalized.api/TypePolicyCacheKeyGenerator :
final class com.apollographql.cache.normalized.memory/MemoryCache : com.apollographql.cache.normalized.api/NormalizedCache { // com.apollographql.cache.normalized.memory/MemoryCache|null[0]
constructor <init>(com.apollographql.cache.normalized.api/NormalizedCache? = ..., kotlin/Int = ..., kotlin/Long = ...) // com.apollographql.cache.normalized.memory/MemoryCache.<init>|<init>(com.apollographql.cache.normalized.api.NormalizedCache?;kotlin.Int;kotlin.Long){}[0]

final val nextCache // com.apollographql.cache.normalized.memory/MemoryCache.nextCache|{}nextCache[0]
final fun <get-nextCache>(): com.apollographql.cache.normalized.api/NormalizedCache? // com.apollographql.cache.normalized.memory/MemoryCache.nextCache.<get-nextCache>|<get-nextCache>(){}[0]

final fun sizeOfRecord(com.apollographql.cache.normalized.api/Record): kotlin/Int // com.apollographql.cache.normalized.memory/MemoryCache.sizeOfRecord|sizeOfRecord(com.apollographql.cache.normalized.api.Record){}[0]
final suspend fun clearAll() // com.apollographql.cache.normalized.memory/MemoryCache.clearAll|clearAll(){}[0]
final suspend fun dump(): kotlin.collections/Map<kotlin.reflect/KClass<*>, kotlin.collections/Map<com.apollographql.cache.normalized.api/CacheKey, com.apollographql.cache.normalized.api/Record>> // com.apollographql.cache.normalized.memory/MemoryCache.dump|dump(){}[0]
Expand All @@ -411,6 +418,7 @@ final class com.apollographql.cache.normalized.memory/MemoryCache : com.apollogr
final suspend fun merge(kotlin.collections/Collection<com.apollographql.cache.normalized.api/Record>, com.apollographql.cache.normalized.api/CacheHeaders, com.apollographql.cache.normalized.api/RecordMerger): kotlin.collections/Set<kotlin/String> // com.apollographql.cache.normalized.memory/MemoryCache.merge|merge(kotlin.collections.Collection<com.apollographql.cache.normalized.api.Record>;com.apollographql.cache.normalized.api.CacheHeaders;com.apollographql.cache.normalized.api.RecordMerger){}[0]
final suspend fun remove(com.apollographql.cache.normalized.api/CacheKey, kotlin/Boolean): kotlin/Boolean // com.apollographql.cache.normalized.memory/MemoryCache.remove|remove(com.apollographql.cache.normalized.api.CacheKey;kotlin.Boolean){}[0]
final suspend fun remove(kotlin.collections/Collection<com.apollographql.cache.normalized.api/CacheKey>, kotlin/Boolean): kotlin/Int // com.apollographql.cache.normalized.memory/MemoryCache.remove|remove(kotlin.collections.Collection<com.apollographql.cache.normalized.api.CacheKey>;kotlin.Boolean){}[0]
final suspend fun size(): kotlin/Long // com.apollographql.cache.normalized.memory/MemoryCache.size|size(){}[0]
final suspend fun trim(kotlin/Long, kotlin/Float): kotlin/Long // com.apollographql.cache.normalized.memory/MemoryCache.trim|trim(kotlin.Long;kotlin.Float){}[0]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,24 @@ interface ReadOnlyNormalizedCache {

/**
* Returns the size in bytes of a [Record].
* This is an optional operation that can be implemented by the caches for debug purposes, otherwise it defaults to `-1`, meaning unknown size.
* This is an optional operation that can be implemented by caches for debug purposes, otherwise it defaults to `-1`, meaning unknown size.
*/
fun sizeOfRecord(record: Record): Int = -1

/**
* Returns the total size in bytes of this cache.
* This is an optional operation that can be implemented by caches for debug or observability purposes, otherwise it defaults to `-1`, meaning unknown size.
*
* Note: in case of a chained cache, this size does not include the size of the next cache in the chain. See [nextCache].
*/
suspend fun size(): Long = -1L

/**
* Some cache implementations (e.g. [com.apollographql.cache.normalized.internal.OptimisticNormalizedCache] and [com.apollographql.cache.normalized.memory.MemoryCache]) support chaining caches.
* For those, this returns the next cache in the chain or `null` if there is none. Implementations that don't support chaining return `null`.
*/
val nextCache: ReadOnlyNormalizedCache?
get() = null

suspend fun close() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,33 @@ import com.benasher44.uuid.Uuid
import kotlin.math.max
import kotlin.reflect.KClass

internal class OptimisticNormalizedCache(private val wrapped: NormalizedCache) : NormalizedCache {
internal class OptimisticNormalizedCache(
override val nextCache: NormalizedCache
) : NormalizedCache {
private val recordJournals = ConcurrentMap<CacheKey, RecordJournal>()

override suspend fun loadRecord(key: CacheKey, cacheHeaders: CacheHeaders): Record? {
val nonOptimisticRecord = wrapped.loadRecord(key, cacheHeaders)
val nonOptimisticRecord = nextCache.loadRecord(key, cacheHeaders)
return nonOptimisticRecord.mergeJournalRecord(key)
}

override suspend fun loadRecords(keys: Collection<CacheKey>, cacheHeaders: CacheHeaders): Collection<Record> {
val nonOptimisticRecords = wrapped.loadRecords(keys, cacheHeaders).associateBy { it.key }
val nonOptimisticRecords = nextCache.loadRecords(keys, cacheHeaders).associateBy { it.key }
return keys.mapNotNull { key ->
nonOptimisticRecords[key].mergeJournalRecord(key)
}
}

override suspend fun merge(record: Record, cacheHeaders: CacheHeaders, recordMerger: RecordMerger): Set<String> {
return wrapped.merge(record, cacheHeaders, recordMerger)
return nextCache.merge(record, cacheHeaders, recordMerger)
}

override suspend fun merge(records: Collection<Record>, cacheHeaders: CacheHeaders, recordMerger: RecordMerger): Set<String> {
return wrapped.merge(records, cacheHeaders, recordMerger)
return nextCache.merge(records, cacheHeaders, recordMerger)
}

override suspend fun clearAll() {
wrapped.clearAll()
nextCache.clearAll()
recordJournals.clear()
}

Expand All @@ -43,11 +45,11 @@ internal class OptimisticNormalizedCache(private val wrapped: NormalizedCache) :
}

override suspend fun remove(cacheKeys: Collection<CacheKey>, cascade: Boolean): Int {
return wrapped.remove(cacheKeys, cascade) + internalRemove(cacheKeys, cascade)
return nextCache.remove(cacheKeys, cascade) + internalRemove(cacheKeys, cascade)
}

override suspend fun trim(maxSizeBytes: Long, trimFactor: Float): Long {
return wrapped.trim(maxSizeBytes, trimFactor)
return nextCache.trim(maxSizeBytes, trimFactor)
}

private fun internalRemove(cacheKeys: Collection<CacheKey>, cascade: Boolean): Int {
Expand Down Expand Up @@ -101,11 +103,15 @@ internal class OptimisticNormalizedCache(private val wrapped: NormalizedCache) :
}

override suspend fun dump(): Map<KClass<*>, Map<CacheKey, Record>> {
return mapOf(this::class to recordJournals.mapValues { (_, journal) -> journal.current }) + wrapped.dump()
return mapOf(this::class to recordJournals.mapValues { (_, journal) -> journal.current }) + nextCache.dump()
}

override fun sizeOfRecord(record: Record): Int {
return wrapped.sizeOfRecord(record)
return nextCache.sizeOfRecord(record)
}

override suspend fun size(): Long {
return nextCache.size()
}

private fun Record?.mergeJournalRecord(key: CacheKey): Record? {
Expand Down
Loading