Skip to content
This repository has been archived by the owner on Jun 9, 2024. It is now read-only.

feat(cachekv): Optimize CacheKV by making SDK Upstream changes. #9

Merged
merged 58 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
8104037
add cachekv btree
Jan 11, 2023
d5e0489
bing bong
Jan 11, 2023
8afe910
stack
Jan 11, 2023
9303cf6
gomod
Jan 11, 2023
3534072
format
Jan 11, 2023
5376766
lmao what
Jan 11, 2023
fce73d8
lint
Jan 11, 2023
390c8a3
deep copy
Jan 11, 2023
1f4ff6c
trees to lib
Jan 11, 2023
e49053f
common
Jan 11, 2023
130a1c6
remove sdk alias
Jan 11, 2023
e986d02
lint imports
Jan 11, 2023
7052697
small cleanup
Jan 11, 2023
b3ce5f5
small cleanup
Jan 11, 2023
a5e4365
small cleanup
Jan 11, 2023
23e1bce
upgrade cachekv
Jan 11, 2023
7c9e9ac
improvements
Jan 11, 2023
b8beaeb
optimize control flow
Jan 11, 2023
4e28d4b
cvalue
Jan 11, 2023
fb19474
bing bong
Jan 11, 2023
29e490c
lint
Jan 11, 2023
e6ef005
bing bong
Jan 11, 2023
d1d4d56
cleanup test commands a bit
Jan 11, 2023
71b471a
Merge branch 'main' into cachekv
Jan 11, 2023
35ead99
Update lib/ds/stack.go
Jan 11, 2023
f4cd915
Update lib/ds/stack.go
Jan 11, 2023
262aedc
Merge branch 'main' into cachekv
Jan 11, 2023
f9a8a45
Merge branch 'cachekv' into upgrade-cachekv
Jan 11, 2023
1a11eac
typo
Jan 11, 2023
9d09be4
comments
Jan 11, 2023
cd3e74e
Revert "comments"
Jan 11, 2023
2e168ad
comments
Jan 11, 2023
b58b43d
Merge branch 'cachekv' into upgrade-cachekv
Jan 11, 2023
c1b0047
Merge branch 'main' into cachekv
Jan 11, 2023
6caa813
Merge branch 'main' into cachekv
Jan 11, 2023
770beb8
Merge branch 'cachekv' into upgrade-cachekv
Jan 11, 2023
cfd316d
lint
Jan 11, 2023
3ea6816
Apply suggestions from code review
Jan 11, 2023
50bd41f
Merge branch 'cachekv' into upgrade-cachekv
Jan 11, 2023
08ce21f
bing bong
Jan 12, 2023
365f0bf
Merge branch 'main' into cachekv
Jan 12, 2023
d8e6c68
Merge branch 'cachekv' into upgrade-cachekv
Jan 12, 2023
80ec2a5
merge main
Jan 13, 2023
1a8c381
cache value implements Cloneable
calbera Jan 13, 2023
ff4a882
clean struct creators
calbera Jan 13, 2023
62e806c
comments
calbera Jan 13, 2023
74a6573
linter
Jan 13, 2023
d54f460
Merge branch 'main' into upgrade-cachekv
Jan 13, 2023
e92845c
impr(cachekv): Remove DeleteCacheValue (#19)
Jan 13, 2023
71c9add
Merge branch 'main' into upgrade-cachekv
Jan 13, 2023
15eee26
benchmarks
Jan 13, 2023
6944532
convert more tests
Jan 13, 2023
5f661ab
rename
Jan 13, 2023
8162272
test name
Jan 13, 2023
8479b36
renaming, cleanup
calbera Jan 13, 2023
df6b357
Merge branch 'upgrade-cachekv' of github.com:berachain/stargazer into…
calbera Jan 13, 2023
d8d81d3
fix merge
Jan 13, 2023
5c8679a
lint
Jan 13, 2023
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
78 changes: 78 additions & 0 deletions core/state/store/cachekv/cache_entry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (C) 2022, Berachain Foundation. All rights reserved.
// See the file LICENSE for licensing terms.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package cachekv

import "github.com/berachain/stargazer/core/state/store/journal"

// Compile-time check to ensure `cacheEntry` implements `journal.CacheEntry`.
var _ journal.CacheEntry = (*cacheEntry)(nil)

// `cacheEntry` is a struct that contains information needed to set a value in a cache.
type cacheEntry struct {
Store *Store // Pointer to the cache store.
Key string // Key of the value to be set.
Prev *cacheValue // Deep copy of object in cache map.
}

// `newCacheEntry` creates a new `cacheEntry` object for the given `store`, `key`, and `prev`
// cache value.
func newCacheEntry(store *Store, key string, prev *cacheValue) *cacheEntry {
// create a deep copy of the Prev field, if it is not nil.
if prev != nil {
prev = prev.Clone()
}

return &cacheEntry{
Store: store,
Key: key,
Prev: prev,
}
}

// `Revert` reverts a set operation on a cache entry by setting the previous value of the entry as
// the current value in the cache map.
//
// `Revert` implements journal.cacheEntry.
func (ce *cacheEntry) Revert() {
// If there was a previous value, set it as the current value in the cache map
if ce.Prev == nil {
// If there was no previous value, remove the Key from the
// cache map and the unsorted cache set
delete(ce.Store.Cache, ce.Key)
delete(ce.Store.UnsortedCache, ce.Key)
return
}

// If there was a previous value, set it sas the current value in the cache map.
ce.Store.Cache[ce.Key] = ce.Prev

// If the previous value was not dirty, remove the Key from the unsorted cache set
if !ce.Prev.dirty {
delete(ce.Store.UnsortedCache, ce.Key)
}
}

// `Clone` creates a deep copy of the cacheEntry object.
// The deep copy contains the same Store and Key fields as the original,
// and a deep copy of the Prev field, if it is not nil.s
//
// `Clone` implements `journal.cacheEntry`.
//
//nolint:nolintlint,ireturn // by design.
func (ce *cacheEntry) Clone() journal.CacheEntry {
// Return a new cacheEntry object with the same Store and Key fields as the original,
// and the Prev field set to the deep copy of the original Prev field (or nil if the original
// was nil).
return newCacheEntry(ce.Store, ce.Key, ce.Prev)
}
172 changes: 172 additions & 0 deletions core/state/store/cachekv/cache_entry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright (C) 2022, Berachain Foundation. All rights reserved.
// See the file LICENSE for licensing terms.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package cachekv

import (
"reflect"
"testing"

sdkcachekv "github.com/cosmos/cosmos-sdk/store/cachekv"
"github.com/cosmos/cosmos-sdk/store/dbadapter"
"github.com/stretchr/testify/suite"
dbm "github.com/tendermint/tm-db"

"github.com/berachain/stargazer/core/state/store/journal"
"github.com/berachain/stargazer/lib/utils"
)

var (
byte0 = []byte{0}
byte1 = []byte{1}
byte0Str = utils.UnsafeBytesToStr(byte0)
byte1Str = utils.UnsafeBytesToStr(byte1)
)

type CacheValueSuite struct {
suite.Suite
cacheKVStore *Store
}

func TestCacheValueSuite(t *testing.T) {
suite.Run(t, new(CacheValueSuite))
}

func (s *CacheValueSuite) SetupTest() {
parent := sdkcachekv.NewStore(dbadapter.Store{DB: dbm.NewMemDB()})
parent.Set(byte0, byte0)
s.cacheKVStore = NewStore(parent, journal.NewManager())
}

func (s *CacheValueSuite) TestRevertDeleteAfterNothing() {
// delete after nothing happened to key
snapshot := s.cacheKVStore.JournalMgr().Size()
// delete key: 0
s.cacheKVStore.Delete(byte0)
s.Require().Equal(([]byte)(nil), s.cacheKVStore.Cache[byte0Str].value)
s.Require().True(s.cacheKVStore.Cache[byte0Str].dirty)
s.Require().Contains(s.cacheKVStore.UnsortedCache, byte0Str)
// revert delete key: 0
s.cacheKVStore.JournalMgr().PopToSize(snapshot)
s.Require().NotContains(s.cacheKVStore.Cache, byte0Str)
s.Require().NotContains(s.cacheKVStore.UnsortedCache, byte0Str)
}

func (s *CacheValueSuite) TestRevertDeleteAfterGet() {
// delete after Get called on key
_ = s.cacheKVStore.Get(byte0)
snapshot := s.cacheKVStore.JournalMgr().Size()
// delete key: 0
s.cacheKVStore.Delete(byte0)
// revert delete key: 0
s.cacheKVStore.JournalMgr().PopToSize(snapshot)
s.Require().Equal(byte0, s.cacheKVStore.Cache[byte0Str].value)
s.Require().False(s.cacheKVStore.Cache[byte0Str].dirty)
s.Require().NotContains(s.cacheKVStore.UnsortedCache, byte0Str)
}

func (s *CacheValueSuite) TestRevertDeleteAfterSet() {
// delete after Set called on key
s.cacheKVStore.Set(byte1, byte1)
snapshot := s.cacheKVStore.JournalMgr().Size()
// delete key: 1
s.cacheKVStore.Delete(byte1)
// revert delete key: 1
s.cacheKVStore.JournalMgr().PopToSize(snapshot)
s.Require().Equal(byte1, s.cacheKVStore.Cache[byte1Str].value)
s.Require().True(s.cacheKVStore.Cache[byte1Str].dirty)
s.Require().Contains(s.cacheKVStore.UnsortedCache, byte1Str)
}

func (s *CacheValueSuite) TestRevertDeleteAfterDelete() {
// delete after Delete called on key
s.cacheKVStore.Delete(byte0)
snapshot := s.cacheKVStore.JournalMgr().Size()
// delete key: 0
s.cacheKVStore.Delete(byte0)
// revert delete key: 0
s.cacheKVStore.JournalMgr().PopToSize(snapshot)
s.Require().Equal(([]byte)(nil), s.cacheKVStore.Cache[byte0Str].value)
s.Require().True(s.cacheKVStore.Cache[byte0Str].dirty)
s.Require().Contains(s.cacheKVStore.UnsortedCache, byte0Str)
}

func (s *CacheValueSuite) TestRevertSetAfterNothing() {
// set after nothing happened to key
snapshot := s.cacheKVStore.JournalMgr().Size()
// set key: 1
s.cacheKVStore.Set(byte1, byte1)
s.Require().Equal(byte1, s.cacheKVStore.Cache[byte1Str].value)
s.Require().True(s.cacheKVStore.Cache[byte1Str].dirty)
s.Require().Contains(s.cacheKVStore.UnsortedCache, byte1Str)
// revert set key: 1
s.cacheKVStore.JournalMgr().PopToSize(snapshot)
s.Require().NotContains(s.cacheKVStore.Cache, byte1Str)
s.Require().NotContains(s.cacheKVStore.UnsortedCache, byte1Str)
}

func (s *CacheValueSuite) TestRevertSetAfterGet() {
// set after get called on key
_ = s.cacheKVStore.Get(byte0)
snapshot := s.cacheKVStore.JournalMgr().Size()
// set key: 0 to val: 1
s.cacheKVStore.Set(byte0, byte1)
// revert set key: 1 to val: 1
s.cacheKVStore.JournalMgr().PopToSize(snapshot)
s.Require().Equal(byte0, s.cacheKVStore.Cache[byte0Str].value)
s.Require().False(s.cacheKVStore.Cache[byte0Str].dirty)
s.Require().NotContains(s.cacheKVStore.UnsortedCache, byte0Str)
}

func (s *CacheValueSuite) TestRevertSetAfterDelete() {
// set after delete called on key
s.cacheKVStore.Delete(byte0)
snapshot := s.cacheKVStore.JournalMgr().Size()
// set key: 0 to val: 0
s.cacheKVStore.Set(byte0, byte0)
// revert set key: 0 to val: 0
s.cacheKVStore.JournalMgr().PopToSize(snapshot)
s.Require().Nil(s.cacheKVStore.Cache[byte0Str].value)
s.Require().True(s.cacheKVStore.Cache[byte0Str].dirty)
s.Require().Contains(s.cacheKVStore.UnsortedCache, byte0Str)
}

func (s *CacheValueSuite) TestRevertSetAfterSet() {
// set after set called on key
s.cacheKVStore.Set(byte1, byte1)
snapshot := s.cacheKVStore.JournalMgr().Size()
// set key: 1 to val: 0
s.cacheKVStore.Set(byte1, byte0)
// revert set key: 1 to val: 0
s.cacheKVStore.JournalMgr().PopToSize(snapshot)
s.Require().Equal(byte1, s.cacheKVStore.Cache[byte1Str].value)
s.Require().True(s.cacheKVStore.Cache[byte1Str].dirty)
s.Require().Contains(s.cacheKVStore.UnsortedCache, byte1Str)
}

func (s *CacheValueSuite) TestCloneSet() {
dcvNonNil := newCacheEntry(s.cacheKVStore, byte1Str, newCacheValue(byte1, true))
dcvNonNilClone, ok := dcvNonNil.Clone().(*cacheEntry)
s.Require().True(ok)
s.Require().Equal(byte1Str, dcvNonNilClone.Key)
s.Require().True(dcvNonNilClone.Prev.dirty)
s.Require().Equal(byte1, dcvNonNilClone.Prev.value)

dcvNil := newCacheEntry(s.cacheKVStore, "", nil)
dcvNilClone, ok := dcvNil.Clone().(*cacheEntry)
s.Require().True(ok)
s.Require().Equal("", dcvNilClone.Key)
s.Require().Equal(dcvNil.Prev, dcvNilClone.Prev)
s.Require().True(reflect.ValueOf(dcvNilClone.Prev).IsNil())
}
41 changes: 41 additions & 0 deletions core/state/store/cachekv/cache_value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (C) 2023, Berachain Foundation. All rights reserved.
// See the file LICENSE for licensing terms.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package cachekv

import "github.com/berachain/stargazer/types"

// Compile-time assertion that `cacheValue` implements `types.Cloneable`.
var _ types.Cloneable[*cacheValue] = (*cacheValue)(nil)

// `cacheValue` represents a cached value in the cachekv store.
// If dirty is true, it indicates the cached value is different from the underlying value.
type cacheValue struct {
value []byte
dirty bool
}

// `newCacheValue` creates a new `cacheValue` object with the given `value` and `dirty` flag.
func newCacheValue(v []byte, d bool) *cacheValue {
return &cacheValue{
value: v,
dirty: d,
}
}

// `Clone` implements `types.Cloneable`.
func (cv *cacheValue) Clone() *cacheValue {
// Return a new cacheValue with the same value and dirty flag
return newCacheValue(append([]byte(nil), cv.value...), cv.dirty)
}
69 changes: 69 additions & 0 deletions core/state/store/cachekv/evm_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (C) 2022, Berachain Foundation. All rights reserved.
// See the file LICENSE for licensing terms.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package cachekv

import (
storetypes "github.com/cosmos/cosmos-sdk/store/types"

"github.com/berachain/stargazer/core/state/store/journal"
"github.com/berachain/stargazer/lib/utils"
)

// Compile-time check to ensure `EvmStore` implements `storetypes.CacheKVStore`.
var _ storetypes.CacheKVStore = (*EvmStore)(nil)

// `EVMStore` is a cache kv store that avoids the mutex lock for EVM stores (codes/storage).
// Writes to the EVM are thread-safe because the EVM interpreter is guaranteed to be single
// threaded. All entry points to the EVM check that only a single execution context is running.
type EvmStore struct {
*Store
}

// `NewEvmStore` creates a new Store object.
func NewEvmStore(parent storetypes.KVStore, journalMgr *journal.Manager) *EvmStore {
return &EvmStore{
NewStore(parent, journalMgr),
}
}

// `Get` shadows Store.Get
// This function retrieves a value associated with the specified key in the store.
func (store *EvmStore) Get(key []byte) []byte {
var bz []byte
// Check if the key is in the store's cache.
cacheValue, ok := store.Cache[utils.UnsafeBytesToStr(key)]
if ok {
// If the key is in the cache, return the value.
return cacheValue.value
}

// If the key is not found in the cache, query the parent store.
bz = store.Parent.Get(key)

// Add the key-value pair to the cache.
store.setCacheValue(key, bz, false)

return bz
}

// `Set` shadows Store.Set.
func (store *EvmStore) Set(key []byte, value []byte) {
store.setCacheValue(key, value, true)
}

// `Delete` shadows Store.Delete.
func (store *EvmStore) Delete(key []byte) {
store.setCacheValue(key, nil, true)
}
Loading