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)
|
bc := newTestChain(t)
|
||||||
|
|
||||||
cs, csInvalid := getTestContractState(t, 4, 5, random.Uint160()) // sender and IDs are not important for the test
|
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, cs))
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, csInvalid))
|
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)
|
GetStorageItemsWithPrefix(id int32, prefix []byte) ([]state.StorageItemWithKey, error)
|
||||||
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
||||||
GetVersion() (Version, error)
|
GetVersion() (Version, error)
|
||||||
|
GetCloned() DAO
|
||||||
GetWrapped() DAO
|
GetWrapped() DAO
|
||||||
HasTransaction(hash util.Uint256) error
|
HasTransaction(hash util.Uint256) error
|
||||||
Persist() (int, error)
|
Persist() (int, error)
|
||||||
|
@ -102,6 +103,15 @@ func (dao *Simple) GetWrapped() DAO {
|
||||||
return d
|
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.
|
// GetAndDecode performs get operation and decoding with serializable structures.
|
||||||
func (dao *Simple) GetAndDecode(entity io.Serializable, key []byte) error {
|
func (dao *Simple) GetAndDecode(entity io.Serializable, key []byte) error {
|
||||||
entityBytes, err := dao.Store.Get(key)
|
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,
|
getContract func(dao.DAO, util.Uint160) (*state.Contract, error), natives []Contract,
|
||||||
block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
||||||
baseExecFee := int64(DefaultBaseExecFee)
|
baseExecFee := int64(DefaultBaseExecFee)
|
||||||
dao := d.GetWrapped()
|
dao := d.GetCloned()
|
||||||
|
|
||||||
if bc != nil && (block == nil || block.Index != 0) {
|
if bc != nil && (block == nil || block.Index != 0) {
|
||||||
baseExecFee = bc.GetBaseExecFee()
|
baseExecFee = bc.GetBaseExecFee()
|
||||||
|
|
|
@ -14,6 +14,9 @@ import (
|
||||||
type MemCachedStore struct {
|
type MemCachedStore struct {
|
||||||
MemoryStore
|
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 protects Persist from double entrance.
|
||||||
plock sync.Mutex
|
plock sync.Mutex
|
||||||
// Persistent Store.
|
// 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.
|
// Get implements the Store interface.
|
||||||
func (s *MemCachedStore) Get(key []byte) ([]byte, error) {
|
func (s *MemCachedStore) Get(key []byte) ([]byte, error) {
|
||||||
s.mut.RLock()
|
s.mut.RLock()
|
||||||
|
@ -239,6 +252,13 @@ func (s *MemCachedStore) persist(isSync bool) (int, error) {
|
||||||
defer s.plock.Unlock()
|
defer s.plock.Unlock()
|
||||||
s.mut.Lock()
|
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()
|
keys = s.mem.Len()
|
||||||
if keys == 0 {
|
if keys == 0 {
|
||||||
s.mut.Unlock()
|
s.mut.Unlock()
|
||||||
|
|
Loading…
Reference in a new issue