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