Skip to content
Snippets Groups Projects
Unverified Commit 8fe9edea authored by Silas Davis's avatar Silas Davis Committed by GitHub
Browse files

Merge pull request #733 from hyperledger/state-cache-fixes

State cache fixes
parents 76261dea 5f252121
No related branches found
No related tags found
No related merge requests found
......@@ -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
}
......@@ -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
}
......
......@@ -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()
......
......@@ -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
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment