core: move native cache from MemCachedStore to DAO

This commit is contained in:
Anna Shaleva 2022-04-20 17:47:48 +03:00
parent b77b412b04
commit 8d2d48f360
9 changed files with 181 additions and 170 deletions

View file

@ -7,6 +7,7 @@ import (
"errors" "errors"
"fmt" "fmt"
iocore "io" iocore "io"
"sync"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
@ -34,11 +35,28 @@ var (
type Simple struct { type Simple struct {
Version Version Version Version
Store *storage.MemCachedStore Store *storage.MemCachedStore
nativeCacheLock sync.RWMutex
nativeCache map[int32]NativeContractCache
// nativeCachePS is the backend store that provides functionality to store
// and retrieve multi-tier native contract cache. The lowest Simple has its
// nativeCachePS set to nil.
nativeCachePS *Simple
private bool private bool
keyBuf []byte keyBuf []byte
dataBuf *io.BufBinWriter dataBuf *io.BufBinWriter
} }
// NativeContractCache is an interface representing cache for a native contract.
// Cache can be copied to create a wrapper around current DAO layer. Wrapped cache
// can be persisted to the underlying DAO native cache.
type NativeContractCache interface {
// Copy returns a copy of native cache item that can safely be changed within
// the subsequent DAO operations.
Copy() NativeContractCache
}
// NewSimple creates new simple dao using provided backend store. // NewSimple creates new simple dao using provided backend store.
func NewSimple(backend storage.Store, stateRootInHeader bool, p2pSigExtensions bool) *Simple { func NewSimple(backend storage.Store, stateRootInHeader bool, p2pSigExtensions bool) *Simple {
st := storage.NewMemCachedStore(backend) st := storage.NewMemCachedStore(backend)
@ -52,7 +70,8 @@ func newSimple(st *storage.MemCachedStore, stateRootInHeader bool, p2pSigExtensi
StateRootInHeader: stateRootInHeader, StateRootInHeader: stateRootInHeader,
P2PSigExtensions: p2pSigExtensions, P2PSigExtensions: p2pSigExtensions,
}, },
Store: st, Store: st,
nativeCache: make(map[int32]NativeContractCache),
} }
} }
@ -66,6 +85,7 @@ func (dao *Simple) GetBatch() *storage.MemBatch {
func (dao *Simple) GetWrapped() *Simple { func (dao *Simple) GetWrapped() *Simple {
d := NewSimple(dao.Store, dao.Version.StateRootInHeader, dao.Version.P2PSigExtensions) d := NewSimple(dao.Store, dao.Version.StateRootInHeader, dao.Version.P2PSigExtensions)
d.Version = dao.Version d.Version = dao.Version
d.nativeCachePS = dao
return d return d
} }
@ -76,6 +96,13 @@ func (dao *Simple) GetPrivate() *Simple {
*d = *dao // Inherit everything... *d = *dao // Inherit everything...
d.Store = storage.NewPrivateMemCachedStore(dao.Store) // except storage, wrap another layer. d.Store = storage.NewPrivateMemCachedStore(dao.Store) // except storage, wrap another layer.
d.private = true d.private = true
d.nativeCachePS = dao
// Do not inherit cache from nativeCachePS; instead should create clear map:
// GetRWCache and GetROCache will retrieve cache from the underlying
// nativeCache if requested. The lowest underlying DAO MUST have its native
// cache initialized before access it, otherwise GetROCache and GetRWCache
// won't work properly.
d.nativeCache = make(map[int32]NativeContractCache)
return d return d
} }
@ -809,6 +836,17 @@ func (dao *Simple) getDataBuf() *io.BufBinWriter {
// Persist flushes all the changes made into the (supposedly) persistent // Persist flushes all the changes made into the (supposedly) persistent
// underlying store. It doesn't block accesses to DAO from other threads. // underlying store. It doesn't block accesses to DAO from other threads.
func (dao *Simple) Persist() (int, error) { func (dao *Simple) Persist() (int, error) {
if dao.nativeCachePS != nil {
if !dao.private {
dao.nativeCacheLock.Lock()
defer dao.nativeCacheLock.Unlock()
}
if !dao.nativeCachePS.private {
dao.nativeCachePS.nativeCacheLock.Lock()
defer dao.nativeCachePS.nativeCacheLock.Unlock()
}
dao.persistNativeCache()
}
return dao.Store.Persist() return dao.Store.Persist()
} }
@ -816,5 +854,77 @@ func (dao *Simple) Persist() (int, error) {
// underlying store. It's a synchronous version of Persist that doesn't allow // underlying store. It's a synchronous version of Persist that doesn't allow
// other threads to work with DAO while flushing the Store. // other threads to work with DAO while flushing the Store.
func (dao *Simple) PersistSync() (int, error) { func (dao *Simple) PersistSync() (int, error) {
if dao.nativeCachePS != nil {
dao.nativeCacheLock.Lock()
dao.nativeCachePS.nativeCacheLock.Lock()
defer func() {
dao.nativeCachePS.nativeCacheLock.Unlock()
dao.nativeCacheLock.Unlock()
}()
dao.persistNativeCache()
}
return dao.Store.PersistSync() return dao.Store.PersistSync()
} }
// persistNativeCache is internal unprotected method for native cache persisting.
// It does NO checks for nativeCachePS is not nil.
func (dao *Simple) persistNativeCache() {
lower := dao.nativeCachePS
for id, nativeCache := range dao.nativeCache {
lower.nativeCache[id] = nativeCache
}
dao.nativeCache = nil
}
// GetROCache returns native contact cache. The cache CAN NOT be modified by
// the caller. It's the caller's duty to keep it unmodified.
func (dao *Simple) GetROCache(id int32) NativeContractCache {
if !dao.private {
dao.nativeCacheLock.RLock()
defer dao.nativeCacheLock.RUnlock()
}
return dao.getCache(id, true)
}
// GetRWCache returns native contact cache. The cache CAN BE safely modified
// by the caller.
func (dao *Simple) GetRWCache(id int32) NativeContractCache {
if !dao.private {
dao.nativeCacheLock.Lock()
defer dao.nativeCacheLock.Unlock()
}
return dao.getCache(id, false)
}
// getCache is an internal unlocked representation of GetROCache and GetRWCache.
func (dao *Simple) getCache(k int32, ro bool) NativeContractCache {
if itm, ok := dao.nativeCache[k]; ok {
// Don't need to create itm copy, because its value was already copied
// the first time it was retrieved from loser ps.
return itm
}
if dao.nativeCachePS != nil {
if ro {
return dao.nativeCachePS.GetROCache(k)
}
v := dao.nativeCachePS.GetRWCache(k)
if v != nil {
// Create a copy here in order not to modify the existing cache.
cp := v.Copy()
dao.nativeCache[k] = cp
return cp
}
}
return nil
}
// SetCache adds native contract cache to the cache map.
func (dao *Simple) SetCache(id int32, v NativeContractCache) {
dao.nativeCacheLock.Lock()
defer dao.nativeCacheLock.Unlock()
dao.nativeCache[id] = v
}

View file

@ -12,7 +12,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator"
@ -533,7 +532,7 @@ func TestStorageFind(t *testing.T) {
func createVM(t testing.TB) (*vm.VM, *interop.Context, *Blockchain) { func createVM(t testing.TB) (*vm.VM, *interop.Context, *Blockchain) {
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, context := chain.newInteropContext(trigger.Application,
dao.NewSimple(chain.dao.Store, chain.config.StateRootInHeader, chain.config.P2PSigExtensions), nil, nil) chain.dao.GetWrapped(), nil, nil)
v := context.SpawnVM() v := context.SpawnVM()
return v, context, chain return v, context, chain
} }

View file

@ -79,12 +79,12 @@ var (
) )
var ( var (
_ interop.Contract = (*Designate)(nil) _ interop.Contract = (*Designate)(nil)
_ storage.NativeContractCache = (*DesignationCache)(nil) _ dao.NativeContractCache = (*DesignationCache)(nil)
) )
// Copy implements NativeContractCache interface. // Copy implements NativeContractCache interface.
func (c *DesignationCache) Copy() storage.NativeContractCache { func (c *DesignationCache) Copy() dao.NativeContractCache {
cp := &DesignationCache{} cp := &DesignationCache{}
copyDesignationCache(c, cp) copyDesignationCache(c, cp)
return cp return cp
@ -128,7 +128,7 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
// data in the storage. // data in the storage.
func (s *Designate) Initialize(ic *interop.Context) error { func (s *Designate) Initialize(ic *interop.Context) error {
cache := &DesignationCache{} cache := &DesignationCache{}
ic.DAO.Store.SetCache(s.ID, cache) ic.DAO.SetCache(s.ID, cache)
return nil return nil
} }
@ -146,7 +146,7 @@ func (s *Designate) InitializeCache(d *dao.Simple) error {
return fmt.Errorf("failed to get nodes from storage for %d role: %w", r, err) return fmt.Errorf("failed to get nodes from storage for %d role: %w", r, err)
} }
} }
d.Store.SetCache(s.ID, cache) d.SetCache(s.ID, cache)
return nil return nil
} }
@ -157,7 +157,7 @@ func (s *Designate) OnPersist(ic *interop.Context) error {
// PostPersist implements Contract interface. // PostPersist implements Contract interface.
func (s *Designate) PostPersist(ic *interop.Context) error { func (s *Designate) PostPersist(ic *interop.Context) error {
cache := ic.DAO.Store.GetRWCache(s.ID).(*DesignationCache) cache := ic.DAO.GetRWCache(s.ID).(*DesignationCache)
if !cache.rolesChangedFlag { if !cache.rolesChangedFlag {
return nil return nil
} }
@ -273,7 +273,7 @@ func (s *Designate) GetLastDesignatedHash(d *dao.Simple, r noderoles.Role) (util
if !s.isValidRole(r) { if !s.isValidRole(r) {
return util.Uint160{}, ErrInvalidRole return util.Uint160{}, ErrInvalidRole
} }
cache := d.Store.GetROCache(s.ID).(*DesignationCache) cache := d.GetROCache(s.ID).(*DesignationCache)
if val := getCachedRoleData(cache, r); val != nil { if val := getCachedRoleData(cache, r); val != nil {
return val.addr, nil return val.addr, nil
} }
@ -285,7 +285,7 @@ func (s *Designate) GetDesignatedByRole(d *dao.Simple, r noderoles.Role, index u
if !s.isValidRole(r) { if !s.isValidRole(r) {
return nil, 0, ErrInvalidRole return nil, 0, ErrInvalidRole
} }
cache := d.Store.GetROCache(s.ID).(*DesignationCache) cache := d.GetROCache(s.ID).(*DesignationCache)
if val := getCachedRoleData(cache, r); val != nil { if val := getCachedRoleData(cache, r); val != nil {
if val.height <= index { if val.height <= index {
return val.nodes.Copy(), val.height, nil return val.nodes.Copy(), val.height, nil
@ -380,7 +380,7 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs
return err return err
} }
cache := ic.DAO.Store.GetRWCache(s.ID).(*DesignationCache) cache := ic.DAO.GetRWCache(s.ID).(*DesignationCache)
err = s.updateCachedRoleData(cache, ic.DAO, r) err = s.updateCachedRoleData(cache, ic.DAO, r)
if err != nil { if err != nil {
return fmt.Errorf("failed to update Designation role data cache: %w", err) return fmt.Errorf("failed to update Designation role data cache: %w", err)

View file

@ -58,12 +58,12 @@ var (
) )
var ( var (
_ interop.Contract = (*Management)(nil) _ interop.Contract = (*Management)(nil)
_ storage.NativeContractCache = (*ManagementCache)(nil) _ dao.NativeContractCache = (*ManagementCache)(nil)
) )
// Copy implements NativeContractCache interface. // Copy implements NativeContractCache interface.
func (c *ManagementCache) Copy() storage.NativeContractCache { func (c *ManagementCache) Copy() dao.NativeContractCache {
cp := &ManagementCache{ cp := &ManagementCache{
contracts: make(map[util.Uint160]*state.Contract), contracts: make(map[util.Uint160]*state.Contract),
nep11: make(map[util.Uint160]struct{}), nep11: make(map[util.Uint160]struct{}),
@ -170,7 +170,7 @@ func (m *Management) getContract(ic *interop.Context, args []stackitem.Item) sta
// GetContract returns contract with given hash from given DAO. // GetContract returns contract with given hash from given DAO.
func (m *Management) GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) { func (m *Management) GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
cache := d.Store.GetROCache(m.ID).(*ManagementCache) cache := d.GetROCache(m.ID).(*ManagementCache)
cs, ok := cache.contracts[hash] cs, ok := cache.contracts[hash]
if !ok { if !ok {
return nil, storage.ErrKeyNotFound return nil, storage.ErrKeyNotFound
@ -272,7 +272,7 @@ func (m *Management) deployWithData(ic *interop.Context, args []stackitem.Item)
} }
func (m *Management) markUpdated(d *dao.Simple, hash util.Uint160, cs *state.Contract) { func (m *Management) markUpdated(d *dao.Simple, hash util.Uint160, cs *state.Contract) {
cache := d.Store.GetRWCache(m.ID).(*ManagementCache) cache := d.GetRWCache(m.ID).(*ManagementCache)
delete(cache.nep11, hash) delete(cache.nep11, hash)
delete(cache.nep17, hash) delete(cache.nep17, hash)
if cs == nil { if cs == nil {
@ -486,7 +486,7 @@ func (m *Management) OnPersist(ic *interop.Context) error {
return err return err
} }
if cache == nil { if cache == nil {
cache = ic.DAO.Store.GetRWCache(m.ID).(*ManagementCache) cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
} }
updateContractCache(cache, cs) updateContractCache(cache, cs)
} }
@ -517,7 +517,7 @@ func (m *Management) InitializeCache(d *dao.Simple) error {
if initErr != nil { if initErr != nil {
return initErr return initErr
} }
d.Store.SetCache(m.ID, cache) d.SetCache(m.ID, cache)
return nil return nil
} }
@ -530,7 +530,7 @@ func (m *Management) PostPersist(ic *interop.Context) error {
// is updated every PostPersist, so until PostPersist is called, the result for the previous block // is updated every PostPersist, so until PostPersist is called, the result for the previous block
// is returned. // is returned.
func (m *Management) GetNEP11Contracts(d *dao.Simple) []util.Uint160 { func (m *Management) GetNEP11Contracts(d *dao.Simple) []util.Uint160 {
cache := d.Store.GetROCache(m.ID).(*ManagementCache) cache := d.GetROCache(m.ID).(*ManagementCache)
result := make([]util.Uint160, 0, len(cache.nep11)) result := make([]util.Uint160, 0, len(cache.nep11))
for h := range cache.nep11 { for h := range cache.nep11 {
result = append(result, h) result = append(result, h)
@ -542,7 +542,7 @@ func (m *Management) GetNEP11Contracts(d *dao.Simple) []util.Uint160 {
// is updated every PostPersist, so until PostPersist is called, the result for the previous block // is updated every PostPersist, so until PostPersist is called, the result for the previous block
// is returned. // is returned.
func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 { func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
cache := d.Store.GetROCache(m.ID).(*ManagementCache) cache := d.GetROCache(m.ID).(*ManagementCache)
result := make([]util.Uint160, 0, len(cache.nep17)) result := make([]util.Uint160, 0, len(cache.nep17))
for h := range cache.nep17 { for h := range cache.nep17 {
result = append(result, h) result = append(result, h)
@ -560,7 +560,7 @@ func (m *Management) Initialize(ic *interop.Context) error {
nep11: make(map[util.Uint160]struct{}), nep11: make(map[util.Uint160]struct{}),
nep17: make(map[util.Uint160]struct{}), nep17: make(map[util.Uint160]struct{}),
} }
ic.DAO.Store.SetCache(m.ID, cache) ic.DAO.SetCache(m.ID, cache)
return nil return nil
} }

View file

@ -105,12 +105,12 @@ var (
) )
var ( var (
_ interop.Contract = (*NEO)(nil) _ interop.Contract = (*NEO)(nil)
_ storage.NativeContractCache = (*NeoCache)(nil) _ dao.NativeContractCache = (*NeoCache)(nil)
) )
// Copy implements NativeContractCache interface. // Copy implements NativeContractCache interface.
func (c *NeoCache) Copy() storage.NativeContractCache { func (c *NeoCache) Copy() dao.NativeContractCache {
cp := &NeoCache{} cp := &NeoCache{}
copyNeoCache(c, cp) copyNeoCache(c, cp)
return cp return cp
@ -245,7 +245,7 @@ func (n *NEO) Initialize(ic *interop.Context) error {
} }
// We need cache to be present in DAO before the subsequent call to `mint`. // We need cache to be present in DAO before the subsequent call to `mint`.
ic.DAO.Store.SetCache(n.ID, cache) ic.DAO.SetCache(n.ID, cache)
committee0 := n.standbyKeys[:n.cfg.GetCommitteeSize(ic.Block.Index)] committee0 := n.standbyKeys[:n.cfg.GetCommitteeSize(ic.Block.Index)]
cvs := toKeysWithVotes(committee0) cvs := toKeysWithVotes(committee0)
@ -297,7 +297,7 @@ func (n *NEO) InitializeCache(bc interop.Ledger, d *dao.Simple) error {
cache.gasPerBlock = n.getSortedGASRecordFromDAO(d) cache.gasPerBlock = n.getSortedGASRecordFromDAO(d)
cache.registerPrice = getIntWithKey(n.ID, d, []byte{prefixRegisterPrice}) cache.registerPrice = getIntWithKey(n.ID, d, []byte{prefixRegisterPrice})
d.Store.SetCache(n.ID, cache) d.SetCache(n.ID, cache)
return nil return nil
} }
@ -348,7 +348,7 @@ func (n *NEO) updateCommittee(cache *NeoCache, ic *interop.Context) error {
// OnPersist implements Contract interface. // OnPersist implements Contract interface.
func (n *NEO) OnPersist(ic *interop.Context) error { func (n *NEO) OnPersist(ic *interop.Context) error {
if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) { if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) {
cache := ic.DAO.Store.GetRWCache(n.ID).(*NeoCache) cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
oldKeys := cache.nextValidators oldKeys := cache.nextValidators
oldCom := cache.committee oldCom := cache.committee
if n.cfg.GetNumOfCNs(ic.Block.Index) != len(oldKeys) || if n.cfg.GetNumOfCNs(ic.Block.Index) != len(oldKeys) ||
@ -365,7 +365,7 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
// PostPersist implements Contract interface. // PostPersist implements Contract interface.
func (n *NEO) PostPersist(ic *interop.Context) error { func (n *NEO) PostPersist(ic *interop.Context) error {
gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index) gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index)
cache := ic.DAO.Store.GetRWCache(n.ID).(*NeoCache) cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
pubs := getCommitteeMembers(cache) pubs := getCommitteeMembers(cache)
committeeSize := n.cfg.GetCommitteeSize(ic.Block.Index) committeeSize := n.cfg.GetCommitteeSize(ic.Block.Index)
index := int(ic.Block.Index) % committeeSize index := int(ic.Block.Index) % committeeSize
@ -532,7 +532,7 @@ func (n *NEO) getSortedGASRecordFromDAO(d *dao.Simple) gasRecord {
// GetGASPerBlock returns gas generated for block with provided index. // GetGASPerBlock returns gas generated for block with provided index.
func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int { func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int {
cache := d.Store.GetROCache(n.ID).(*NeoCache) cache := d.GetROCache(n.ID).(*NeoCache)
gr := cache.gasPerBlock gr := cache.gasPerBlock
for i := len(gr) - 1; i >= 0; i-- { for i := len(gr) - 1; i >= 0; i-- {
if gr[i].Index <= index { if gr[i].Index <= index {
@ -545,7 +545,7 @@ func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int {
// GetCommitteeAddress returns address of the committee. // GetCommitteeAddress returns address of the committee.
func (n *NEO) GetCommitteeAddress(d *dao.Simple) util.Uint160 { func (n *NEO) GetCommitteeAddress(d *dao.Simple) util.Uint160 {
cache := d.Store.GetROCache(n.ID).(*NeoCache) cache := d.GetROCache(n.ID).(*NeoCache)
return cache.committeeHash return cache.committeeHash
} }
@ -575,7 +575,7 @@ func (n *NEO) SetGASPerBlock(ic *interop.Context, index uint32, gas *big.Int) er
return errors.New("invalid committee signature") return errors.New("invalid committee signature")
} }
n.putGASRecord(ic.DAO, index, gas) n.putGASRecord(ic.DAO, index, gas)
cache := ic.DAO.Store.GetRWCache(n.ID).(*NeoCache) cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
cache.gasPerBlock = append(cache.gasPerBlock, gasIndexPair{ cache.gasPerBlock = append(cache.gasPerBlock, gasIndexPair{
Index: index, Index: index,
GASPerBlock: *gas, GASPerBlock: *gas,
@ -588,7 +588,7 @@ func (n *NEO) getRegisterPrice(ic *interop.Context, _ []stackitem.Item) stackite
} }
func (n *NEO) getRegisterPriceInternal(d *dao.Simple) int64 { func (n *NEO) getRegisterPriceInternal(d *dao.Simple) int64 {
cache := d.Store.GetROCache(n.ID).(*NeoCache) cache := d.GetROCache(n.ID).(*NeoCache)
return cache.registerPrice return cache.registerPrice
} }
@ -602,7 +602,7 @@ func (n *NEO) setRegisterPrice(ic *interop.Context, args []stackitem.Item) stack
} }
setIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice}, price.Int64()) setIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice}, price.Int64())
cache := ic.DAO.Store.GetRWCache(n.ID).(*NeoCache) cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
cache.registerPrice = price.Int64() cache.registerPrice = price.Int64()
return stackitem.Null{} return stackitem.Null{}
} }
@ -672,7 +672,7 @@ func (n *NEO) CalculateNEOHolderReward(d *dao.Simple, value *big.Int, start, end
} else if value.Sign() < 0 { } else if value.Sign() < 0 {
return nil, errors.New("negative value") return nil, errors.New("negative value")
} }
cache := d.Store.GetROCache(n.ID).(*NeoCache) cache := d.GetROCache(n.ID).(*NeoCache)
gr := cache.gasPerBlock gr := cache.gasPerBlock
var sum, tmp big.Int var sum, tmp big.Int
for i := len(gr) - 1; i >= 0; i-- { for i := len(gr) - 1; i >= 0; i-- {
@ -743,7 +743,7 @@ func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicK
if si == nil { if si == nil {
return nil return nil
} }
cache := ic.DAO.Store.GetRWCache(n.ID).(*NeoCache) cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
cache.validators = nil cache.validators = nil
c := new(candidate).FromBytes(si) c := new(candidate).FromBytes(si)
c.Registered = false c.Registered = false
@ -823,7 +823,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.Public
// ModifyAccountVotes modifies votes of the specified account by value (can be negative). // ModifyAccountVotes modifies votes of the specified account by value (can be negative).
// typ specifies if this modify is occurring during transfer or vote (with old or new validator). // typ specifies if this modify is occurring during transfer or vote (with old or new validator).
func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d *dao.Simple, value *big.Int, isNewVote bool) error { func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d *dao.Simple, value *big.Int, isNewVote bool) error {
cache := d.Store.GetRWCache(n.ID).(*NeoCache) cache := d.GetRWCache(n.ID).(*NeoCache)
cache.votesChanged = true cache.votesChanged = true
if acc.VoteTo != nil { if acc.VoteTo != nil {
key := makeValidatorKey(acc.VoteTo) key := makeValidatorKey(acc.VoteTo)
@ -934,7 +934,7 @@ func (n *NEO) getAccountState(ic *interop.Context, args []stackitem.Item) stacki
// ComputeNextBlockValidators returns an actual list of current validators. // ComputeNextBlockValidators returns an actual list of current validators.
func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys.PublicKeys, error) { func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys.PublicKeys, error) {
numOfCNs := n.cfg.GetNumOfCNs(bc.BlockHeight() + 1) numOfCNs := n.cfg.GetNumOfCNs(bc.BlockHeight() + 1)
cache := d.Store.GetRWCache(n.ID).(*NeoCache) cache := d.GetRWCache(n.ID).(*NeoCache)
if vals := cache.validators; vals != nil && numOfCNs == len(vals) { if vals := cache.validators; vals != nil && numOfCNs == len(vals) {
return vals.Copy(), nil return vals.Copy(), nil
} }
@ -969,7 +969,7 @@ func (n *NEO) modifyVoterTurnout(d *dao.Simple, amount *big.Int) error {
// GetCommitteeMembers returns public keys of nodes in committee using cached value. // GetCommitteeMembers returns public keys of nodes in committee using cached value.
func (n *NEO) GetCommitteeMembers(d *dao.Simple) keys.PublicKeys { func (n *NEO) GetCommitteeMembers(d *dao.Simple) keys.PublicKeys {
cache := d.Store.GetROCache(n.ID).(*NeoCache) cache := d.GetROCache(n.ID).(*NeoCache)
return getCommitteeMembers(cache) return getCommitteeMembers(cache)
} }
@ -1049,7 +1049,7 @@ func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) st
// GetNextBlockValidatorsInternal returns next block validators. // GetNextBlockValidatorsInternal returns next block validators.
func (n *NEO) GetNextBlockValidatorsInternal(d *dao.Simple) keys.PublicKeys { func (n *NEO) GetNextBlockValidatorsInternal(d *dao.Simple) keys.PublicKeys {
cache := d.Store.GetROCache(n.ID).(*NeoCache) cache := d.GetROCache(n.ID).(*NeoCache)
return cache.nextValidators.Copy() return cache.nextValidators.Copy()
} }

View file

@ -53,12 +53,12 @@ var (
) )
var ( var (
_ interop.Contract = (*Notary)(nil) _ interop.Contract = (*Notary)(nil)
_ storage.NativeContractCache = (*NotaryCache)(nil) _ dao.NativeContractCache = (*NotaryCache)(nil)
) )
// Copy implements NativeContractCache interface. // Copy implements NativeContractCache interface.
func (c *NotaryCache) Copy() storage.NativeContractCache { func (c *NotaryCache) Copy() dao.NativeContractCache {
cp := &NotaryCache{} cp := &NotaryCache{}
copyNotaryCache(c, cp) copyNotaryCache(c, cp)
return cp return cp
@ -142,7 +142,7 @@ func (n *Notary) Initialize(ic *interop.Context) error {
maxNotValidBeforeDelta: defaultMaxNotValidBeforeDelta, maxNotValidBeforeDelta: defaultMaxNotValidBeforeDelta,
notaryServiceFeePerKey: defaultNotaryServiceFeePerKey, notaryServiceFeePerKey: defaultNotaryServiceFeePerKey,
} }
ic.DAO.Store.SetCache(n.ID, cache) ic.DAO.SetCache(n.ID, cache)
return nil return nil
} }
@ -152,7 +152,7 @@ func (n *Notary) InitializeCache(d *dao.Simple) error {
notaryServiceFeePerKey: getIntWithKey(n.ID, d, notaryServiceFeeKey), notaryServiceFeePerKey: getIntWithKey(n.ID, d, notaryServiceFeeKey),
} }
d.Store.SetCache(n.ID, cache) d.SetCache(n.ID, cache)
return nil return nil
} }
@ -407,7 +407,7 @@ func (n *Notary) getMaxNotValidBeforeDelta(ic *interop.Context, _ []stackitem.It
// GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method. // GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method.
func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 { func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 {
cache := dao.Store.GetROCache(n.ID).(*NotaryCache) cache := dao.GetROCache(n.ID).(*NotaryCache)
return cache.maxNotValidBeforeDelta return cache.maxNotValidBeforeDelta
} }
@ -423,7 +423,7 @@ func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem
panic("invalid committee signature") panic("invalid committee signature")
} }
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, int64(value)) setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, int64(value))
cache := ic.DAO.Store.GetRWCache(n.ID).(*NotaryCache) cache := ic.DAO.GetRWCache(n.ID).(*NotaryCache)
cache.maxNotValidBeforeDelta = value cache.maxNotValidBeforeDelta = value
return stackitem.Null{} return stackitem.Null{}
} }
@ -435,7 +435,7 @@ func (n *Notary) getNotaryServiceFeePerKey(ic *interop.Context, _ []stackitem.It
// GetNotaryServiceFeePerKey is an internal representation of Notary getNotaryServiceFeePerKey method. // GetNotaryServiceFeePerKey is an internal representation of Notary getNotaryServiceFeePerKey method.
func (n *Notary) GetNotaryServiceFeePerKey(dao *dao.Simple) int64 { func (n *Notary) GetNotaryServiceFeePerKey(dao *dao.Simple) int64 {
cache := dao.Store.GetROCache(n.ID).(*NotaryCache) cache := dao.GetROCache(n.ID).(*NotaryCache)
return cache.notaryServiceFeePerKey return cache.notaryServiceFeePerKey
} }
@ -449,7 +449,7 @@ func (n *Notary) setNotaryServiceFeePerKey(ic *interop.Context, args []stackitem
panic("invalid committee signature") panic("invalid committee signature")
} }
setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, int64(value)) setIntWithKey(n.ID, ic.DAO, notaryServiceFeeKey, int64(value))
cache := ic.DAO.Store.GetRWCache(n.ID).(*NotaryCache) cache := ic.DAO.GetRWCache(n.ID).(*NotaryCache)
cache.notaryServiceFeePerKey = value cache.notaryServiceFeePerKey = value
return stackitem.Null{} return stackitem.Null{}
} }

View file

@ -84,12 +84,12 @@ var (
) )
var ( var (
_ interop.Contract = (*Oracle)(nil) _ interop.Contract = (*Oracle)(nil)
_ storage.NativeContractCache = (*OracleCache)(nil) _ dao.NativeContractCache = (*OracleCache)(nil)
) )
// Copy implements NativeContractCache interface. // Copy implements NativeContractCache interface.
func (c *OracleCache) Copy() storage.NativeContractCache { func (c *OracleCache) Copy() dao.NativeContractCache {
cp := &OracleCache{} cp := &OracleCache{}
copyOracleCache(c, cp) copyOracleCache(c, cp)
return cp return cp
@ -235,14 +235,14 @@ func (o *Oracle) Initialize(ic *interop.Context) error {
cache := &OracleCache{ cache := &OracleCache{
requestPrice: int64(DefaultOracleRequestPrice), requestPrice: int64(DefaultOracleRequestPrice),
} }
ic.DAO.Store.SetCache(o.ID, cache) ic.DAO.SetCache(o.ID, cache)
return nil return nil
} }
func (o *Oracle) InitializeCache(d *dao.Simple) { func (o *Oracle) InitializeCache(d *dao.Simple) {
cache := &OracleCache{} cache := &OracleCache{}
cache.requestPrice = getIntWithKey(o.ID, d, prefixRequestPrice) cache.requestPrice = getIntWithKey(o.ID, d, prefixRequestPrice)
d.Store.SetCache(o.ID, cache) d.SetCache(o.ID, cache)
} }
func getResponse(tx *transaction.Transaction) *transaction.OracleResponse { func getResponse(tx *transaction.Transaction) *transaction.OracleResponse {
@ -459,7 +459,7 @@ func (o *Oracle) getPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Ite
} }
func (o *Oracle) getPriceInternal(d *dao.Simple) int64 { func (o *Oracle) getPriceInternal(d *dao.Simple) int64 {
cache := d.Store.GetROCache(o.ID).(*OracleCache) cache := d.GetROCache(o.ID).(*OracleCache)
return cache.requestPrice return cache.requestPrice
} }
@ -472,7 +472,7 @@ func (o *Oracle) setPrice(ic *interop.Context, args []stackitem.Item) stackitem.
panic("invalid committee signature") panic("invalid committee signature")
} }
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, price.Int64()) setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, price.Int64())
cache := ic.DAO.Store.GetRWCache(o.ID).(*OracleCache) cache := ic.DAO.GetRWCache(o.ID).(*OracleCache)
cache.requestPrice = price.Int64() cache.requestPrice = price.Int64()
return stackitem.Null{} return stackitem.Null{}
} }

View file

@ -63,12 +63,12 @@ type PolicyCache struct {
} }
var ( var (
_ interop.Contract = (*Policy)(nil) _ interop.Contract = (*Policy)(nil)
_ storage.NativeContractCache = (*PolicyCache)(nil) _ dao.NativeContractCache = (*PolicyCache)(nil)
) )
// Copy implements NativeContractCache interface. // Copy implements NativeContractCache interface.
func (c *PolicyCache) Copy() storage.NativeContractCache { func (c *PolicyCache) Copy() dao.NativeContractCache {
cp := &PolicyCache{} cp := &PolicyCache{}
copyPolicyCache(c, cp) copyPolicyCache(c, cp)
return cp return cp
@ -148,7 +148,7 @@ func (p *Policy) Initialize(ic *interop.Context) error {
storagePrice: DefaultStoragePrice, storagePrice: DefaultStoragePrice,
blockedAccounts: make([]util.Uint160, 0), blockedAccounts: make([]util.Uint160, 0),
} }
ic.DAO.Store.SetCache(p.ID, cache) ic.DAO.SetCache(p.ID, cache)
return nil return nil
} }
@ -159,7 +159,7 @@ func (p *Policy) InitializeCache(d *dao.Simple) error {
if err != nil { if err != nil {
return err return err
} }
d.Store.SetCache(p.ID, cache) d.SetCache(p.ID, cache)
return nil return nil
} }
@ -204,13 +204,13 @@ func (p *Policy) getFeePerByte(ic *interop.Context, _ []stackitem.Item) stackite
// GetFeePerByteInternal returns required transaction's fee per byte. // GetFeePerByteInternal returns required transaction's fee per byte.
func (p *Policy) GetFeePerByteInternal(dao *dao.Simple) int64 { func (p *Policy) GetFeePerByteInternal(dao *dao.Simple) int64 {
cache := dao.Store.GetROCache(p.ID).(*PolicyCache) cache := dao.GetROCache(p.ID).(*PolicyCache)
return cache.feePerByte return cache.feePerByte
} }
// GetMaxVerificationGas returns maximum gas allowed to be burned during verificaion. // GetMaxVerificationGas returns maximum gas allowed to be burned during verificaion.
func (p *Policy) GetMaxVerificationGas(dao *dao.Simple) int64 { func (p *Policy) GetMaxVerificationGas(dao *dao.Simple) int64 {
cache := dao.Store.GetROCache(p.ID).(*PolicyCache) cache := dao.GetROCache(p.ID).(*PolicyCache)
return cache.maxVerificationGas return cache.maxVerificationGas
} }
@ -220,7 +220,7 @@ func (p *Policy) getExecFeeFactor(ic *interop.Context, _ []stackitem.Item) stack
// GetExecFeeFactorInternal returns current execution fee factor. // GetExecFeeFactorInternal returns current execution fee factor.
func (p *Policy) GetExecFeeFactorInternal(d *dao.Simple) int64 { func (p *Policy) GetExecFeeFactorInternal(d *dao.Simple) int64 {
cache := d.Store.GetROCache(p.ID).(*PolicyCache) cache := d.GetROCache(p.ID).(*PolicyCache)
return int64(cache.execFeeFactor) return int64(cache.execFeeFactor)
} }
@ -233,7 +233,7 @@ func (p *Policy) setExecFeeFactor(ic *interop.Context, args []stackitem.Item) st
panic("invalid committee signature") panic("invalid committee signature")
} }
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, int64(value)) setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, int64(value))
cache := ic.DAO.Store.GetRWCache(p.ID).(*PolicyCache) cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
cache.execFeeFactor = value cache.execFeeFactor = value
return stackitem.Null{} return stackitem.Null{}
} }
@ -255,7 +255,7 @@ func (p *Policy) IsBlocked(dao *dao.Simple, hash util.Uint160) bool {
// of the blocked account in the blocked accounts list (or the position it should be // of the blocked account in the blocked accounts list (or the position it should be
// put at). // put at).
func (p *Policy) isBlockedInternal(dao *dao.Simple, hash util.Uint160) (int, bool) { func (p *Policy) isBlockedInternal(dao *dao.Simple, hash util.Uint160) (int, bool) {
cache := dao.Store.GetROCache(p.ID).(*PolicyCache) cache := dao.GetROCache(p.ID).(*PolicyCache)
length := len(cache.blockedAccounts) length := len(cache.blockedAccounts)
i := sort.Search(length, func(i int) bool { i := sort.Search(length, func(i int) bool {
return !cache.blockedAccounts[i].Less(hash) return !cache.blockedAccounts[i].Less(hash)
@ -272,7 +272,7 @@ func (p *Policy) getStoragePrice(ic *interop.Context, _ []stackitem.Item) stacki
// GetStoragePriceInternal returns current execution fee factor. // GetStoragePriceInternal returns current execution fee factor.
func (p *Policy) GetStoragePriceInternal(d *dao.Simple) int64 { func (p *Policy) GetStoragePriceInternal(d *dao.Simple) int64 {
cache := d.Store.GetROCache(p.ID).(*PolicyCache) cache := d.GetROCache(p.ID).(*PolicyCache)
return int64(cache.storagePrice) return int64(cache.storagePrice)
} }
@ -285,7 +285,7 @@ func (p *Policy) setStoragePrice(ic *interop.Context, args []stackitem.Item) sta
panic("invalid committee signature") panic("invalid committee signature")
} }
setIntWithKey(p.ID, ic.DAO, storagePriceKey, int64(value)) setIntWithKey(p.ID, ic.DAO, storagePriceKey, int64(value))
cache := ic.DAO.Store.GetRWCache(p.ID).(*PolicyCache) cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
cache.storagePrice = value cache.storagePrice = value
return stackitem.Null{} return stackitem.Null{}
} }
@ -300,7 +300,7 @@ func (p *Policy) setFeePerByte(ic *interop.Context, args []stackitem.Item) stack
panic("invalid committee signature") panic("invalid committee signature")
} }
setIntWithKey(p.ID, ic.DAO, feePerByteKey, value) setIntWithKey(p.ID, ic.DAO, feePerByteKey, value)
cache := ic.DAO.Store.GetRWCache(p.ID).(*PolicyCache) cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
cache.feePerByte = value cache.feePerByte = value
return stackitem.Null{} return stackitem.Null{}
} }
@ -323,7 +323,7 @@ func (p *Policy) blockAccount(ic *interop.Context, args []stackitem.Item) stacki
} }
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...) key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
ic.DAO.PutStorageItem(p.ID, key, state.StorageItem{}) ic.DAO.PutStorageItem(p.ID, key, state.StorageItem{})
cache := ic.DAO.Store.GetRWCache(p.ID).(*PolicyCache) cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
if len(cache.blockedAccounts) == i { if len(cache.blockedAccounts) == i {
cache.blockedAccounts = append(cache.blockedAccounts, hash) cache.blockedAccounts = append(cache.blockedAccounts, hash)
} else { } else {
@ -346,7 +346,7 @@ func (p *Policy) unblockAccount(ic *interop.Context, args []stackitem.Item) stac
} }
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...) key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
ic.DAO.DeleteStorageItem(p.ID, key) ic.DAO.DeleteStorageItem(p.ID, key)
cache := ic.DAO.Store.GetRWCache(p.ID).(*PolicyCache) cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
cache.blockedAccounts = append(cache.blockedAccounts[:i], cache.blockedAccounts[i+1:]...) cache.blockedAccounts = append(cache.blockedAccounts[:i], cache.blockedAccounts[i+1:]...)
return stackitem.NewBool(true) return stackitem.NewBool(true)
} }

