erigon-pulse/eth/mgr/mgr.go
Alex Sharov daa359c363
Mgr schedule iterator (#566)
* db based version of PrefixByCumulativeWitnessSize

* db based version of PrefixByCumulativeWitnessSize

* retain all in Trie by default

* fix WitnessLen logic in calcTrie roots

* Rename IntermediateTrieWitnessLenBucket to IntermediateWitnessLenBucket

* handle corner cases in WL

* Use correct incarnation for IH bucket

* use name WitnessSize

* save progress towards db-only witness estimation

* results from trie and from db are still different

* less recursion

* correct incarnation in CumulativeSearch

* reuse results from previous Tick, separate concepts of parent and startKey

* experiment: if not including trie structure to WitnessSize will reduce cumulative error

* tool to generate all IH and tool to calculate assessment of cumulative error

* tool to generate all IH

* Calculate totalWitnessSize based on DB data - then schedule will not overrun state during MGR cycle

* better stats

* Calculate totalWitnessSize based on DB data - then schedule will not overrun state during MGR cycle

* Calculate totalWitnessSize based on DB data - then schedule will not overrun state during MGR cycle

* calculate ticks size distribution

* estimate cumulative error

* fix linter

* resetIH from scratch if needed

* cleanup

* fix test

* fix test
2020-05-28 12:33:05 +01:00

99 lines
2.4 KiB
Go

package mgr
import (
"fmt"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
)
const (
TicksPerCycle uint64 = 256
BlocksPerTick uint64 = 20
BlocksPerCycle uint64 = BlocksPerTick * TicksPerCycle
BytesPerWitness uint64 = 1024 * 1024
)
type Tick struct {
Number uint64
FromSize uint64
ToSize uint64
From []byte
To []byte
FromBlock uint64
ToBlock uint64
}
func (t Tick) String() string {
return fmt.Sprintf("Tick{%d,Blocks:%d-%d,Sizes:%d-%d,Prefixes:%x-%x}", t.Number, t.FromBlock, t.ToBlock, t.FromSize, t.ToSize, t.From, t.To)
}
func (t Tick) IsLastInCycle() bool {
return t.Number == TicksPerCycle-1
}
// NewTick constructor building Tick object and calculating all state-size-parameters
// not filling exact keys: from, to
func NewTick(blockNr, stateSize uint64, prevTick *Tick) *Tick {
number := blockNr / BlocksPerTick % TicksPerCycle
fromSize := number * stateSize / TicksPerCycle
tick := &Tick{
Number: number,
FromBlock: blockNr,
ToBlock: blockNr - blockNr%BlocksPerTick + BlocksPerTick - 1,
FromSize: fromSize,
ToSize: fromSize + stateSize/TicksPerCycle - 1,
}
if tick.Number != 0 && prevTick != nil {
prevTick.FromSize = prevTick.ToSize + 1
}
return tick
}
type Schedule struct {
estimator WitnessEstimator
prevTick *Tick
stateSize uint64
}
type WitnessEstimator interface {
TotalCumulativeWitnessSize() (uint64, error)
PrefixByCumulativeWitnessSize(from []byte, size uint64) (prefix []byte, err error)
}
func NewSchedule(estimator WitnessEstimator) *Schedule {
return &Schedule{estimator: estimator}
}
// Tick - next step of MGR Schedule. Calculating range of keys of valid size
//
// Important: ticks are cycled. When `TicksPerCycle` reached - it starts from beginning of state.
// Means tick.FromSize > prevTick.ToSize - only when tick.Number != 0
func (s *Schedule) Tick(block uint64) (*Tick, error) {
var err error
if s.prevTick == nil {
s.stateSize, err = s.estimator.TotalCumulativeWitnessSize()
if err != nil {
return nil, err
}
}
tick := NewTick(block, s.stateSize, s.prevTick)
var prevKey []byte
if tick.Number != 0 && s.prevTick != nil {
prevKey = s.prevTick.To
}
tick.From, _ = dbutils.NextSubtree(prevKey)
if tick.To, err = s.estimator.PrefixByCumulativeWitnessSize(tick.From, tick.ToSize-tick.FromSize); err != nil {
return tick, err
}
if tick.From == nil {
tick.From = []byte{}
}
s.prevTick = tick
return tick, nil
}