From 8d2d48f36090ad29350b898acf4bef1f2653b81a Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 20 Apr 2022 17:47:48 +0300 Subject: [PATCH] core: move native cache from MemCachedStore to DAO --- pkg/core/dao/dao.go | 112 ++++++++++++++++++++++++++- pkg/core/interop_system_core_test.go | 3 +- pkg/core/native/designate.go | 18 ++--- pkg/core/native/management.go | 20 ++--- pkg/core/native/native_neo.go | 36 ++++----- pkg/core/native/notary.go | 18 ++--- pkg/core/native/oracle.go | 14 ++-- pkg/core/native/policy.go | 30 +++---- pkg/core/storage/memcached_store.go | 100 +----------------------- 9 files changed, 181 insertions(+), 170 deletions(-) diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index b178d2aaf..db9bc0807 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" iocore "io" + "sync" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/state" @@ -34,11 +35,28 @@ var ( type Simple struct { Version Version 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 keyBuf []byte 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. func NewSimple(backend storage.Store, stateRootInHeader bool, p2pSigExtensions bool) *Simple { st := storage.NewMemCachedStore(backend) @@ -52,7 +70,8 @@ func newSimple(st *storage.MemCachedStore, stateRootInHeader bool, p2pSigExtensi StateRootInHeader: stateRootInHeader, 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 { d := NewSimple(dao.Store, dao.Version.StateRootInHeader, dao.Version.P2PSigExtensions) d.Version = dao.Version + d.nativeCachePS = dao return d } @@ -76,6 +96,13 @@ func (dao *Simple) GetPrivate() *Simple { *d = *dao // Inherit everything... d.Store = storage.NewPrivateMemCachedStore(dao.Store) // except storage, wrap another layer. 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 } @@ -809,6 +836,17 @@ func (dao *Simple) getDataBuf() *io.BufBinWriter { // Persist flushes all the changes made into the (supposedly) persistent // underlying store. It doesn't block accesses to DAO from other threads. 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() } @@ -816,5 +854,77 @@ func (dao *Simple) Persist() (int, error) { // underlying store. It's a synchronous version of Persist that doesn't allow // other threads to work with DAO while flushing the Store. 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() } + +// 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 +} diff --git a/pkg/core/interop_system_core_test.go b/pkg/core/interop_system_core_test.go index 2407b5e46..2c7b34307 100644 --- a/pkg/core/interop_system_core_test.go +++ b/pkg/core/interop_system_core_test.go @@ -12,7 +12,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config" "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/dao" "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/iterator" @@ -533,7 +532,7 @@ func TestStorageFind(t *testing.T) { func createVM(t testing.TB) (*vm.VM, *interop.Context, *Blockchain) { chain := newTestChain(t) 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() return v, context, chain } diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go index 6282c3384..809395c78 100644 --- a/pkg/core/native/designate.go +++ b/pkg/core/native/designate.go @@ -79,12 +79,12 @@ var ( ) var ( - _ interop.Contract = (*Designate)(nil) - _ storage.NativeContractCache = (*DesignationCache)(nil) + _ interop.Contract = (*Designate)(nil) + _ dao.NativeContractCache = (*DesignationCache)(nil) ) // Copy implements NativeContractCache interface. -func (c *DesignationCache) Copy() storage.NativeContractCache { +func (c *DesignationCache) Copy() dao.NativeContractCache { cp := &DesignationCache{} copyDesignationCache(c, cp) return cp @@ -128,7 +128,7 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate { // data in the storage. func (s *Designate) Initialize(ic *interop.Context) error { cache := &DesignationCache{} - ic.DAO.Store.SetCache(s.ID, cache) + ic.DAO.SetCache(s.ID, cache) 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) } } - d.Store.SetCache(s.ID, cache) + d.SetCache(s.ID, cache) return nil } @@ -157,7 +157,7 @@ func (s *Designate) OnPersist(ic *interop.Context) error { // PostPersist implements Contract interface. 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 { return nil } @@ -273,7 +273,7 @@ func (s *Designate) GetLastDesignatedHash(d *dao.Simple, r noderoles.Role) (util if !s.isValidRole(r) { return util.Uint160{}, ErrInvalidRole } - cache := d.Store.GetROCache(s.ID).(*DesignationCache) + cache := d.GetROCache(s.ID).(*DesignationCache) if val := getCachedRoleData(cache, r); val != 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) { 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.height <= index { return val.nodes.Copy(), val.height, nil @@ -380,7 +380,7 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs return err } - cache := ic.DAO.Store.GetRWCache(s.ID).(*DesignationCache) + cache := ic.DAO.GetRWCache(s.ID).(*DesignationCache) err = s.updateCachedRoleData(cache, ic.DAO, r) if err != nil { return fmt.Errorf("failed to update Designation role data cache: %w", err) diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 2643c4f03..6ed98cd97 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -58,12 +58,12 @@ var ( ) var ( - _ interop.Contract = (*Management)(nil) - _ storage.NativeContractCache = (*ManagementCache)(nil) + _ interop.Contract = (*Management)(nil) + _ dao.NativeContractCache = (*ManagementCache)(nil) ) // Copy implements NativeContractCache interface. -func (c *ManagementCache) Copy() storage.NativeContractCache { +func (c *ManagementCache) Copy() dao.NativeContractCache { cp := &ManagementCache{ contracts: make(map[util.Uint160]*state.Contract), 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. 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] if !ok { 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) { - cache := d.Store.GetRWCache(m.ID).(*ManagementCache) + cache := d.GetRWCache(m.ID).(*ManagementCache) delete(cache.nep11, hash) delete(cache.nep17, hash) if cs == nil { @@ -486,7 +486,7 @@ func (m *Management) OnPersist(ic *interop.Context) error { return err } if cache == nil { - cache = ic.DAO.Store.GetRWCache(m.ID).(*ManagementCache) + cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache) } updateContractCache(cache, cs) } @@ -517,7 +517,7 @@ func (m *Management) InitializeCache(d *dao.Simple) error { if initErr != nil { return initErr } - d.Store.SetCache(m.ID, cache) + d.SetCache(m.ID, cache) 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 returned. 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)) for h := range cache.nep11 { 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 returned. 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)) for h := range cache.nep17 { result = append(result, h) @@ -560,7 +560,7 @@ func (m *Management) Initialize(ic *interop.Context) error { nep11: 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 } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index b9dedc49f..f99aa2f07 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -105,12 +105,12 @@ var ( ) var ( - _ interop.Contract = (*NEO)(nil) - _ storage.NativeContractCache = (*NeoCache)(nil) + _ interop.Contract = (*NEO)(nil) + _ dao.NativeContractCache = (*NeoCache)(nil) ) // Copy implements NativeContractCache interface. -func (c *NeoCache) Copy() storage.NativeContractCache { +func (c *NeoCache) Copy() dao.NativeContractCache { cp := &NeoCache{} copyNeoCache(c, 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`. - ic.DAO.Store.SetCache(n.ID, cache) + ic.DAO.SetCache(n.ID, cache) committee0 := n.standbyKeys[:n.cfg.GetCommitteeSize(ic.Block.Index)] cvs := toKeysWithVotes(committee0) @@ -297,7 +297,7 @@ func (n *NEO) InitializeCache(bc interop.Ledger, d *dao.Simple) error { cache.gasPerBlock = n.getSortedGASRecordFromDAO(d) cache.registerPrice = getIntWithKey(n.ID, d, []byte{prefixRegisterPrice}) - d.Store.SetCache(n.ID, cache) + d.SetCache(n.ID, cache) return nil } @@ -348,7 +348,7 @@ func (n *NEO) updateCommittee(cache *NeoCache, ic *interop.Context) error { // OnPersist implements Contract interface. func (n *NEO) OnPersist(ic *interop.Context) error { 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 oldCom := cache.committee 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. func (n *NEO) PostPersist(ic *interop.Context) error { 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) committeeSize := n.cfg.GetCommitteeSize(ic.Block.Index) 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. 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 for i := len(gr) - 1; i >= 0; i-- { 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. 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 } @@ -575,7 +575,7 @@ func (n *NEO) SetGASPerBlock(ic *interop.Context, index uint32, gas *big.Int) er return errors.New("invalid committee signature") } 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{ Index: index, GASPerBlock: *gas, @@ -588,7 +588,7 @@ func (n *NEO) getRegisterPrice(ic *interop.Context, _ []stackitem.Item) stackite } func (n *NEO) getRegisterPriceInternal(d *dao.Simple) int64 { - cache := d.Store.GetROCache(n.ID).(*NeoCache) + cache := d.GetROCache(n.ID).(*NeoCache) 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()) - cache := ic.DAO.Store.GetRWCache(n.ID).(*NeoCache) + cache := ic.DAO.GetRWCache(n.ID).(*NeoCache) cache.registerPrice = price.Int64() return stackitem.Null{} } @@ -672,7 +672,7 @@ func (n *NEO) CalculateNEOHolderReward(d *dao.Simple, value *big.Int, start, end } else if value.Sign() < 0 { return nil, errors.New("negative value") } - cache := d.Store.GetROCache(n.ID).(*NeoCache) + cache := d.GetROCache(n.ID).(*NeoCache) gr := cache.gasPerBlock var sum, tmp big.Int 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 { return nil } - cache := ic.DAO.Store.GetRWCache(n.ID).(*NeoCache) + cache := ic.DAO.GetRWCache(n.ID).(*NeoCache) cache.validators = nil c := new(candidate).FromBytes(si) 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). // 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 { - cache := d.Store.GetRWCache(n.ID).(*NeoCache) + cache := d.GetRWCache(n.ID).(*NeoCache) cache.votesChanged = true if acc.VoteTo != nil { 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. func (n *NEO) ComputeNextBlockValidators(bc interop.Ledger, d *dao.Simple) (keys.PublicKeys, error) { 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) { 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. 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) } @@ -1049,7 +1049,7 @@ func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) st // GetNextBlockValidatorsInternal returns next block validators. 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() } diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go index 79cd648c7..fadf8a372 100644 --- a/pkg/core/native/notary.go +++ b/pkg/core/native/notary.go @@ -53,12 +53,12 @@ var ( ) var ( - _ interop.Contract = (*Notary)(nil) - _ storage.NativeContractCache = (*NotaryCache)(nil) + _ interop.Contract = (*Notary)(nil) + _ dao.NativeContractCache = (*NotaryCache)(nil) ) // Copy implements NativeContractCache interface. -func (c *NotaryCache) Copy() storage.NativeContractCache { +func (c *NotaryCache) Copy() dao.NativeContractCache { cp := &NotaryCache{} copyNotaryCache(c, cp) return cp @@ -142,7 +142,7 @@ func (n *Notary) Initialize(ic *interop.Context) error { maxNotValidBeforeDelta: defaultMaxNotValidBeforeDelta, notaryServiceFeePerKey: defaultNotaryServiceFeePerKey, } - ic.DAO.Store.SetCache(n.ID, cache) + ic.DAO.SetCache(n.ID, cache) return nil } @@ -152,7 +152,7 @@ func (n *Notary) InitializeCache(d *dao.Simple) error { notaryServiceFeePerKey: getIntWithKey(n.ID, d, notaryServiceFeeKey), } - d.Store.SetCache(n.ID, cache) + d.SetCache(n.ID, cache) return nil } @@ -407,7 +407,7 @@ func (n *Notary) getMaxNotValidBeforeDelta(ic *interop.Context, _ []stackitem.It // GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method. func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 { - cache := dao.Store.GetROCache(n.ID).(*NotaryCache) + cache := dao.GetROCache(n.ID).(*NotaryCache) return cache.maxNotValidBeforeDelta } @@ -423,7 +423,7 @@ func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem panic("invalid committee signature") } 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 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. func (n *Notary) GetNotaryServiceFeePerKey(dao *dao.Simple) int64 { - cache := dao.Store.GetROCache(n.ID).(*NotaryCache) + cache := dao.GetROCache(n.ID).(*NotaryCache) return cache.notaryServiceFeePerKey } @@ -449,7 +449,7 @@ func (n *Notary) setNotaryServiceFeePerKey(ic *interop.Context, args []stackitem panic("invalid committee signature") } 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 return stackitem.Null{} } diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index 25273f60f..7b3914b5c 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -84,12 +84,12 @@ var ( ) var ( - _ interop.Contract = (*Oracle)(nil) - _ storage.NativeContractCache = (*OracleCache)(nil) + _ interop.Contract = (*Oracle)(nil) + _ dao.NativeContractCache = (*OracleCache)(nil) ) // Copy implements NativeContractCache interface. -func (c *OracleCache) Copy() storage.NativeContractCache { +func (c *OracleCache) Copy() dao.NativeContractCache { cp := &OracleCache{} copyOracleCache(c, cp) return cp @@ -235,14 +235,14 @@ func (o *Oracle) Initialize(ic *interop.Context) error { cache := &OracleCache{ requestPrice: int64(DefaultOracleRequestPrice), } - ic.DAO.Store.SetCache(o.ID, cache) + ic.DAO.SetCache(o.ID, cache) return nil } func (o *Oracle) InitializeCache(d *dao.Simple) { cache := &OracleCache{} 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 { @@ -459,7 +459,7 @@ func (o *Oracle) getPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Ite } func (o *Oracle) getPriceInternal(d *dao.Simple) int64 { - cache := d.Store.GetROCache(o.ID).(*OracleCache) + cache := d.GetROCache(o.ID).(*OracleCache) return cache.requestPrice } @@ -472,7 +472,7 @@ func (o *Oracle) setPrice(ic *interop.Context, args []stackitem.Item) stackitem. panic("invalid committee signature") } 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() return stackitem.Null{} } diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index b4a7895ab..9dd40876d 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -63,12 +63,12 @@ type PolicyCache struct { } var ( - _ interop.Contract = (*Policy)(nil) - _ storage.NativeContractCache = (*PolicyCache)(nil) + _ interop.Contract = (*Policy)(nil) + _ dao.NativeContractCache = (*PolicyCache)(nil) ) // Copy implements NativeContractCache interface. -func (c *PolicyCache) Copy() storage.NativeContractCache { +func (c *PolicyCache) Copy() dao.NativeContractCache { cp := &PolicyCache{} copyPolicyCache(c, cp) return cp @@ -148,7 +148,7 @@ func (p *Policy) Initialize(ic *interop.Context) error { storagePrice: DefaultStoragePrice, blockedAccounts: make([]util.Uint160, 0), } - ic.DAO.Store.SetCache(p.ID, cache) + ic.DAO.SetCache(p.ID, cache) return nil } @@ -159,7 +159,7 @@ func (p *Policy) InitializeCache(d *dao.Simple) error { if err != nil { return err } - d.Store.SetCache(p.ID, cache) + d.SetCache(p.ID, cache) return nil } @@ -204,13 +204,13 @@ func (p *Policy) getFeePerByte(ic *interop.Context, _ []stackitem.Item) stackite // GetFeePerByteInternal returns required transaction's fee per byte. func (p *Policy) GetFeePerByteInternal(dao *dao.Simple) int64 { - cache := dao.Store.GetROCache(p.ID).(*PolicyCache) + cache := dao.GetROCache(p.ID).(*PolicyCache) return cache.feePerByte } // GetMaxVerificationGas returns maximum gas allowed to be burned during verificaion. func (p *Policy) GetMaxVerificationGas(dao *dao.Simple) int64 { - cache := dao.Store.GetROCache(p.ID).(*PolicyCache) + cache := dao.GetROCache(p.ID).(*PolicyCache) return cache.maxVerificationGas } @@ -220,7 +220,7 @@ func (p *Policy) getExecFeeFactor(ic *interop.Context, _ []stackitem.Item) stack // GetExecFeeFactorInternal returns current execution fee factor. 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) } @@ -233,7 +233,7 @@ func (p *Policy) setExecFeeFactor(ic *interop.Context, args []stackitem.Item) st panic("invalid committee signature") } 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 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 // put at). 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) i := sort.Search(length, func(i int) bool { 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. 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) } @@ -285,7 +285,7 @@ func (p *Policy) setStoragePrice(ic *interop.Context, args []stackitem.Item) sta panic("invalid committee signature") } 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 return stackitem.Null{} } @@ -300,7 +300,7 @@ func (p *Policy) setFeePerByte(ic *interop.Context, args []stackitem.Item) stack panic("invalid committee signature") } 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 return stackitem.Null{} } @@ -323,7 +323,7 @@ func (p *Policy) blockAccount(ic *interop.Context, args []stackitem.Item) stacki } key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...) 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 { cache.blockedAccounts = append(cache.blockedAccounts, hash) } else { @@ -346,7 +346,7 @@ func (p *Policy) unblockAccount(ic *interop.Context, args []stackitem.Item) stac } key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...) 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:]...) return stackitem.NewBool(true) } diff --git a/pkg/core/storage/memcached_store.go b/pkg/core/storage/memcached_store.go index 42fdccf69..10461545e 100644 --- a/pkg/core/storage/memcached_store.go +++ b/pkg/core/storage/memcached_store.go @@ -15,9 +15,6 @@ import ( type MemCachedStore struct { MemoryStore - nativeCacheLock sync.RWMutex - nativeCache map[int32]NativeContractCache - private bool // plock protects Persist from double entrance. plock sync.Mutex @@ -25,15 +22,6 @@ type MemCachedStore struct { 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 ( // KeyValue represents key-value pair. KeyValue struct { @@ -58,12 +46,8 @@ type ( // NewMemCachedStore creates a new MemCachedStore object. 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{ MemoryStore: *NewMemoryStore(), - nativeCache: cache, ps: lower, } } @@ -71,14 +55,8 @@ func NewMemCachedStore(lower Store) *MemCachedStore { // NewPrivateMemCachedStore creates a new private (unlocked) MemCachedStore object. // Private cached stores are closed after Persist. 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{ MemoryStore: *NewMemoryStore(), - nativeCache: cache, private: true, ps: lower, } @@ -363,23 +341,15 @@ func (s *MemCachedStore) persist(isSync bool) (int, error) { } s.mem = 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 } s.plock.Lock() defer s.plock.Unlock() s.mut.Lock() - s.nativeCacheLock.Lock() keys = len(s.mem) + len(s.stor) if keys == 0 { - s.nativeCacheLock.Unlock() s.mut.Unlock() 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 // nothing ever changes it, therefore accesses to it (reads) can go // 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.mem = make(map[string][]byte, len(s.mem)) s.stor = make(map[string][]byte, len(s.stor)) - cached, isPSCached := tempstore.ps.(*MemCachedStore) - if isPSCached { - s.nativeCache = make(map[int32]NativeContractCache) - } if !isSync { - s.nativeCacheLock.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) if !isSync { s.mut.Lock() - s.nativeCacheLock.Lock() } if err == nil { // 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 { 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.mem = tempstore.mem s.stor = tempstore.stor } - s.nativeCacheLock.Unlock() s.mut.Unlock() 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 // Store. func (s *MemCachedStore) Close() error {