View file

@ -15,9 +15,6 @@ import (
type MemCachedStore struct { type MemCachedStore struct {
MemoryStore MemoryStore
nativeCacheLock sync.RWMutex
nativeCache map[int32]NativeContractCache
private bool private bool
// plock protects Persist from double entrance. // plock protects Persist from double entrance.
plock sync.Mutex plock sync.Mutex
@ -25,15 +22,6 @@ type MemCachedStore struct {
ps Store ps Store
} }
// NativeContractCache is an interface representing cache for a native contract.
// Cache can be copied to create a wrapper around current DAO layer. Wrapped cache
// can be persisted to the underlying DAO native cache.
type NativeContractCache interface {
// Copy returns a copy of native cache item that can safely be changed within
// the subsequent DAO operations.
Copy() NativeContractCache
}
type ( type (
// KeyValue represents key-value pair. // KeyValue represents key-value pair.
KeyValue struct { KeyValue struct {
@ -58,12 +46,8 @@ type (
// NewMemCachedStore creates a new MemCachedStore object. // NewMemCachedStore creates a new MemCachedStore object.
func NewMemCachedStore(lower Store) *MemCachedStore { func NewMemCachedStore(lower Store) *MemCachedStore {
// Do not copy cache from ps; instead should create clear map: GetRWCache and
// GetROCache will retrieve cache from the underlying nativeCache if requested.
cache := make(map[int32]NativeContractCache)
return &MemCachedStore{ return &MemCachedStore{
MemoryStore: *NewMemoryStore(), MemoryStore: *NewMemoryStore(),
nativeCache: cache,
ps: lower, ps: lower,
} }
} }
@ -71,14 +55,8 @@ func NewMemCachedStore(lower Store) *MemCachedStore {
// NewPrivateMemCachedStore creates a new private (unlocked) MemCachedStore object. // NewPrivateMemCachedStore creates a new private (unlocked) MemCachedStore object.
// Private cached stores are closed after Persist. // Private cached stores are closed after Persist.
func NewPrivateMemCachedStore(lower Store) *MemCachedStore { func NewPrivateMemCachedStore(lower Store) *MemCachedStore {
// Do not copy cache from ps; instead should create clear map: GetRWCache and
// GetROCache will retrieve cache from the underlying nativeCache if requested.
// The lowest underlying store MUST have its native cache initialized, otherwise
// GetROCache and GetRWCache won't work properly.
cache := make(map[int32]NativeContractCache)
return &MemCachedStore{ return &MemCachedStore{
MemoryStore: *NewMemoryStore(), MemoryStore: *NewMemoryStore(),
nativeCache: cache,
private: true, private: true,
ps: lower, ps: lower,
} }
@ -363,23 +341,15 @@ func (s *MemCachedStore) persist(isSync bool) (int, error) {
} }
s.mem = nil s.mem = nil
s.stor = nil s.stor = nil
if cached, ok := s.ps.(*MemCachedStore); ok {
for id, nativeCache := range s.nativeCache {
cached.nativeCache[id] = nativeCache
}
s.nativeCache = nil
}
return keys, nil return keys, nil
} }
s.plock.Lock() s.plock.Lock()
defer s.plock.Unlock() defer s.plock.Unlock()
s.mut.Lock() s.mut.Lock()
s.nativeCacheLock.Lock()
keys = len(s.mem) + len(s.stor) keys = len(s.mem) + len(s.stor)
if keys == 0 { if keys == 0 {
s.nativeCacheLock.Unlock()
s.mut.Unlock() s.mut.Unlock()
return 0, nil return 0, nil
} }
@ -388,30 +358,17 @@ func (s *MemCachedStore) persist(isSync bool) (int, error) {
// starts using fresh new maps. This tempstore is only known here and // starts using fresh new maps. This tempstore is only known here and
// nothing ever changes it, therefore accesses to it (reads) can go // nothing ever changes it, therefore accesses to it (reads) can go
// unprotected while writes are handled by s proper. // unprotected while writes are handled by s proper.
var tempstore = &MemCachedStore{MemoryStore: MemoryStore{mem: s.mem, stor: s.stor}, ps: s.ps, nativeCache: s.nativeCache} var tempstore = &MemCachedStore{MemoryStore: MemoryStore{mem: s.mem, stor: s.stor}, ps: s.ps}
s.ps = tempstore s.ps = tempstore
s.mem = make(map[string][]byte, len(s.mem)) s.mem = make(map[string][]byte, len(s.mem))
s.stor = make(map[string][]byte, len(s.stor)) s.stor = make(map[string][]byte, len(s.stor))
cached, isPSCached := tempstore.ps.(*MemCachedStore)
if isPSCached {
s.nativeCache = make(map[int32]NativeContractCache)
}
if !isSync { if !isSync {
s.nativeCacheLock.Unlock()
s.mut.Unlock() s.mut.Unlock()
} }
if isPSCached {
cached.nativeCacheLock.Lock()
for id, nativeCache := range tempstore.nativeCache {
cached.nativeCache[id] = nativeCache
}
cached.nativeCacheLock.Unlock()
}
err = tempstore.ps.PutChangeSet(tempstore.mem, tempstore.stor) err = tempstore.ps.PutChangeSet(tempstore.mem, tempstore.stor)
if !isSync { if !isSync {
s.mut.Lock() s.mut.Lock()
s.nativeCacheLock.Lock()
} }
if err == nil { if err == nil {
// tempstore.mem and tempstore.del are completely flushed now // tempstore.mem and tempstore.del are completely flushed now
@ -427,69 +384,14 @@ func (s *MemCachedStore) persist(isSync bool) (int, error) {
for k := range s.stor { for k := range s.stor {
put(tempstore.stor, k, s.stor[k]) put(tempstore.stor, k, s.stor[k])
} }
if isPSCached {
for id, nativeCache := range s.nativeCache {
tempstore.nativeCache[id] = nativeCache
}
s.nativeCache = tempstore.nativeCache
}
s.ps = tempstore.ps s.ps = tempstore.ps
s.mem = tempstore.mem s.mem = tempstore.mem
s.stor = tempstore.stor s.stor = tempstore.stor
} }
s.nativeCacheLock.Unlock()
s.mut.Unlock() s.mut.Unlock()
return keys, err return keys, err
} }
// GetROCache returns native contact cache. The cache CAN NOT be modified by
// the caller. It's the caller's duty to keep it unmodified.
func (s *MemCachedStore) GetROCache(id int32) NativeContractCache {
s.nativeCacheLock.RLock()
defer s.nativeCacheLock.RUnlock()
return s.getCache(id, true)
}
// GetRWCache returns native contact cache. The cache CAN BE safely modified
// by the caller.
func (s *MemCachedStore) GetRWCache(k int32) NativeContractCache {
s.nativeCacheLock.Lock()
defer s.nativeCacheLock.Unlock()
return s.getCache(k, false)
}
func (s *MemCachedStore) getCache(k int32, ro bool) NativeContractCache {
if itm, ok := s.nativeCache[k]; ok {
// Don't need to create itm copy, because its value was already copied
// the first time it was retrieved from loser ps.
return itm
}
if cached, ok := s.ps.(*MemCachedStore); ok {
if ro {
return cached.GetROCache(k)
}
v := cached.GetRWCache(k)
if v != nil {
// Create a copy here in order not to modify the existing cache.
cp := v.Copy()
s.nativeCache[k] = cp
return cp
}
}
return nil
}
func (s *MemCachedStore) SetCache(k int32, v NativeContractCache) {
s.nativeCacheLock.Lock()
defer s.nativeCacheLock.Unlock()
s.nativeCache[k] = v
}
// Close implements Store interface, clears up memory and closes the lower layer // Close implements Store interface, clears up memory and closes the lower layer
// Store. // Store.
func (s *MemCachedStore) Close() error { func (s *MemCachedStore) Close() error {