Skip to content
Open
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
155 changes: 155 additions & 0 deletions giga/deps/store/fast_cachekv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package store

import (
"bytes"
"io"
"sort"

"github.com/cosmos/cosmos-sdk/store/tracekv"
"github.com/cosmos/cosmos-sdk/store/types"
)

// FastStore is a single-goroutine giga cachekv store using plain maps instead of sync.Map.
// It MUST NOT be used from multiple goroutines concurrently.
// Use this for snapshot stores in the giga executor path where each store is
// owned by a single task goroutine.
type FastStore struct {
cache map[string]*types.CValue
deleted map[string]struct{}
parent types.KVStore
storeKey types.StoreKey
}

var _ types.CacheKVStore = (*FastStore)(nil)

// NewFastStore creates a new FastStore backed by plain maps.
func NewFastStore(parent types.KVStore, storeKey types.StoreKey) *FastStore {
return &FastStore{
cache: make(map[string]*types.CValue),
deleted: make(map[string]struct{}),
parent: parent,
storeKey: storeKey,
}
}

func (store *FastStore) GetWorkingHash() ([]byte, error) {
panic("should never attempt to get working hash from cache kv store")
}

func (store *FastStore) GetStoreType() types.StoreType {
return store.parent.GetStoreType()
}

func (store *FastStore) Get(key []byte) []byte {
types.AssertValidKey(key)
keyStr := UnsafeBytesToStr(key)
if cv, ok := store.cache[keyStr]; ok {
return cv.Value()
}
return store.parent.Get(key)
}

func (store *FastStore) Set(key []byte, value []byte) {
types.AssertValidKey(key)
types.AssertValidValue(value)
store.setCacheValue(key, value, false, true)
}

func (store *FastStore) Has(key []byte) bool {
return store.Get(key) != nil
}

func (store *FastStore) Delete(key []byte) {
types.AssertValidKey(key)
store.setCacheValue(key, nil, true, true)
}

func (store *FastStore) Write() {
keys := make([]string, 0, len(store.cache))
for k, v := range store.cache {
if v.Dirty() {
keys = append(keys, k)
}
}
sort.Strings(keys)

for _, key := range keys {
if _, del := store.deleted[key]; del {
store.parent.Delete([]byte(key))
continue
}
if cv, ok := store.cache[key]; ok && cv.Value() != nil {
store.parent.Set([]byte(key), cv.Value())
}
}

// Clear both maps — in the FastStore snapshot chain, parents are either
// another FastStore or a VIS, both of which make writes immediately
// visible. Reads after Write() fall through to the parent.
clear(store.cache)
clear(store.deleted)
}

func (store *FastStore) CacheWrap(storeKey types.StoreKey) types.CacheWrap {
return NewFastStore(store, storeKey)
}

func (store *FastStore) CacheWrapWithTrace(storeKey types.StoreKey, w io.Writer, tc types.TraceContext) types.CacheWrap {
return NewFastStore(tracekv.NewStore(store, w, tc), storeKey)
}

func (store *FastStore) VersionExists(version int64) bool {
return store.parent.VersionExists(version)
}

func (store *FastStore) setCacheValue(key, value []byte, deleted bool, dirty bool) {
types.AssertValidKey(key)
keyStr := UnsafeBytesToStr(key)
store.cache[keyStr] = types.NewCValue(value, dirty)
if deleted {
store.deleted[keyStr] = struct{}{}
} else {
delete(store.deleted, keyStr)
}
}

func (store *FastStore) GetParent() types.KVStore {
return store.parent
}

func (store *FastStore) DeleteAll(start, end []byte) error {
for _, k := range store.GetAllKeyStrsInRange(start, end) {
store.Delete([]byte(k))
}
return nil
}

func (store *FastStore) GetAllKeyStrsInRange(start, end []byte) (res []string) {
keyStrs := map[string]struct{}{}
for _, pk := range store.parent.GetAllKeyStrsInRange(start, end) {
keyStrs[pk] = struct{}{}
}
for k, v := range store.cache {
kbz := []byte(k)
if bytes.Compare(kbz, start) < 0 || bytes.Compare(kbz, end) >= 0 {
continue
}
if v.Value() == nil {
delete(keyStrs, k)
} else {
keyStrs[k] = struct{}{}
}
}
Comment on lines +132 to +142

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
for k := range keyStrs {
res = append(res, k)
}
Comment on lines +143 to +145

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
return res
}

func (store *FastStore) Iterator(start, end []byte) types.Iterator {
panic("unexpected iterator call on fast giga cachekv store")

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods Warning

Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
}

func (store *FastStore) ReverseIterator(start, end []byte) types.Iterator {
panic("unexpected reverse iterator call on fast giga cachekv store")

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods Warning

Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
}
16 changes: 15 additions & 1 deletion giga/deps/xevm/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,22 @@ func (s *DBImpl) HasSelfDestructed(acc common.Address) bool {
return bytes.Equal(val, AccountDeleted)
}

// gigaCacheMultiStorer is implemented by cachemulti.Store to provide a
// lightweight CMS that uses plain maps instead of sync.Map. This is safe
// for single-goroutine use in the giga executor snapshot path.
type gigaCacheMultiStorer interface {
CacheMultiStoreGiga() storetypes.CacheMultiStore
}

