From 1a110c96733cfce6eb1b9837b84fcde218bb9a9d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:52:04 +0000 Subject: [PATCH 1/2] perf(wasmJs): Cache theme cache manifest in-memory to prevent repeated JSON parsing Co-authored-by: himattm <6266621+himattm@users.noreply.github.com> --- .jules/bolt.md | 3 ++ .../halogen/engine/LocalStorageThemeCache.kt | 32 +++++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..9a22b4a --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-04-20 - [LocalStorageThemeCache caching bottleneck] +**Learning:** The `LocalStorageThemeCache` does redundant JSON decoding of the entire manifest on every operation (read/write/evict/contains) which creates overhead for wasmJs. +**Action:** Introduce an in-memory Set to cache the manifest, updating it during mutations and lazy-loading it once. This avoids repeated JSON decoding on `readManifest()`. diff --git a/halogen-engine/src/wasmJsMain/kotlin/halogen/engine/LocalStorageThemeCache.kt b/halogen-engine/src/wasmJsMain/kotlin/halogen/engine/LocalStorageThemeCache.kt index aba1f32..24a1fb4 100644 --- a/halogen-engine/src/wasmJsMain/kotlin/halogen/engine/LocalStorageThemeCache.kt +++ b/halogen-engine/src/wasmJsMain/kotlin/halogen/engine/LocalStorageThemeCache.kt @@ -49,9 +49,13 @@ public class LocalStorageThemeCache( private val json = Json { ignoreUnknownKeys = true } private val manifestKey = "${prefix}__keys__" + // Cache the manifest in-memory to prevent repeated JSON decoding on every operation. + // Lazy load the manifest once, then keep it in sync. + private val manifestCache: MutableSet by lazy { loadManifest() } + // ── Manifest helpers ──────────────────────────────────────────────── - private fun readManifest(): MutableSet { + private fun loadManifest(): MutableSet { val raw = jsGetItem(manifestKey.toJsString())?.toString() ?: return mutableSetOf() return try { json.decodeFromString>(raw).toMutableSet() @@ -110,9 +114,8 @@ public class LocalStorageThemeCache( sizeBytes = specJson.encodeToByteArray().size, ) writeEntry(storageKey, entry) - val manifest = readManifest() - manifest.add(key) - writeManifest(manifest) + manifestCache.add(key) + writeManifest(manifestCache) _changes.tryEmit(CacheEvent.Inserted(key, source)) } @@ -124,9 +127,8 @@ public class LocalStorageThemeCache( val storageKey = "$prefix$key" val existed = readEntry(storageKey) != null removeEntry(storageKey) - val manifest = readManifest() - manifest.remove(key) - writeManifest(manifest) + manifestCache.remove(key) + writeManifest(manifestCache) if (existed) { _changes.tryEmit(CacheEvent.Evicted(key)) } @@ -134,37 +136,35 @@ public class LocalStorageThemeCache( override suspend fun evict(keys: Set) { val removed = mutableSetOf() - val manifest = readManifest() for (k in keys) { val storageKey = "$prefix$k" if (readEntry(storageKey) != null) { removeEntry(storageKey) - manifest.remove(k) + manifestCache.remove(k) removed.add(k) } } - writeManifest(manifest) + writeManifest(manifestCache) if (removed.isNotEmpty()) { _changes.tryEmit(CacheEvent.EvictedBatch(removed)) } } override suspend fun clear() { - val manifest = readManifest() - for (key in manifest) { + for (key in manifestCache) { removeEntry("$prefix$key") } + manifestCache.clear() jsRemoveItem(manifestKey.toJsString()) _changes.tryEmit(CacheEvent.Cleared) } - override suspend fun keys(): Set = readManifest() + override suspend fun keys(): Set = manifestCache.toSet() - override suspend fun size(): Int = readManifest().size + override suspend fun size(): Int = manifestCache.size override suspend fun entries(): List { - val manifest = readManifest() - return manifest.mapNotNull { key -> + return manifestCache.mapNotNull { key -> val entry = readEntry("$prefix$key") ?: return@mapNotNull null ThemeCacheEntry( key = key, From 00dc3b4f1e3acfa9c19523d7bb82d556e42ddc0c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 03:02:52 +0000 Subject: [PATCH 2/2] perf(wasmJs): Cache theme cache manifest in-memory to prevent repeated JSON parsing Co-authored-by: himattm <6266621+himattm@users.noreply.github.com>