diff --git a/account/state/state_cache.go b/account/state/state_cache.go index f9d85c979c2e253a6d417f49338d3579894663f6..04107211e78eb756221a7c02293d8f3f3a66a8ca 100644 --- a/account/state/state_cache.go +++ b/account/state/state_cache.go @@ -24,16 +24,15 @@ import ( ) type Cache interface { - IterableWriter + Writer Sync(state Writer) error Reset(backend Iterable) Flush(state IterableWriter) error - Backend() Iterable } type stateCache struct { sync.RWMutex - backend Iterable + backend Reader accounts map[acm.Address]*accountInfo } @@ -47,7 +46,7 @@ type accountInfo struct { // Returns a Cache that wraps an underlying Reader to use on a cache miss, can write to an output Writer // via Sync. Goroutine safe for concurrent access. -func NewCache(backend Iterable) Cache { +func NewCache(backend Reader) Cache { return &stateCache{ backend: backend, accounts: make(map[acm.Address]*accountInfo), @@ -97,17 +96,18 @@ func (cache *stateCache) RemoveAccount(address acm.Address) error { return nil } -// Iterates over all accounts first in cache and then in backend until consumer returns true for 'stop' -func (cache *stateCache) IterateAccounts(consumer func(acm.Account) (stop bool)) (stopped bool, err error) { +// Iterates over all cached accounts first in cache and then in backend until consumer returns true for 'stop' +func (cache *stateCache) IterateCachedAccount(consumer func(acm.Account) (stop bool)) (stopped bool, err error) { // Try cache first for early exit cache.RLock() for _, info := range cache.accounts { if consumer(info.account) { + cache.RUnlock() return true, nil } } cache.RUnlock() - return cache.backend.IterateAccounts(consumer) + return false, nil } func (cache *stateCache) GetStorage(address acm.Address, key binary.Word256) (binary.Word256, error) { @@ -119,19 +119,20 @@ func (cache *stateCache) GetStorage(address acm.Address, key binary.Word256) (bi accInfo.RLock() value, ok := accInfo.storage[key] accInfo.RUnlock() - if ok { - return value, nil - } else { - // Load from backend - value, err := cache.backend.GetStorage(address, key) - if err != nil { - return binary.Zero256, err - } + if !ok { accInfo.Lock() - accInfo.storage[key] = value - accInfo.Unlock() - return value, nil + defer accInfo.Unlock() + value, ok = accInfo.storage[key] + if !ok { + // Load from backend + value, err = cache.backend.GetStorage(address, key) + if err != nil { + return binary.Zero256, err + } + accInfo.storage[key] = value + } } + return value, nil } // NOTE: Set value to zero to remove. @@ -150,8 +151,8 @@ func (cache *stateCache) SetStorage(address acm.Address, key binary.Word256, val return nil } -// Iterates over all storage items first in cache and then in backend until consumer returns true for 'stop' -func (cache *stateCache) IterateStorage(address acm.Address, +// Iterates over all cached storage items first in cache and then in backend until consumer returns true for 'stop' +func (cache *stateCache) IterateCachedStorage(address acm.Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) { accInfo, err := cache.get(address) if err != nil { @@ -161,11 +162,12 @@ func (cache *stateCache) IterateStorage(address acm.Address, // Try cache first for early exit for key, value := range accInfo.storage { if consumer(key, value) { + accInfo.RUnlock() return true, nil } } accInfo.RUnlock() - return cache.backend.IterateStorage(address, consumer) + return false, nil } // Syncs changes to the backend in deterministic order. Sends storage updates before updating @@ -231,27 +233,26 @@ func (cache *stateCache) Flush(state IterableWriter) error { return nil } -func (cache *stateCache) Backend() Iterable { - return cache.backend -} - // Get the cache accountInfo item creating it if necessary func (cache *stateCache) get(address acm.Address) (*accountInfo, error) { cache.RLock() accInfo := cache.accounts[address] cache.RUnlock() if accInfo == nil { - account, err := cache.backend.GetAccount(address) - if err != nil { - return nil, err - } - accInfo = &accountInfo{ - account: account, - storage: make(map[binary.Word256]binary.Word256), - } cache.Lock() - cache.accounts[address] = accInfo - cache.Unlock() + defer cache.Unlock() + accInfo = cache.accounts[address] + if accInfo == nil { + account, err := cache.backend.GetAccount(address) + if err != nil { + return nil, err + } + accInfo = &accountInfo{ + account: account, + storage: make(map[binary.Word256]binary.Word256), + } + cache.accounts[address] = accInfo + } } return accInfo, nil } diff --git a/account/state/state_cache_test.go b/account/state/state_cache_test.go index de9683b2fbdc16f8ec93dc0577408b76431d04e5..a0c5bc80ec8afc715c962d6d45a722ba4b75a4d0 100644 --- a/account/state/state_cache_test.go +++ b/account/state/state_cache_test.go @@ -78,6 +78,16 @@ func TestStateCache_UpdateAccount(t *testing.T) { } func TestStateCache_RemoveAccount(t *testing.T) { + // Build backend states for read and write + readBackend := testAccounts() + cache := NewCache(readBackend) + + acc := readBackend.Accounts[addressOf("acc1")] + err := cache.RemoveAccount(acc.Address()) + require.NoError(t, err) + + dead, err := cache.GetAccount(acc.Address()) + assert.Nil(t, dead, err) } func TestStateCache_GetStorage(t *testing.T) { @@ -121,8 +131,6 @@ type testState struct { Storage map[acm.Address]map[binary.Word256]binary.Word256 } -var _ Iterable = &testState{} - func newTestState() *testState { return &testState{ Accounts: make(map[acm.Address]acm.Account), @@ -155,24 +163,6 @@ func word(str string) binary.Word256 { return binary.LeftPadWord256([]byte(str)) } -func (tsr *testState) IterateAccounts(consumer func(acm.Account) (stop bool)) (stopped bool, err error) { - for _, acc := range tsr.Accounts { - if consumer(acc) { - return true, nil - } - } - return false, nil -} - -func (tsr *testState) IterateStorage(address acm.Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) { - for key, value := range tsr.Storage[address] { - if consumer(key, value) { - return true, nil - } - } - return false, nil -} - func (tsr *testState) GetAccount(address acm.Address) (acm.Account, error) { return tsr.Accounts[address], nil } diff --git a/execution/execution.go b/execution/execution.go index 7b5a9ee7ea77cb3c5a6d4d66d68eb118ead05159..58277636ad5dab3f1242bb5444ea1be3d0a93143 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -37,9 +37,7 @@ import ( const GasLimit = uint64(1000000) type BatchExecutor interface { - state.Iterable - state.AccountUpdater - state.StorageSetter + state.Reader // Execute transaction against block cache (i.e. block buffer) Execute(tx txs.Tx) error // Reset executor to underlying State @@ -120,31 +118,11 @@ func (exe *executor) GetAccount(address acm.Address) (acm.Account, error) { return exe.stateCache.GetAccount(address) } -func (exe *executor) UpdateAccount(account acm.Account) error { - return exe.stateCache.UpdateAccount(account) -} - -func (exe *executor) RemoveAccount(address acm.Address) error { - return exe.stateCache.RemoveAccount(address) -} - -func (exe *executor) IterateAccounts(consumer func(acm.Account) bool) (bool, error) { - return exe.stateCache.IterateAccounts(consumer) -} - // Storage func (exe *executor) GetStorage(address acm.Address, key binary.Word256) (binary.Word256, error) { return exe.stateCache.GetStorage(address, key) } -func (exe *executor) SetStorage(address acm.Address, key binary.Word256, value binary.Word256) error { - return exe.stateCache.SetStorage(address, key, value) -} - -func (exe *executor) IterateStorage(address acm.Address, consumer func(key, value binary.Word256) bool) (bool, error) { - return exe.stateCache.IterateStorage(address, consumer) -} - func (exe *executor) Commit() (hash []byte, err error) { exe.Lock() defer exe.Unlock() diff --git a/execution/namereg_cache.go b/execution/namereg_cache.go index f941b9f9ba370057153acd781f3a8cd6ce2496c9..5cb0c63aacd2b8d1851f3901fb93dae044cd7ae2 100644 --- a/execution/namereg_cache.go +++ b/execution/namereg_cache.go @@ -109,11 +109,13 @@ func (cache *NameRegCache) Sync(state NameRegWriter) error { if nameInfo.removed { err := state.RemoveNameRegEntry(name) if err != nil { + nameInfo.RUnlock() return err } } else if nameInfo.updated { err := state.UpdateNameRegEntry(nameInfo.entry) if err != nil { + nameInfo.RUnlock() return err } } @@ -150,16 +152,19 @@ func (cache *NameRegCache) get(name string) (*nameInfo, error) { nmeInfo := cache.names[name] cache.RUnlock() if nmeInfo == nil { - entry, err := cache.backend.GetNameRegEntry(name) - if err != nil { - return nil, err - } - nmeInfo = &nameInfo{ - entry: entry, - } cache.Lock() - cache.names[name] = nmeInfo - cache.Unlock() + defer cache.Unlock() + nmeInfo = cache.names[name] + if nmeInfo == nil { + entry, err := cache.backend.GetNameRegEntry(name) + if err != nil { + return nil, err + } + nmeInfo = &nameInfo{ + entry: entry, + } + cache.names[name] = nmeInfo + } } return nmeInfo, nil }