func (s *DBImpl) Snapshot() int {
newCtx := s.ctx.WithMultiStore(s.ctx.MultiStore().CacheMultiStore()).WithEventManager(sdk.NewEventManager())
ms := s.ctx.MultiStore()
var cms storetypes.CacheMultiStore
if gms, ok := ms.(gigaCacheMultiStorer); ok {
cms = gms.CacheMultiStoreGiga()
} else {
cms = ms.CacheMultiStore()
}
newCtx := s.ctx.WithMultiStore(cms).WithEventManager(sdk.NewEventManager())
s.snapshottedCtxs = append(s.snapshottedCtxs, s.ctx)
s.ctx = newCtx
version := len(s.snapshottedCtxs) - 1
Expand Down
85 changes: 84 additions & 1 deletion sei-cosmos/store/cachemulti/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
materializeOnce *sync.Once

closers []io.Closer

fast bool // when true, lazy store creation uses FastStore (plain maps)
}

var _ types.CacheMultiStore = Store{}
Expand Down Expand Up @@ -132,6 +134,75 @@
return NewFromKVStore(cms.db, stores, gigaStores, cms.keys, cms.gigaKeys, cms.traceWriter, cms.traceContext)
}

// newFastCacheMultiStoreFromCMS creates a lightweight child CMS using plain-map
// based stores instead of sync.Map-based stores. The child CMS is intended for
// single-goroutine use only (giga executor snapshot path).
//
// Stores are created lazily: only when actually accessed by the transaction.
// This avoids allocating ~50 cachekv/FastStore wrappers per Snapshot when
// only 3-4 stores are typically touched.
func newFastCacheMultiStoreFromCMS(cms Store) Store {
// Collect both materialized stores and unmaterialized parents as parents
// for the child CMS. The child's getOrCreateStore will create FastStores
// lazily on demand, avoiding the cost of eagerly creating wrappers for
// all ~50 store keys.
cms.mu.RLock()
parentCount := len(cms.stores) + len(cms.parents)
parents := make(map[types.StoreKey]types.CacheWrapper, parentCount)
for k, v := range cms.stores {
parents[k] = v
}
Comment on lines +152 to +154

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
for k, v := range cms.parents {
parents[k] = v
}
Comment on lines +155 to +157

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
cms.mu.RUnlock()

gigaStores := make(map[types.StoreKey]types.KVStore, len(cms.gigaStores))
for k, v := range cms.gigaStores {
gigaStores[k] = v
}
Comment on lines +161 to +163

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism

return newFastFromKVStore(cms.db, parents, gigaStores, cms.keys, cms.gigaKeys, cms.traceWriter, cms.traceContext)
}

// newFastFromKVStore is like NewFromKVStore but uses plain-map FastStore types
// instead of sync.Map-based stores. Single-goroutine use only.
func newFastFromKVStore(
store types.KVStore, stores map[types.StoreKey]types.CacheWrapper,
gigaStores map[types.StoreKey]types.KVStore,
keys map[string]types.StoreKey, gigaKeys []types.StoreKey,
traceWriter io.Writer, traceContext types.TraceContext,
) Store {
cms := Store{
db: gigacachekv.NewFastStore(store, nil),
stores: make(map[types.StoreKey]types.CacheWrap, len(stores)),
parents: make(map[types.StoreKey]types.CacheWrapper, len(stores)),
keys: keys,
gigaKeys: gigaKeys,
traceWriter: traceWriter,
traceContext: traceContext,
mu: &sync.RWMutex{},
materializeOnce: &sync.Once{},
fast: true,
}

for key, s := range stores {
cms.parents[key] = s
}
Comment on lines +189 to +191

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism

cms.gigaStores = make(map[types.StoreKey]types.KVStore, len(gigaKeys))
for _, key := range gigaKeys {
if gigaStore, ok := gigaStores[key]; ok {
cms.gigaStores[key] = gigacachekv.NewFastStore(gigaStore, key)
} else {
parent := stores[key].(types.KVStore)
cms.gigaStores[key] = gigacachekv.NewFastStore(parent, key)
}
}

return cms
}

// getOrCreateStore lazily creates a cachekv store from its parent on first access.
// Thread-safe: concurrent callers (e.g. slashing BeginBlocker goroutines) may
// call GetKVStore on the same CMS simultaneously.
Expand Down Expand Up @@ -160,7 +231,12 @@
if cms.TracingEnabled() {
cw = tracekv.NewStore(parent.(types.KVStore), cms.traceWriter, cms.traceContext)
}
s := cachekv.NewStore(cw.(types.KVStore), key, types.DefaultCacheSizeLimit)
var s types.CacheWrap
if cms.fast {
s = gigacachekv.NewFastStore(cw.(types.KVStore), key)
} else {
s = cachekv.NewStore(cw.(types.KVStore), key, types.DefaultCacheSizeLimit)
}
cms.stores[key] = s
delete(cms.parents, key)
return s
Expand Down Expand Up @@ -228,6 +304,13 @@
return newCacheMultiStoreFromCMS(cms)
}

// CacheMultiStoreGiga creates a lightweight CMS using plain-map stores.
// Only safe when the returned CMS will be used by a single goroutine (e.g.
// giga executor snapshots within a single OCC task).
func (cms Store) CacheMultiStoreGiga() types.CacheMultiStore {
return newFastCacheMultiStoreFromCMS(cms)
}

// CacheMultiStoreWithVersion implements the MultiStore interface. It will panic
// as an already cached multi-store cannot load previous versions.
//
Expand Down
Loading