Skip to content

Conversation

@pdrobnjak
Copy link
Contributor

Summary

  • Replace sync.Map with plain map in cachekv stores for better performance

Stack

8/19 — depends on perf/skip-redundant-snapshot (replaces auto-closed #2822)

🤖 Generated with Claude Code

@github-actions
Copy link

github-actions bot commented Feb 11, 2026

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedFeb 11, 2026, 2:02 PM

Within OCC, each worker gets its own CacheMultiStore chain with
dedicated cachekv.Store instances — zero concurrent access. The
sync.Map thread-safety overhead (CAS, internal node allocs) is
pure waste in this single-goroutine context.

Replace *sync.Map with typed Go maps in both sei-cosmos and giga
cachekv implementations:
- cache:         map[string]*types.CValue
- deleted:       map[string]struct{}
- unsortedCache: map[string]struct{}

Keep sync.RWMutex for defense-in-depth (uncontested, <20ns).

Micro-benchmarks: 32-56% faster across all cachekv operations,
4 allocs/op eliminated from Set paths.
TPS benchmark: ~8,936 avg (+14.6% cumulative over origin/main).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@pdrobnjak pdrobnjak force-pushed the perf/replace-syncmap-cachekv branch from bb5063e to bbff2d7 Compare February 11, 2026 13:34
@pdrobnjak pdrobnjak force-pushed the perf/skip-redundant-snapshot branch from 08c4efe to 8d2dc33 Compare February 11, 2026 13:34
@pdrobnjak pdrobnjak changed the base branch from perf/skip-redundant-snapshot to main February 11, 2026 13:34
@codecov
Copy link

codecov bot commented Feb 11, 2026

Codecov Report

❌ Patch coverage is 96.07843% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.23%. Comparing base (21e991f) to head (bbff2d7).

Files with missing lines Patch % Lines
giga/deps/store/cachekv.go 95.45% 1 Missing ⚠️
sei-cosmos/store/cachekv/store.go 96.42% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2855      +/-   ##
==========================================
- Coverage   46.28%   46.23%   -0.05%     
==========================================
  Files        2005     2005              
  Lines      163018   163027       +9     
==========================================
- Hits        75449    75374      -75     
- Misses      81047    81131      +84     
  Partials     6522     6522              
Flag Coverage Δ
sei-chain 40.86% <96.07%> (-0.06%) ⬇️
sei-cosmos 48.12% <96.55%> (+<0.01%) ⬆️
sei-db 68.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sei-cosmos/store/cachekv/memiterator.go 81.81% <100.00%> (ø)
giga/deps/store/cachekv.go 78.40% <95.45%> (-1.38%) ⬇️
sei-cosmos/store/cachekv/store.go 86.26% <96.42%> (-2.33%) ⬇️

... and 11 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment on lines +117 to +120
for key, cv := range store.cache {
// Replace with a clean (non-dirty) version of the same value
store.cache.Store(key, types.NewCValue(cv.Value(), false))
return true
})
store.cache[key] = types.NewCValue(cv.Value(), false)
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +173 to +184
for key, cv := range store.cache {
kbz := []byte(key)
if bytes.Compare(kbz, start) < 0 || bytes.Compare(kbz, end) >= 0 {
// we don't want to break out of the iteration since cache isn't sorted
return true
continue
}
cv := value.(*types.CValue)
if cv.Value() == nil {
delete(keyStrs, key.(string))
delete(keyStrs, key)
} else {
keyStrs[key.(string)] = struct{}{}
keyStrs[key] = struct{}{}
}
return true
})
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +276 to +282
for cKey := range store.unsortedCache {
if dbm.IsKeyInDomain(conv.UnsafeStrToBytes(cKey), start, end) {
cacheValue, ok := store.cache.Load(key)
if ok {
unsorted = append(unsorted, &kv.Pair{Key: []byte(cKey), Value: cacheValue.(*types.CValue).Value()})
if cacheValue, ok := store.cache[cKey]; ok {
unsorted = append(unsorted, &kv.Pair{Key: []byte(cKey), Value: cacheValue.Value()})
}
}
return true
})
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +359 to +370
for key, cv := range store.cache {
kbz := []byte(key)
if bytes.Compare(kbz, start) < 0 || bytes.Compare(kbz, end) >= 0 {
// we don't want to break out of the iteration since cache isn't sorted
return true
continue
}
cv := value.(*types.CValue)
if cv.Value() == nil {
delete(keyStrs, key.(string))
delete(keyStrs, key)
} else {
keyStrs[key.(string)] = struct{}{}
keyStrs[key] = struct{}{}
}
return true
})
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
@pdrobnjak pdrobnjak marked this pull request as draft February 11, 2026 14:01
@pdrobnjak pdrobnjak self-assigned this Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant