github.com/theQRL/go-zond@v0.1.1/core/state/trie_prefetcher.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package state 18 19 import ( 20 "sync" 21 22 "github.com/theQRL/go-zond/common" 23 "github.com/theQRL/go-zond/log" 24 "github.com/theQRL/go-zond/metrics" 25 ) 26 27 var ( 28 // triePrefetchMetricsPrefix is the prefix under which to publish the metrics. 29 triePrefetchMetricsPrefix = "trie/prefetch/" 30 ) 31 32 // triePrefetcher is an active prefetcher, which receives accounts or storage 33 // items and does trie-loading of them. The goal is to get as much useful content 34 // into the caches as possible. 35 // 36 // Note, the prefetcher's API is not thread safe. 37 type triePrefetcher struct { 38 db Database // Database to fetch trie nodes through 39 root common.Hash // Root hash of the account trie for metrics 40 fetches map[string]Trie // Partially or fully fetcher tries 41 fetchers map[string]*subfetcher // Subfetchers for each trie 42 43 deliveryMissMeter metrics.Meter 44 accountLoadMeter metrics.Meter 45 accountDupMeter metrics.Meter 46 accountSkipMeter metrics.Meter 47 accountWasteMeter metrics.Meter 48 storageLoadMeter metrics.Meter 49 storageDupMeter metrics.Meter 50 storageSkipMeter metrics.Meter 51 storageWasteMeter metrics.Meter 52 } 53 54 func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePrefetcher { 55 prefix := triePrefetchMetricsPrefix + namespace 56 p := &triePrefetcher{ 57 db: db, 58 root: root, 59 fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map 60 61 deliveryMissMeter: metrics.GetOrRegisterMeter(prefix+"/deliverymiss", nil), 62 accountLoadMeter: metrics.GetOrRegisterMeter(prefix+"/account/load", nil), 63 accountDupMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup", nil), 64 accountSkipMeter: metrics.GetOrRegisterMeter(prefix+"/account/skip", nil), 65 accountWasteMeter: metrics.GetOrRegisterMeter(prefix+"/account/waste", nil), 66 storageLoadMeter: metrics.GetOrRegisterMeter(prefix+"/storage/load", nil), 67 storageDupMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup", nil), 68 storageSkipMeter: metrics.GetOrRegisterMeter(prefix+"/storage/skip", nil), 69 storageWasteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/waste", nil), 70 } 71 return p 72 } 73 74 // close iterates over all the subfetchers, aborts any that were left spinning 75 // and reports the stats to the metrics subsystem. 76 func (p *triePrefetcher) close() { 77 for _, fetcher := range p.fetchers { 78 fetcher.abort() // safe to do multiple times 79 80 if metrics.Enabled { 81 if fetcher.root == p.root { 82 p.accountLoadMeter.Mark(int64(len(fetcher.seen))) 83 p.accountDupMeter.Mark(int64(fetcher.dups)) 84 p.accountSkipMeter.Mark(int64(len(fetcher.tasks))) 85 86 for _, key := range fetcher.used { 87 delete(fetcher.seen, string(key)) 88 } 89 p.accountWasteMeter.Mark(int64(len(fetcher.seen))) 90 } else { 91 p.storageLoadMeter.Mark(int64(len(fetcher.seen))) 92 p.storageDupMeter.Mark(int64(fetcher.dups)) 93 p.storageSkipMeter.Mark(int64(len(fetcher.tasks))) 94 95 for _, key := range fetcher.used { 96 delete(fetcher.seen, string(key)) 97 } 98 p.storageWasteMeter.Mark(int64(len(fetcher.seen))) 99 } 100 } 101 } 102 // Clear out all fetchers (will crash on a second call, deliberate) 103 p.fetchers = nil 104 } 105 106 // copy creates a deep-but-inactive copy of the trie prefetcher. Any trie data 107 // already loaded will be copied over, but no goroutines will be started. This 108 // is mostly used in the miner which creates a copy of it's actively mutated 109 // state to be sealed while it may further mutate the state. 110 func (p *triePrefetcher) copy() *triePrefetcher { 111 copy := &triePrefetcher{ 112 db: p.db, 113 root: p.root, 114 fetches: make(map[string]Trie), // Active prefetchers use the fetches map 115 116 deliveryMissMeter: p.deliveryMissMeter, 117 accountLoadMeter: p.accountLoadMeter, 118 accountDupMeter: p.accountDupMeter, 119 accountSkipMeter: p.accountSkipMeter, 120 accountWasteMeter: p.accountWasteMeter, 121 storageLoadMeter: p.storageLoadMeter, 122 storageDupMeter: p.storageDupMeter, 123 storageSkipMeter: p.storageSkipMeter, 124 storageWasteMeter: p.storageWasteMeter, 125 } 126 // If the prefetcher is already a copy, duplicate the data 127 if p.fetches != nil { 128 for root, fetch := range p.fetches { 129 if fetch == nil { 130 continue 131 } 132 copy.fetches[root] = p.db.CopyTrie(fetch) 133 } 134 return copy 135 } 136 // Otherwise we're copying an active fetcher, retrieve the current states 137 for id, fetcher := range p.fetchers { 138 copy.fetches[id] = fetcher.peek() 139 } 140 return copy 141 } 142 143 // prefetch schedules a batch of trie items to prefetch. 144 func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte) { 145 // If the prefetcher is an inactive one, bail out 146 if p.fetches != nil { 147 return 148 } 149 // Active fetcher, schedule the retrievals 150 id := p.trieID(owner, root) 151 fetcher := p.fetchers[id] 152 if fetcher == nil { 153 fetcher = newSubfetcher(p.db, p.root, owner, root, addr) 154 p.fetchers[id] = fetcher 155 } 156 fetcher.schedule(keys) 157 } 158 159 // trie returns the trie matching the root hash, or nil if the prefetcher doesn't 160 // have it. 161 func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie { 162 // If the prefetcher is inactive, return from existing deep copies 163 id := p.trieID(owner, root) 164 if p.fetches != nil { 165 trie := p.fetches[id] 166 if trie == nil { 167 p.deliveryMissMeter.Mark(1) 168 return nil 169 } 170 return p.db.CopyTrie(trie) 171 } 172 // Otherwise the prefetcher is active, bail if no trie was prefetched for this root 173 fetcher := p.fetchers[id] 174 if fetcher == nil { 175 p.deliveryMissMeter.Mark(1) 176 return nil 177 } 178 // Interrupt the prefetcher if it's by any chance still running and return 179 // a copy of any pre-loaded trie. 180 fetcher.abort() // safe to do multiple times 181 182 trie := fetcher.peek() 183 if trie == nil { 184 p.deliveryMissMeter.Mark(1) 185 return nil 186 } 187 return trie 188 } 189 190 // used marks a batch of state items used to allow creating statistics as to 191 // how useful or wasteful the prefetcher is. 192 func (p *triePrefetcher) used(owner common.Hash, root common.Hash, used [][]byte) { 193 if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil { 194 fetcher.used = used 195 } 196 } 197 198 // trieID returns an unique trie identifier consists the trie owner and root hash. 199 func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string { 200 return string(append(owner.Bytes(), root.Bytes()...)) 201 } 202 203 // subfetcher is a trie fetcher goroutine responsible for pulling entries for a 204 // single trie. It is spawned when a new root is encountered and lives until the 205 // main prefetcher is paused and either all requested items are processed or if 206 // the trie being worked on is retrieved from the prefetcher. 207 type subfetcher struct { 208 db Database // Database to load trie nodes through 209 state common.Hash // Root hash of the state to prefetch 210 owner common.Hash // Owner of the trie, usually account hash 211 root common.Hash // Root hash of the trie to prefetch 212 addr common.Address // Address of the account that the trie belongs to 213 trie Trie // Trie being populated with nodes 214 215 tasks [][]byte // Items queued up for retrieval 216 lock sync.Mutex // Lock protecting the task queue 217 218 wake chan struct{} // Wake channel if a new task is scheduled 219 stop chan struct{} // Channel to interrupt processing 220 term chan struct{} // Channel to signal interruption 221 copy chan chan Trie // Channel to request a copy of the current trie 222 223 seen map[string]struct{} // Tracks the entries already loaded 224 dups int // Number of duplicate preload tasks 225 used [][]byte // Tracks the entries used in the end 226 } 227 228 // newSubfetcher creates a goroutine to prefetch state items belonging to a 229 // particular root hash. 230 func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash, addr common.Address) *subfetcher { 231 sf := &subfetcher{ 232 db: db, 233 state: state, 234 owner: owner, 235 root: root, 236 addr: addr, 237 wake: make(chan struct{}, 1), 238 stop: make(chan struct{}), 239 term: make(chan struct{}), 240 copy: make(chan chan Trie), 241 seen: make(map[string]struct{}), 242 } 243 go sf.loop() 244 return sf 245 } 246 247 // schedule adds a batch of trie keys to the queue to prefetch. 248 func (sf *subfetcher) schedule(keys [][]byte) { 249 // Append the tasks to the current queue 250 sf.lock.Lock() 251 sf.tasks = append(sf.tasks, keys...) 252 sf.lock.Unlock() 253 254 // Notify the prefetcher, it's fine if it's already terminated 255 select { 256 case sf.wake <- struct{}{}: 257 default: 258 } 259 } 260 261 // peek tries to retrieve a deep copy of the fetcher's trie in whatever form it 262 // is currently. 263 func (sf *subfetcher) peek() Trie { 264 ch := make(chan Trie) 265 select { 266 case sf.copy <- ch: 267 // Subfetcher still alive, return copy from it 268 return <-ch 269 270 case <-sf.term: 271 // Subfetcher already terminated, return a copy directly 272 if sf.trie == nil { 273 return nil 274 } 275 return sf.db.CopyTrie(sf.trie) 276 } 277 } 278 279 // abort interrupts the subfetcher immediately. It is safe to call abort multiple 280 // times but it is not thread safe. 281 func (sf *subfetcher) abort() { 282 select { 283 case <-sf.stop: 284 default: 285 close(sf.stop) 286 } 287 <-sf.term 288 } 289 290 // loop waits for new tasks to be scheduled and keeps loading them until it runs 291 // out of tasks or its underlying trie is retrieved for committing. 292 func (sf *subfetcher) loop() { 293 // No matter how the loop stops, signal anyone waiting that it's terminated 294 defer close(sf.term) 295 296 // Start by opening the trie and stop processing if it fails 297 if sf.owner == (common.Hash{}) { 298 trie, err := sf.db.OpenTrie(sf.root) 299 if err != nil { 300 log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err) 301 return 302 } 303 sf.trie = trie 304 } else { 305 trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root) 306 if err != nil { 307 log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err) 308 return 309 } 310 sf.trie = trie 311 } 312 // Trie opened successfully, keep prefetching items 313 for { 314 select { 315 case <-sf.wake: 316 // Subfetcher was woken up, retrieve any tasks to avoid spinning the lock 317 sf.lock.Lock() 318 tasks := sf.tasks 319 sf.tasks = nil 320 sf.lock.Unlock() 321 322 // Prefetch any tasks until the loop is interrupted 323 for i, task := range tasks { 324 select { 325 case <-sf.stop: 326 // If termination is requested, add any leftover back and return 327 sf.lock.Lock() 328 sf.tasks = append(sf.tasks, tasks[i:]...) 329 sf.lock.Unlock() 330 return 331 332 case ch := <-sf.copy: 333 // Somebody wants a copy of the current trie, grant them 334 ch <- sf.db.CopyTrie(sf.trie) 335 336 default: 337 // No termination request yet, prefetch the next entry 338 if _, ok := sf.seen[string(task)]; ok { 339 sf.dups++ 340 } else { 341 if len(task) == common.AddressLength { 342 sf.trie.GetAccount(common.BytesToAddress(task)) 343 } else { 344 sf.trie.GetStorage(sf.addr, task) 345 } 346 sf.seen[string(task)] = struct{}{} 347 } 348 } 349 } 350 351 case ch := <-sf.copy: 352 // Somebody wants a copy of the current trie, grant them 353 ch <- sf.db.CopyTrie(sf.trie) 354 355 case <-sf.stop: 356 // Termination is requested, abort and leave remaining tasks 357 return 358 } 359 } 360 }