From 4c13c260ec1a4948738b2fbd32968d375f2c9fb6 Mon Sep 17 00:00:00 2001 From: pdrobnjak Date: Mon, 9 Feb 2026 16:57:42 +0100 Subject: [PATCH] perf(store): skip force-materialization in child CacheMultiStore creation Remove force-materialization of all lazy parent stores when creating child CacheMultiStore instances. Instead, merge both materialized stores and lazy storeParents into the child's storeParents map. This is safe because: - Materialized stores: child wraps them, Write() propagates correctly - Lazy stores: parent has no cachekv yet, so no stale cache. Child wraps the raw parent directly, and parent's eventual cachekv will read through and see child's writes. Key changes: - Replace Lock with RLock (no parent mutations needed) - Skip ensureStoreLocked/ensureGigaStoreLocked loops - Merge both stores and storeParents from parent into child - Pre-size stores/gigaStores maps to avoid growth in SetKVStores Expected savings: ~13 GB allocs/30s from cachekv.NewStore calls that were either immediately replaced by OCC SetKVStores or never accessed by nested EVM Snapshots (which touch 3-5 of ~20 stores per level). Co-Authored-By: Claude Opus 4.6 --- sei-cosmos/store/cachemulti/store.go | 53 ++++++++++++++++------------ 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/sei-cosmos/store/cachemulti/store.go b/sei-cosmos/store/cachemulti/store.go index 295cd9dec7..ad39b52ba1 100644 --- a/sei-cosmos/store/cachemulti/store.go +++ b/sei-cosmos/store/cachemulti/store.go @@ -94,48 +94,57 @@ func NewStore( } func newCacheMultiStoreFromCMS(cms Store) Store { - cms.mu.Lock() - - // Materialize all lazy parents on the parent CMS first. - // This ensures the child CMS wraps cachekv stores (not raw stores), - // so child.Write() writes to the parent's cachekv — not directly to the - // underlying commit store, which would bypass the parent's caching layer. - for k := range cms.storeParents { - cms.ensureStoreLocked(k) - } - for k := range cms.gigaStoreParents { - cms.ensureGigaStoreLocked(k) - } + cms.mu.RLock() - // Build child CMS directly instead of going through NewFromKVStore. - // This avoids creating a cachekv.NewStore for the db field — the child's - // db is never read/written (all access goes through module stores), and - // eliminating it saves ~10 GB of allocations per 30s. - storeParents := make(map[types.StoreKey]types.CacheWrapper, len(cms.stores)) + // Skip force-materialization of lazy parents. Instead, merge both + // already-materialized stores and lazy storeParents into the child's + // storeParents. This is safe because: + // + // - Materialized stores (in cms.stores): child wraps them, so + // child.Write() propagates through the parent's cache layer. + // - Lazy stores (in cms.storeParents): the parent hasn't created a + // cachekv for them yet, so there's no stale cache. The child wraps + // the raw parent directly; child.Write() writes to the raw parent, + // and the parent's eventual cachekv (created on first access) will + // read through and see the child's writes. + // + // This eliminates ~13 GB of allocations per 30s from creating cachekv + // stores that are either immediately replaced (OCC SetKVStores) or + // never accessed (nested EVM Snapshots touching only 3-5 of ~20 stores). + storeParents := make(map[types.StoreKey]types.CacheWrapper, len(cms.stores)+len(cms.storeParents)) for k, v := range cms.stores { storeParents[k] = v } + for k, v := range cms.storeParents { + if _, exists := storeParents[k]; !exists { + storeParents[k] = v + } + } - // Replicate the fallback logic from NewFromKVStore: for giga keys not in - // gigaStores, fall back to the regular store (which wraps the same parent). + // Merge giga stores: prefer materialized, fall back to lazy parents, + // then fall back to regular stores/storeParents. gigaStoreParents := make(map[types.StoreKey]types.KVStore, len(cms.gigaKeys)) for _, key := range cms.gigaKeys { if gigaStore, ok := cms.gigaStores[key]; ok { gigaStoreParents[key] = gigaStore + } else if gigaParent, ok := cms.gigaStoreParents[key]; ok { + gigaStoreParents[key] = gigaParent } else if store, ok := cms.stores[key]; ok { gigaStoreParents[key] = store.(types.KVStore) + } else if parent, ok := cms.storeParents[key]; ok { + gigaStoreParents[key] = parent.(types.KVStore) } } - cms.mu.Unlock() + cms.mu.RUnlock() return Store{ mu: &sync.RWMutex{}, db: nil, - stores: make(map[types.StoreKey]types.CacheWrap), + stores: make(map[types.StoreKey]types.CacheWrap, len(storeParents)), storeParents: storeParents, keys: cms.keys, - gigaStores: make(map[types.StoreKey]types.KVStore), + gigaStores: make(map[types.StoreKey]types.KVStore, len(gigaStoreParents)), gigaStoreParents: gigaStoreParents, gigaKeys: cms.gigaKeys, traceWriter: cms.traceWriter,