From b22495767727e25d2abe812ee13b855fe0f5cb96 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 3 Feb 2022 11:46:09 +0300 Subject: [PATCH] storage: attempt to Clone Allows to regain ~3-4% TPS, doesn't affect chain restore time much. --- pkg/core/blockchain_test.go | 2 +- pkg/core/dao/dao.go | 10 ++++++++++ pkg/core/interop/context.go | 2 +- pkg/core/storage/memcached_store.go | 20 ++++++++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 492c74c1d..2739d9504 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -1116,7 +1116,7 @@ func TestVerifyHashAgainstScript(t *testing.T) { bc := newTestChain(t) cs, csInvalid := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test - ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil) + ic := bc.newInteropContext(trigger.Verification, bc.dao.GetWrapped(), nil, nil) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, csInvalid)) diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index aff495107..faf8f585d 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -50,6 +50,7 @@ type DAO interface { GetStorageItemsWithPrefix(id int32, prefix []byte) ([]state.StorageItemWithKey, error) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) GetVersion() (Version, error) + GetCloned() DAO GetWrapped() DAO HasTransaction(hash util.Uint256) error Persist() (int, error) @@ -102,6 +103,15 @@ func (dao *Simple) GetWrapped() DAO { return d } +// GetCloned returns new DAO instance with shared trie of MemCachedStore, use it for +// the latest layer that either doesn't need to Persist, or Persists to another well-known +// non-shared (!) layer. +func (dao *Simple) GetCloned() DAO { + d := *dao + d.Store = dao.Store.Clone() + return &d +} + // GetAndDecode performs get operation and decoding with serializable structures. func (dao *Simple) GetAndDecode(entity io.Serializable, key []byte) error { entityBytes, err := dao.Store.Get(key) diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 26a0e723c..38dd64df0 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -71,7 +71,7 @@ func NewContext(trigger trigger.Type, bc Ledger, d dao.DAO, getContract func(dao.DAO, util.Uint160) (*state.Contract, error), natives []Contract, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context { baseExecFee := int64(DefaultBaseExecFee) - dao := d.GetWrapped() + dao := d.GetCloned() if bc != nil && (block == nil || block.Index != 0) { baseExecFee = bc.GetBaseExecFee() diff --git a/pkg/core/storage/memcached_store.go b/pkg/core/storage/memcached_store.go index e922fc297..ab205016f 100644 --- a/pkg/core/storage/memcached_store.go +++ b/pkg/core/storage/memcached_store.go @@ -14,6 +14,9 @@ import ( type MemCachedStore struct { MemoryStore + // lowerTrie stores lower level MemCachedStore trie for cloned MemCachedStore, + // which allows for much more efficient Persist. + lowerTrie *btree.BTree // plock protects Persist from double entrance. plock sync.Mutex // Persistent Store. @@ -55,6 +58,16 @@ func NewMemCachedStore(lower Store) *MemCachedStore { } } +// NewClonedMemCachedStore creates a cloned MemCachedStore which shares the trie +// with another MemCachedStore (until you write into it). +func (s *MemCachedStore) Clone() *MemCachedStore { + return &MemCachedStore{ + MemoryStore: MemoryStore{mem: *s.mem.Clone()}, // Shared COW trie. + lowerTrie: &s.mem, + ps: s.ps, // But the same PS. + } +} + // Get implements the Store interface. func (s *MemCachedStore) Get(key []byte) ([]byte, error) { s.mut.RLock() @@ -239,6 +252,13 @@ func (s *MemCachedStore) persist(isSync bool) (int, error) { defer s.plock.Unlock() s.mut.Lock() + if s.lowerTrie != nil { + keys = s.mem.Len() - s.lowerTrie.Len() + *s.lowerTrie = s.mem + s.mut.Unlock() + return keys, nil + } + keys = s.mem.Len() if keys == 0 { s.mut.Unlock()