github.com/bcnmy/go-ethereum@v1.10.27/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/ethereum/go-ethereum/common" 23 "github.com/ethereum/go-ethereum/log" 24 "github.com/ethereum/go-ethereum/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 copy.fetches[root] = p.db.CopyTrie(fetch) 130 } 131 return copy 132 } 133 // Otherwise we're copying an active fetcher, retrieve the current states 134 for id, fetcher := range p.fetchers { 135 copy.fetches[id] = fetcher.peek() 136 } 137 return copy 138 } 139 140 // prefetch schedules a batch of trie items to prefetch. 141 func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, keys [][]byte) { 142 // If the prefetcher is an inactive one, bail out 143 if p.fetches != nil { 144 return 145 } 146 // Active fetcher, schedule the retrievals 147 id := p.trieID(owner, root) 148 fetcher := p.fetchers[id] 149 if fetcher == nil { 150 fetcher = newSubfetcher(p.db, owner, root) 151 p.fetchers[id] = fetcher 152 } 153 fetcher.schedule(keys) 154 } 155 156 // trie returns the trie matching the root hash, or nil if the prefetcher doesn't 157 // have it. 158 func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie { 159 // If the prefetcher is inactive, return from existing deep copies 160 id := p.trieID(owner, root) 161 if p.fetches != nil { 162 trie := p.fetches[id] 163 if trie == nil { 164 p.deliveryMissMeter.Mark(1) 165 return nil 166 } 167 return p.db.CopyTrie(trie) 168 } 169 // Otherwise the prefetcher is active, bail if no trie was prefetched for this root 170 fetcher := p.fetchers[id] 171 if fetcher == nil { 172 p.deliveryMissMeter.Mark(1) 173 return nil 174 } 175 // Interrupt the prefetcher if it's by any chance still running and return 176 // a copy of any pre-loaded trie. 177 fetcher.abort() // safe to do multiple times 178 179 trie := fetcher.peek() 180 if trie == nil { 181 p.deliveryMissMeter.Mark(1) 182 return nil 183 } 184 return trie 185 } 186 187 // used marks a batch of state items used to allow creating statistics as to 188 // how useful or wasteful the prefetcher is. 189 func (p *triePrefetcher) used(owner common.Hash, root common.Hash, used [][]byte) { 190 if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil { 191 fetcher.used = used 192 } 193 } 194 195 // trieID returns an unique trie identifier consists the trie owner and root hash. 196 func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string { 197 return string(append(owner.Bytes(), root.Bytes()...)) 198 } 199 200 // subfetcher is a trie fetcher goroutine responsible for pulling entries for a 201 // single trie. It is spawned when a new root is encountered and lives until the 202 // main prefetcher is paused and either all requested items are processed or if 203 // the trie being worked on is retrieved from the prefetcher. 204 type subfetcher struct { 205 db Database // Database to load trie nodes through 206 owner common.Hash // Owner of the trie, usually account hash 207 root common.Hash // Root hash of the trie to prefetch 208 trie Trie // Trie being populated with nodes 209 210 tasks [][]byte // Items queued up for retrieval 211 lock sync.Mutex // Lock protecting the task queue 212 213 wake chan struct{} // Wake channel if a new task is scheduled 214 stop chan struct{} // Channel to interrupt processing 215 term chan struct{} // Channel to signal interruption 216 copy chan chan Trie // Channel to request a copy of the current trie 217 218 seen map[string]struct{} // Tracks the entries already loaded 219 dups int // Number of duplicate preload tasks 220 used [][]byte // Tracks the entries used in the end 221 } 222 223 // newSubfetcher creates a goroutine to prefetch state items belonging to a 224 // particular root hash. 225 func newSubfetcher(db Database, owner common.Hash, root common.Hash) *subfetcher { 226 sf := &subfetcher{ 227 db: db, 228 owner: owner, 229 root: root, 230 wake: make(chan struct{}, 1), 231 stop: make(chan struct{}), 232 term: make(chan struct{}), 233 copy: make(chan chan Trie), 234 seen: make(map[string]struct{}), 235 } 236 go sf.loop() 237 return sf 238 } 239 240 // schedule adds a batch of trie keys to the queue to prefetch. 241 func (sf *subfetcher) schedule(keys [][]byte) { 242 // Append the tasks to the current queue 243 sf.lock.Lock() 244 sf.tasks = append(sf.tasks, keys...) 245 sf.lock.Unlock() 246 247 // Notify the prefetcher, it's fine if it's already terminated 248 select { 249 case sf.wake <- struct{}{}: 250 default: 251 } 252 } 253 254 // peek tries to retrieve a deep copy of the fetcher's trie in whatever form it 255 // is currently. 256 func (sf *subfetcher) peek() Trie { 257 ch := make(chan Trie) 258 select { 259 case sf.copy <- ch: 260 // Subfetcher still alive, return copy from it 261 return <-ch 262 263 case <-sf.term: 264 // Subfetcher already terminated, return a copy directly 265 if sf.trie == nil { 266 return nil 267 } 268 return sf.db.CopyTrie(sf.trie) 269 } 270 } 271 272 // abort interrupts the subfetcher immediately. It is safe to call abort multiple 273 // times but it is not thread safe. 274 func (sf *subfetcher) abort() { 275 select { 276 case <-sf.stop: 277 default: 278 close(sf.stop) 279 } 280 <-sf.term 281 } 282 283 // loop waits for new tasks to be scheduled and keeps loading them until it runs 284 // out of tasks or its underlying trie is retrieved for committing. 285 func (sf *subfetcher) loop() { 286 // No matter how the loop stops, signal anyone waiting that it's terminated 287 defer close(sf.term) 288 289 // Start by opening the trie and stop processing if it fails 290 if sf.owner == (common.Hash{}) { 291 trie, err := sf.db.OpenTrie(sf.root) 292 if err != nil { 293 log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err) 294 return 295 } 296 sf.trie = trie 297 } else { 298 trie, err := sf.db.OpenStorageTrie(sf.owner, 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 } 305 // Trie opened successfully, keep prefetching items 306 for { 307 select { 308 case <-sf.wake: 309 // Subfetcher was woken up, retrieve any tasks to avoid spinning the lock 310 sf.lock.Lock() 311 tasks := sf.tasks 312 sf.tasks = nil 313 sf.lock.Unlock() 314 315 // Prefetch any tasks until the loop is interrupted 316 for i, task := range tasks { 317 select { 318 case <-sf.stop: 319 // If termination is requested, add any leftover back and return 320 sf.lock.Lock() 321 sf.tasks = append(sf.tasks, tasks[i:]...) 322 sf.lock.Unlock() 323 return 324 325 case ch := <-sf.copy: 326 // Somebody wants a copy of the current trie, grant them 327 ch <- sf.db.CopyTrie(sf.trie) 328 329 default: 330 // No termination request yet, prefetch the next entry 331 if _, ok := sf.seen[string(task)]; ok { 332 sf.dups++ 333 } else { 334 if len(task) == len(common.Address{}) { 335 sf.trie.TryGetAccount(task) 336 } else { 337 sf.trie.TryGet(task) 338 } 339 sf.seen[string(task)] = struct{}{} 340 } 341 } 342 } 343 344 case ch := <-sf.copy: 345 // Somebody wants a copy of the current trie, grant them 346 ch <- sf.db.CopyTrie(sf.trie) 347 348 case <-sf.stop: 349 // Termination is requested, abort and leave remaining tasks 350 return 351 } 352 } 353 }