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"
"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
}

View file

@ -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
}

View file

@ -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)

View file

@ -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
}

View file

@ -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()
}

View file

@ -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{}
}

View file

@ -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{}
}

View file

@ -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)
}

View file

@ -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 {