storage: attempt to Clone
Allows to regain ~3-4% TPS, doesn't affect chain restore time much.
This commit is contained in:
parent
85cc5f4247
commit
b224957677
4 changed files with 32 additions and 2 deletions
|
@ -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))
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue