github.com/ethereum/go-ethereum@v1.16.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 "errors" 21 "sync" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/log" 25 "github.com/ethereum/go-ethereum/metrics" 26 ) 27 28 var ( 29 // triePrefetchMetricsPrefix is the prefix under which to publish the metrics. 30 triePrefetchMetricsPrefix = "trie/prefetch/" 31 32 // errTerminated is returned if a fetcher is attempted to be operated after it 33 // has already terminated. 34 errTerminated = errors.New("fetcher is already terminated") 35 ) 36 37 // triePrefetcher is an active prefetcher, which receives accounts or storage 38 // items and does trie-loading of them. The goal is to get as much useful content 39 // into the caches as possible. 40 // 41 // Note, the prefetcher's API is not thread safe. 42 type triePrefetcher struct { 43 verkle bool // Flag whether the prefetcher is in verkle mode 44 db Database // Database to fetch trie nodes through 45 root common.Hash // Root hash of the account trie for metrics 46 fetchers map[string]*subfetcher // Subfetchers for each trie 47 term chan struct{} // Channel to signal interruption 48 noreads bool // Whether to ignore state-read-only prefetch requests 49 50 deliveryMissMeter *metrics.Meter 51 52 accountLoadReadMeter *metrics.Meter 53 accountLoadWriteMeter *metrics.Meter 54 accountDupReadMeter *metrics.Meter 55 accountDupWriteMeter *metrics.Meter 56 accountDupCrossMeter *metrics.Meter 57 accountWasteMeter *metrics.Meter 58 59 storageLoadReadMeter *metrics.Meter 60 storageLoadWriteMeter *metrics.Meter 61 storageDupReadMeter *metrics.Meter 62 storageDupWriteMeter *metrics.Meter 63 storageDupCrossMeter *metrics.Meter 64 storageWasteMeter *metrics.Meter 65 } 66 67 func newTriePrefetcher(db Database, root common.Hash, namespace string, noreads bool) *triePrefetcher { 68 prefix := triePrefetchMetricsPrefix + namespace 69 return &triePrefetcher{ 70 verkle: db.TrieDB().IsVerkle(), 71 db: db, 72 root: root, 73 fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map 74 term: make(chan struct{}), 75 noreads: noreads, 76 77 deliveryMissMeter: metrics.GetOrRegisterMeter(prefix+"/deliverymiss", nil), 78 79 accountLoadReadMeter: metrics.GetOrRegisterMeter(prefix+"/account/load/read", nil), 80 accountLoadWriteMeter: metrics.GetOrRegisterMeter(prefix+"/account/load/write", nil), 81 accountDupReadMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup/read", nil), 82 accountDupWriteMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup/write", nil), 83 accountDupCrossMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup/cross", nil), 84 accountWasteMeter: metrics.GetOrRegisterMeter(prefix+"/account/waste", nil), 85 86 storageLoadReadMeter: metrics.GetOrRegisterMeter(prefix+"/storage/load/read", nil), 87 storageLoadWriteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/load/write", nil), 88 storageDupReadMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup/read", nil), 89 storageDupWriteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup/write", nil), 90 storageDupCrossMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup/cross", nil), 91 storageWasteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/waste", nil), 92 } 93 } 94 95 // terminate iterates over all the subfetchers and issues a termination request 96 // to all of them. Depending on the async parameter, the method will either block 97 // until all subfetchers spin down, or return immediately. 98 func (p *triePrefetcher) terminate(async bool) { 99 // Short circuit if the fetcher is already closed 100 select { 101 case <-p.term: 102 return 103 default: 104 } 105 // Terminate all sub-fetchers, sync or async, depending on the request 106 for _, fetcher := range p.fetchers { 107 fetcher.terminate(async) 108 } 109 close(p.term) 110 } 111 112 // report aggregates the pre-fetching and usage metrics and reports them. 113 func (p *triePrefetcher) report() { 114 if !metrics.Enabled() { 115 return 116 } 117 for _, fetcher := range p.fetchers { 118 fetcher.wait() // ensure the fetcher's idle before poking in its internals 119 120 if fetcher.root == p.root { 121 p.accountLoadReadMeter.Mark(int64(len(fetcher.seenReadAddr))) 122 p.accountLoadWriteMeter.Mark(int64(len(fetcher.seenWriteAddr))) 123 124 p.accountDupReadMeter.Mark(int64(fetcher.dupsRead)) 125 p.accountDupWriteMeter.Mark(int64(fetcher.dupsWrite)) 126 p.accountDupCrossMeter.Mark(int64(fetcher.dupsCross)) 127 128 for _, key := range fetcher.usedAddr { 129 delete(fetcher.seenReadAddr, key) 130 delete(fetcher.seenWriteAddr, key) 131 } 132 p.accountWasteMeter.Mark(int64(len(fetcher.seenReadAddr) + len(fetcher.seenWriteAddr))) 133 } else { 134 p.storageLoadReadMeter.Mark(int64(len(fetcher.seenReadSlot))) 135 p.storageLoadWriteMeter.Mark(int64(len(fetcher.seenWriteSlot))) 136 137 p.storageDupReadMeter.Mark(int64(fetcher.dupsRead)) 138 p.storageDupWriteMeter.Mark(int64(fetcher.dupsWrite)) 139 p.storageDupCrossMeter.Mark(int64(fetcher.dupsCross)) 140 141 for _, key := range fetcher.usedSlot { 142 delete(fetcher.seenReadSlot, key) 143 delete(fetcher.seenWriteSlot, key) 144 } 145 p.storageWasteMeter.Mark(int64(len(fetcher.seenReadSlot) + len(fetcher.seenWriteSlot))) 146 } 147 } 148 } 149 150 // prefetch schedules a batch of trie items to prefetch. After the prefetcher is 151 // closed, all the following tasks scheduled will not be executed and an error 152 // will be returned. 153 // 154 // prefetch is called from two locations: 155 // 156 // 1. Finalize of the state-objects storage roots. This happens at the end 157 // of every transaction, meaning that if several transactions touches 158 // upon the same contract, the parameters invoking this method may be 159 // repeated. 160 // 2. Finalize of the main account trie. This happens only once per block. 161 func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, addrs []common.Address, slots []common.Hash, read bool) error { 162 // If the state item is only being read, but reads are disabled, return 163 if read && p.noreads { 164 return nil 165 } 166 // Ensure the subfetcher is still alive 167 select { 168 case <-p.term: 169 return errTerminated 170 default: 171 } 172 id := p.trieID(owner, root) 173 fetcher := p.fetchers[id] 174 if fetcher == nil { 175 fetcher = newSubfetcher(p.db, p.root, owner, root, addr) 176 p.fetchers[id] = fetcher 177 } 178 return fetcher.schedule(addrs, slots, read) 179 } 180 181 // trie returns the trie matching the root hash, blocking until the fetcher of 182 // the given trie terminates. If no fetcher exists for the request, nil will be 183 // returned. 184 func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie { 185 // Bail if no trie was prefetched for this root 186 fetcher := p.fetchers[p.trieID(owner, root)] 187 if fetcher == nil { 188 log.Error("Prefetcher missed to load trie", "owner", owner, "root", root) 189 p.deliveryMissMeter.Mark(1) 190 return nil 191 } 192 // Subfetcher exists, retrieve its trie 193 return fetcher.peek() 194 } 195 196 // used marks a batch of state items used to allow creating statistics as to 197 // how useful or wasteful the fetcher is. 198 func (p *triePrefetcher) used(owner common.Hash, root common.Hash, usedAddr []common.Address, usedSlot []common.Hash) { 199 if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil { 200 fetcher.wait() // ensure the fetcher's idle before poking in its internals 201 202 fetcher.usedAddr = append(fetcher.usedAddr, usedAddr...) 203 fetcher.usedSlot = append(fetcher.usedSlot, usedSlot...) 204 } 205 } 206 207 // trieID returns an unique trie identifier consists the trie owner and root hash. 208 func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string { 209 // The trie in verkle is only identified by state root 210 if p.verkle { 211 return p.root.Hex() 212 } 213 // The trie in merkle is either identified by state root (account trie), 214 // or identified by the owner and trie root (storage trie) 215 trieID := make([]byte, common.HashLength*2) 216 copy(trieID, owner.Bytes()) 217 copy(trieID[common.HashLength:], root.Bytes()) 218 return string(trieID) 219 } 220 221 // subfetcher is a trie fetcher goroutine responsible for pulling entries for a 222 // single trie. It is spawned when a new root is encountered and lives until the 223 // main prefetcher is paused and either all requested items are processed or if 224 // the trie being worked on is retrieved from the prefetcher. 225 type subfetcher struct { 226 db Database // Database to load trie nodes through 227 state common.Hash // Root hash of the state to prefetch 228 owner common.Hash // Owner of the trie, usually account hash 229 root common.Hash // Root hash of the trie to prefetch 230 addr common.Address // Address of the account that the trie belongs to 231 trie Trie // Trie being populated with nodes 232 233 tasks []*subfetcherTask // Items queued up for retrieval 234 lock sync.Mutex // Lock protecting the task queue 235 236 wake chan struct{} // Wake channel if a new task is scheduled 237 stop chan struct{} // Channel to interrupt processing 238 term chan struct{} // Channel to signal interruption 239 240 seenReadAddr map[common.Address]struct{} // Tracks the accounts already loaded via read operations 241 seenWriteAddr map[common.Address]struct{} // Tracks the accounts already loaded via write operations 242 seenReadSlot map[common.Hash]struct{} // Tracks the storage already loaded via read operations 243 seenWriteSlot map[common.Hash]struct{} // Tracks the storage already loaded via write operations 244 245 dupsRead int // Number of duplicate preload tasks via reads only 246 dupsWrite int // Number of duplicate preload tasks via writes only 247 dupsCross int // Number of duplicate preload tasks via read-write-crosses 248 249 usedAddr []common.Address // Tracks the accounts used in the end 250 usedSlot []common.Hash // Tracks the storage used in the end 251 } 252 253 // subfetcherTask is a trie path to prefetch, tagged with whether it originates 254 // from a read or a write request. 255 type subfetcherTask struct { 256 read bool 257 addr *common.Address 258 slot *common.Hash 259 } 260 261 // newSubfetcher creates a goroutine to prefetch state items belonging to a 262 // particular root hash. 263 func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash, addr common.Address) *subfetcher { 264 sf := &subfetcher{ 265 db: db, 266 state: state, 267 owner: owner, 268 root: root, 269 addr: addr, 270 wake: make(chan struct{}, 1), 271 stop: make(chan struct{}), 272 term: make(chan struct{}), 273 seenReadAddr: make(map[common.Address]struct{}), 274 seenWriteAddr: make(map[common.Address]struct{}), 275 seenReadSlot: make(map[common.Hash]struct{}), 276 seenWriteSlot: make(map[common.Hash]struct{}), 277 } 278 go sf.loop() 279 return sf 280 } 281 282 // schedule adds a batch of trie keys to the queue to prefetch. 283 func (sf *subfetcher) schedule(addrs []common.Address, slots []common.Hash, read bool) error { 284 // Ensure the subfetcher is still alive 285 select { 286 case <-sf.term: 287 return errTerminated 288 default: 289 } 290 // Append the tasks to the current queue 291 sf.lock.Lock() 292 for _, addr := range addrs { 293 sf.tasks = append(sf.tasks, &subfetcherTask{read: read, addr: &addr}) 294 } 295 for _, slot := range slots { 296 sf.tasks = append(sf.tasks, &subfetcherTask{read: read, slot: &slot}) 297 } 298 sf.lock.Unlock() 299 300 // Notify the background thread to execute scheduled tasks 301 select { 302 case sf.wake <- struct{}{}: 303 // Wake signal sent 304 default: 305 // Wake signal not sent as a previous one is already queued 306 } 307 return nil 308 } 309 310 // wait blocks until the subfetcher terminates. This method is used to block on 311 // an async termination before accessing internal fields from the fetcher. 312 func (sf *subfetcher) wait() { 313 <-sf.term 314 } 315 316 // peek retrieves the fetcher's trie, populated with any pre-fetched data. The 317 // returned trie will be a shallow copy, so modifying it will break subsequent 318 // peeks for the original data. The method will block until all the scheduled 319 // data has been loaded and the fethcer terminated. 320 func (sf *subfetcher) peek() Trie { 321 // Block until the fetcher terminates, then retrieve the trie 322 sf.wait() 323 return sf.trie 324 } 325 326 // terminate requests the subfetcher to stop accepting new tasks and spin down 327 // as soon as everything is loaded. Depending on the async parameter, the method 328 // will either block until all disk loads finish or return immediately. 329 func (sf *subfetcher) terminate(async bool) { 330 select { 331 case <-sf.stop: 332 default: 333 close(sf.stop) 334 } 335 if async { 336 return 337 } 338 <-sf.term 339 } 340 341 // openTrie resolves the target trie from database for prefetching. 342 func (sf *subfetcher) openTrie() error { 343 // Open the verkle tree if the sub-fetcher is in verkle mode. Note, there is 344 // only a single fetcher for verkle. 345 if sf.db.TrieDB().IsVerkle() { 346 tr, err := sf.db.OpenTrie(sf.state) 347 if err != nil { 348 log.Warn("Trie prefetcher failed opening verkle trie", "root", sf.root, "err", err) 349 return err 350 } 351 sf.trie = tr 352 return nil 353 } 354 // Open the merkle tree if the sub-fetcher is in merkle mode 355 if sf.owner == (common.Hash{}) { 356 tr, err := sf.db.OpenTrie(sf.state) 357 if err != nil { 358 log.Warn("Trie prefetcher failed opening account trie", "root", sf.root, "err", err) 359 return err 360 } 361 sf.trie = tr 362 return nil 363 } 364 tr, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil) 365 if err != nil { 366 log.Warn("Trie prefetcher failed opening storage trie", "root", sf.root, "err", err) 367 return err 368 } 369 sf.trie = tr 370 return nil 371 } 372 373 // loop loads newly-scheduled trie tasks as they are received and loads them, stopping 374 // when requested. 375 func (sf *subfetcher) loop() { 376 // No matter how the loop stops, signal anyone waiting that it's terminated 377 defer close(sf.term) 378 379 if err := sf.openTrie(); err != nil { 380 return 381 } 382 for { 383 select { 384 case <-sf.wake: 385 // Execute all remaining tasks in a single run 386 sf.lock.Lock() 387 tasks := sf.tasks 388 sf.tasks = nil 389 sf.lock.Unlock() 390 391 for _, task := range tasks { 392 if task.addr != nil { 393 key := *task.addr 394 if task.read { 395 if _, ok := sf.seenReadAddr[key]; ok { 396 sf.dupsRead++ 397 continue 398 } 399 if _, ok := sf.seenWriteAddr[key]; ok { 400 sf.dupsCross++ 401 continue 402 } 403 } else { 404 if _, ok := sf.seenReadAddr[key]; ok { 405 sf.dupsCross++ 406 continue 407 } 408 if _, ok := sf.seenWriteAddr[key]; ok { 409 sf.dupsWrite++ 410 continue 411 } 412 } 413 } else { 414 key := *task.slot 415 if task.read { 416 if _, ok := sf.seenReadSlot[key]; ok { 417 sf.dupsRead++ 418 continue 419 } 420 if _, ok := sf.seenWriteSlot[key]; ok { 421 sf.dupsCross++ 422 continue 423 } 424 } else { 425 if _, ok := sf.seenReadSlot[key]; ok { 426 sf.dupsCross++ 427 continue 428 } 429 if _, ok := sf.seenWriteSlot[key]; ok { 430 sf.dupsWrite++ 431 continue 432 } 433 } 434 } 435 if task.addr != nil { 436 sf.trie.GetAccount(*task.addr) 437 } else { 438 sf.trie.GetStorage(sf.addr, (*task.slot)[:]) 439 } 440 if task.read { 441 if task.addr != nil { 442 sf.seenReadAddr[*task.addr] = struct{}{} 443 } else { 444 sf.seenReadSlot[*task.slot] = struct{}{} 445 } 446 } else { 447 if task.addr != nil { 448 sf.seenWriteAddr[*task.addr] = struct{}{} 449 } else { 450 sf.seenWriteSlot[*task.slot] = struct{}{} 451 } 452 } 453 } 454 455 case <-sf.stop: 456 // Termination is requested, abort if no more tasks are pending. If 457 // there are some, exhaust them first. 458 sf.lock.Lock() 459 done := sf.tasks == nil 460 sf.lock.Unlock() 461 462 if done { 463 return 464 } 465 // Some tasks are pending, loop and pick them up (that wake branch 466 // will be selected eventually, whilst stop remains closed to this 467 // branch will also run afterwards). 468 } 469 } 470 }