github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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 fetched tries. Only populated for inactive copies. 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 trieID := make([]byte, common.HashLength*2) 201 copy(trieID, owner.Bytes()) 202 copy(trieID[common.HashLength:], root.Bytes()) 203 return string(trieID) 204 } 205 206 // subfetcher is a trie fetcher goroutine responsible for pulling entries for a 207 // single trie. It is spawned when a new root is encountered and lives until the 208 // main prefetcher is paused and either all requested items are processed or if 209 // the trie being worked on is retrieved from the prefetcher. 210 type subfetcher struct { 211 db Database // Database to load trie nodes through 212 state common.Hash // Root hash of the state to prefetch 213 owner common.Hash // Owner of the trie, usually account hash 214 root common.Hash // Root hash of the trie to prefetch 215 addr common.Address // Address of the account that the trie belongs to 216 trie Trie // Trie being populated with nodes 217 218 tasks [][]byte // Items queued up for retrieval 219 lock sync.Mutex // Lock protecting the task queue 220 221 wake chan struct{} // Wake channel if a new task is scheduled 222 stop chan struct{} // Channel to interrupt processing 223 term chan struct{} // Channel to signal interruption 224 copy chan chan Trie // Channel to request a copy of the current trie 225 226 seen map[string]struct{} // Tracks the entries already loaded 227 dups int // Number of duplicate preload tasks 228 used [][]byte // Tracks the entries used in the end 229 } 230 231 // newSubfetcher creates a goroutine to prefetch state items belonging to a 232 // particular root hash. 233 func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash, addr common.Address) *subfetcher { 234 sf := &subfetcher{ 235 db: db, 236 state: state, 237 owner: owner, 238 root: root, 239 addr: addr, 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 // The trie argument can be nil as verkle doesn't support prefetching 309 // yet. TODO FIX IT(rjl493456442), otherwise code will panic here. 310 trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil) 311 if err != nil { 312 log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err) 313 return 314 } 315 sf.trie = trie 316 } 317 // Trie opened successfully, keep prefetching items 318 for { 319 select { 320 case <-sf.wake: 321 // Subfetcher was woken up, retrieve any tasks to avoid spinning the lock 322 sf.lock.Lock() 323 tasks := sf.tasks 324 sf.tasks = nil 325 sf.lock.Unlock() 326 327 // Prefetch any tasks until the loop is interrupted 328 for i, task := range tasks { 329 select { 330 case <-sf.stop: 331 // If termination is requested, add any leftover back and return 332 sf.lock.Lock() 333 sf.tasks = append(sf.tasks, tasks[i:]...) 334 sf.lock.Unlock() 335 return 336 337 case ch := <-sf.copy: 338 // Somebody wants a copy of the current trie, grant them 339 ch <- sf.db.CopyTrie(sf.trie) 340 341 default: 342 // No termination request yet, prefetch the next entry 343 if _, ok := sf.seen[string(task)]; ok { 344 sf.dups++ 345 } else { 346 if len(task) == common.AddressLength { 347 sf.trie.GetAccount(common.BytesToAddress(task)) 348 } else { 349 sf.trie.GetStorage(sf.addr, task) 350 } 351 sf.seen[string(task)] = struct{}{} 352 } 353 } 354 } 355 356 case ch := <-sf.copy: 357 // Somebody wants a copy of the current trie, grant them 358 ch <- sf.db.CopyTrie(sf.trie) 359 360 case <-sf.stop: 361 // Termination is requested, abort and leave remaining tasks 362 return 363 } 364 } 365 }