Marcello Galhardo
08/12/2026, 10:37 AMMarcello Galhardo
08/12/2026, 10:39 AM2026-08-12 12:36:27.329 19531-19590 AndroidRuntime dev.johnoreilly.confetti E FATAL EXCEPTION: DefaultDispatcher-worker-5 (Fix with AI)
Process: dev.johnoreilly.confetti, PID: 19531
java.lang.OutOfMemoryError: Failed to allocate a 32 byte allocation with 2000544 free bytes and 1953KB until OOM, target footprint 201326592, growth limit 201326592; giving up on allocation because <1% of heap free after GC.
at java.util.ArrayList.iterator(ArrayList.java:1036)
at java.util.AbstractCollection.addAll(AbstractCollection.java:335)
at java.util.LinkedHashSet.<init>(LinkedHashSet.java:182)
at kotlin.collections.CollectionsKt___CollectionsKt.toMutableSet(_Collections.kt:1906)
at kotlin.collections.CollectionsKt___CollectionsKt.distinct(_Collections.kt:1836)
at com.apollographql.cache.normalized.api.MaxAgeProviderKt.allImplements(MaxAgeProvider.kt:150)
at com.apollographql.cache.normalized.api.MaxAgeProviderKt.access$allImplements(MaxAgeProvider.kt:1)
at com.apollographql.cache.normalized.api.SchemaCoordinatesMaxAgeProvider.getMaxAge-5sfh64U(MaxAgeProvider.kt:75)
at com.apollographql.cache.normalized.api.SchemaCoordinatesMaxAgeProvider.getParentMaxAge-5sfh64U(MaxAgeProvider.kt:101)
at com.apollographql.cache.normalized.api.SchemaCoordinatesMaxAgeProvider.getMaxAge-5sfh64U(MaxAgeProvider.kt:97)
at com.apollographql.cache.normalized.api.SchemaCoordinatesMaxAgeProvider.getParentMaxAge-5sfh64U(MaxAgeProvider.kt:101)
at com.apollographql.cache.normalized.api.SchemaCoordinatesMaxAgeProvider.getMaxAge-5sfh64U(MaxAgeProvider.kt:97)
at com.apollographql.cache.normalized.api.SchemaCoordinatesMaxAgeProvider.getParentMaxAge-5sfh64U(MaxAgeProvider.kt:101)
at com.apollographql.cache.normalized.api.SchemaCoordinatesMaxAgeProvider.getMaxAge-5sfh64U(MaxAgeProvider.kt:97)
at com.apollographql.cache.normalized.api.CacheControlCacheResolver.resolveField(CacheResolver.kt:263)
at com.apollographql.cache.normalized.internal.CacheBatchReader.collectData(CacheBatchReader.kt:154)
at com.apollographql.cache.normalized.internal.CacheBatchReader$collectData$1.invokeSuspend(Unknown Source:14)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:278)
at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:101)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:47)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:124)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:89)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:586)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:798)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:717)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:704)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@859d4d2, Dispatchers.Default]mbonnin
08/12/2026, 10:44 AMmbonnin
08/12/2026, 10:44 AMmbonnin
08/12/2026, 10:46 AMMarcello Galhardo
08/12/2026, 11:48 AMmbonnin
08/12/2026, 11:55 AMMarcello Galhardo
08/12/2026, 11:56 AMMarcello Galhardo
08/12/2026, 11:57 AMmbonnin
08/12/2026, 12:00 PMmbonnin
08/12/2026, 12:27 PMMarcello Galhardo
08/12/2026, 12:28 PMMarcello Galhardo
08/12/2026, 12:28 PMmbonnin
08/12/2026, 1:52 PMmbonnin
08/12/2026, 1:52 PMMarcello Galhardo
08/13/2026, 9:48 AMMarcello Galhardo
08/13/2026, 9:49 AMMarcello Galhardo
08/13/2026, 9:49 AMbod
08/13/2026, 9:53 AMbod
08/13/2026, 9:54 AMMarcello Galhardo
08/13/2026, 10:03 AM┃ ### Root Cause
┃
┃ 1. @cacheControl directive triggers Apollo TTL resolver:
┃ • extra.graphqls configured @cacheControl(maxAge: ...) directives on types (Session, Speaker, etc.).
┃ • Apollo compiler generated a non-empty maxAges map, enabling CacheControlCacheResolver during all normalized cache reads.
┃ 2. Recursive schema coordinate traversal:
┃ • For every single field read across hundreds of cached sessions and speakers, Apollo calls CacheControlCacheResolver.resolveField(...) → SchemaCoordinatesMaxAgeProvider.getMaxAge(...).
┃ • getMaxAge recursively calls getParentMaxAge and allImplements(...).
┃ 3. Massive heap allocation storm:
┃ • allImplements(...) continuously constructs ArrayList.iterator, LinkedHashSet, and invokes CollectionsKt.distinct(...) to traverse interface hierarchies.
┃ • For a full conference graph, this generates millions of short-lived collection objects in seconds, overwhelming the Android GC and causing OutOfMemoryError on 192MB–200MB heap limit devices.Marcello Galhardo
08/13/2026, 10:05 AMConfetti already configures maxStale(Duration.INFINITE) in ApolloClientCache and manages cache freshness explicitly via FetchPolicy (CacheFirst vs NetworkOnly) and Interceptor.Marcello Galhardo
08/13/2026, 10:06 AMbod
08/13/2026, 10:06 AMbod
08/13/2026, 4:07 PMmaxStale(Duration.INFINITE) that the assistant thought meant the max ages were ignored doesn't actually mean that it's just a way to still get stale values from the cache and implement that custom logic. (I should have commented!)Marcello Galhardo
08/13/2026, 4:09 PMMarcello Galhardo
08/13/2026, 4:10 PMbod
08/13/2026, 4:10 PMbod
08/13/2026, 4:10 PMMarcello Galhardo
08/13/2026, 4:11 PMMarcello Galhardo
08/13/2026, 4:11 PMMarcello Galhardo
08/13/2026, 4:11 PMMarcello Galhardo
08/13/2026, 4:12 PMMarcello Galhardo
08/13/2026, 4:12 PMbod
08/13/2026, 4:13 PMMarcello Galhardo
08/13/2026, 4:13 PMMarcello Galhardo
08/13/2026, 4:13 PMMarcello Galhardo
08/13/2026, 4:15 PMbod
08/13/2026, 4:15 PMbod
08/13/2026, 4:15 PMMarcello Galhardo
08/13/2026, 4:16 PMMarcello Galhardo
08/13/2026, 4:17 PMMarcello Galhardo
08/13/2026, 4:17 PMbod
08/13/2026, 4:18 PMMarcello Galhardo
08/13/2026, 4:19 PMJohn O'Reilly
08/13/2026, 4:22 PMbod
08/13/2026, 4:25 PMMarcello Galhardo
08/13/2026, 4:26 PMMarcello Galhardo
08/13/2026, 4:26 PMJohn O'Reilly
08/13/2026, 4:26 PMMarcello Galhardo
08/13/2026, 4:27 PMJohn O'Reilly
08/13/2026, 4:28 PMJohn O'Reilly
08/13/2026, 4:28 PMJohn O'Reilly
08/13/2026, 4:30 PMMarcello Galhardo
08/13/2026, 4:30 PMMarcello Galhardo
08/13/2026, 5:03 PMMarcello Galhardo
08/13/2026, 5:17 PMMarcello Galhardo
08/13/2026, 5:24 PMMarcello Galhardo
08/16/2026, 9:43 AMbod
08/16/2026, 1:25 PMbod
08/16/2026, 2:07 PMbod
08/17/2026, 9:27 AMJohn O'Reilly
08/17/2026, 10:40 AMbod
08/17/2026, 10:41 AMMarcello Galhardo
08/17/2026, 10:44 AMbod
08/17/2026, 10:46 AMmbonnin
08/17/2026, 10:47 AMbod
08/17/2026, 10:48 AMmbonnin
08/17/2026, 10:48 AMmbonnin
08/17/2026, 10:48 AMmbonnin
08/17/2026, 10:50 AMmbonnin
08/17/2026, 10:51 AMmbonnin
08/17/2026, 10:51 AMbod
08/17/2026, 10:56 AMmbonnin
08/17/2026, 11:00 AMmbonnin
08/17/2026, 11:01 AMQUERY ? 👀mbonnin
08/17/2026, 11:01 AMmbonnin
08/17/2026, 11:01 AMQUERY + etags = winmbonnin
08/17/2026, 11:02 AMbod
08/17/2026, 11:02 AMmbonnin
08/17/2026, 11:03 AMmain only when I have time to look into them 😅Marcello Galhardo
08/17/2026, 11:15 AMMarcello Galhardo
08/21/2026, 7:35 AMbod
09/14/2026, 1:36 PM