core: add cache to native Policy contract

We can cache maxBlockSize, maxTransactionsPerBlock and feePerByte
in order to reduce the number of storage requests.
This commit is contained in:
Anna Shaleva 2020-06-18 21:36:20 +03:00
parent 08cc04c3d5
commit c2735a4569
2 changed files with 60 additions and 0 deletions

View file

@ -640,6 +640,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
bc.lock.Unlock() bc.lock.Unlock()
return err return err
} }
bc.contracts.Policy.OnPersistEnd(bc.dao)
bc.topBlock.Store(block) bc.topBlock.Store(block)
atomic.StoreUint32(&bc.blockHeight, block.Index) atomic.StoreUint32(&bc.blockHeight, block.Index)
bc.memPool.RemoveStale(bc.isTxStillRelevant, bc) bc.memPool.RemoveStale(bc.isTxStillRelevant, bc)

View file

@ -4,6 +4,7 @@ import (
"encoding/binary" "encoding/binary"
"math/big" "math/big"
"sort" "sort"
"sync"
"github.com/nspcc-dev/neo-go/pkg/core/dao" "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"
@ -42,6 +43,14 @@ var (
// Policy represents Policy native contract. // Policy represents Policy native contract.
type Policy struct { type Policy struct {
interop.ContractMD interop.ContractMD
lock sync.RWMutex
// isValid defies whether cached values were changed during the current
// consensus iteration. If false, these values will be updated after
// blockchain DAO persisting. If true, we can safely use cached values.
isValid bool
maxTransactionsPerBlock uint32
maxBlockSize uint32
feePerByte int64
} }
var _ interop.Contract = (*Policy)(nil) var _ interop.Contract = (*Policy)(nil)
@ -135,6 +144,12 @@ func (p *Policy) Initialize(ic *interop.Context) error {
if err != nil { if err != nil {
return err return err
} }
p.isValid = true
p.maxTransactionsPerBlock = defaultMaxTransactionsPerBlock
p.maxBlockSize = defaultMaxBlockSize
p.feePerByte = defaultFeePerByte
return nil return nil
} }
@ -143,6 +158,26 @@ func (p *Policy) OnPersist(ic *interop.Context) error {
return nil return nil
} }
// OnPersistEnd updates cached Policy values if they've been changed
func (p *Policy) OnPersistEnd(dao dao.DAO) {
if p.isValid {
return
}
p.lock.Lock()
defer p.lock.Unlock()
maxTxPerBlock := p.getUint32WithKey(dao, maxTransactionsPerBlockKey)
p.maxTransactionsPerBlock = maxTxPerBlock
maxBlockSize := p.getUint32WithKey(dao, maxBlockSizeKey)
p.maxBlockSize = maxBlockSize
feePerByte := p.getInt64WithKey(dao, feePerByteKey)
p.feePerByte = feePerByte
p.isValid = true
}
// getMaxTransactionsPerBlock is Policy contract method and returns the upper // getMaxTransactionsPerBlock is Policy contract method and returns the upper
// limit of transactions per block. // limit of transactions per block.
func (p *Policy) getMaxTransactionsPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.Item { func (p *Policy) getMaxTransactionsPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
@ -152,11 +187,21 @@ func (p *Policy) getMaxTransactionsPerBlock(ic *interop.Context, _ []stackitem.I
// GetMaxTransactionsPerBlockInternal returns the upper limit of transactions per // GetMaxTransactionsPerBlockInternal returns the upper limit of transactions per
// block. // block.
func (p *Policy) GetMaxTransactionsPerBlockInternal(dao dao.DAO) uint32 { func (p *Policy) GetMaxTransactionsPerBlockInternal(dao dao.DAO) uint32 {
p.lock.RLock()
defer p.lock.RUnlock()
if p.isValid {
return p.maxTransactionsPerBlock
}
return p.getUint32WithKey(dao, maxTransactionsPerBlockKey) return p.getUint32WithKey(dao, maxTransactionsPerBlockKey)
} }
// getMaxBlockSize is Policy contract method and returns maximum block size. // getMaxBlockSize is Policy contract method and returns maximum block size.
func (p *Policy) getMaxBlockSize(ic *interop.Context, _ []stackitem.Item) stackitem.Item { func (p *Policy) getMaxBlockSize(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
p.lock.RLock()
defer p.lock.RUnlock()
if p.isValid {
return stackitem.NewBigInteger(big.NewInt(int64(p.maxBlockSize)))
}
return stackitem.NewBigInteger(big.NewInt(int64(p.getUint32WithKey(ic.DAO, maxBlockSizeKey)))) return stackitem.NewBigInteger(big.NewInt(int64(p.getUint32WithKey(ic.DAO, maxBlockSizeKey))))
} }
@ -168,6 +213,11 @@ 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.DAO) int64 { func (p *Policy) GetFeePerByteInternal(dao dao.DAO) int64 {
p.lock.RLock()
defer p.lock.RUnlock()
if p.isValid {
return p.feePerByte
}
return p.getInt64WithKey(dao, feePerByteKey) return p.getInt64WithKey(dao, feePerByteKey)
} }
@ -205,10 +255,13 @@ func (p *Policy) setMaxTransactionsPerBlock(ic *interop.Context, args []stackite
return stackitem.NewBool(false) return stackitem.NewBool(false)
} }
value := uint32(toBigInt(args[0]).Int64()) value := uint32(toBigInt(args[0]).Int64())
p.lock.Lock()
defer p.lock.Unlock()
err = p.setUint32WithKey(ic.DAO, maxTransactionsPerBlockKey, value) err = p.setUint32WithKey(ic.DAO, maxTransactionsPerBlockKey, value)
if err != nil { if err != nil {
panic(err) panic(err)
} }
p.isValid = false
return stackitem.NewBool(true) return stackitem.NewBool(true)
} }
@ -225,10 +278,13 @@ func (p *Policy) setMaxBlockSize(ic *interop.Context, args []stackitem.Item) sta
if payload.MaxSize <= value { if payload.MaxSize <= value {
return stackitem.NewBool(false) return stackitem.NewBool(false)
} }
p.lock.Lock()
defer p.lock.Unlock()
err = p.setUint32WithKey(ic.DAO, maxBlockSizeKey, value) err = p.setUint32WithKey(ic.DAO, maxBlockSizeKey, value)
if err != nil { if err != nil {
panic(err) panic(err)
} }
p.isValid = false
return stackitem.NewBool(true) return stackitem.NewBool(true)
} }
@ -242,10 +298,13 @@ func (p *Policy) setFeePerByte(ic *interop.Context, args []stackitem.Item) stack
return stackitem.NewBool(false) return stackitem.NewBool(false)
} }
value := toBigInt(args[0]).Int64() value := toBigInt(args[0]).Int64()
p.lock.Lock()
defer p.lock.Unlock()
err = p.setInt64WithKey(ic.DAO, feePerByteKey, value) err = p.setInt64WithKey(ic.DAO, feePerByteKey, value)
if err != nil { if err != nil {
panic(err) panic(err)
} }
p.isValid = false
return stackitem.NewBool(true) return stackitem.NewBool(true)
} }