github.com/Unheilbar/quorum@v1.0.0/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 publis 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 theaccount trie for metrics 40 fetches map[common.Hash]Trie // Partially or fully fetcher tries 41 fetchers map[common.Hash]*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 // newTriePrefetcher 55 func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePrefetcher { 56 prefix := triePrefetchMetricsPrefix + namespace 57 p := &triePrefetcher{ 58 db: db, 59 root: root, 60 fetchers: make(map[common.Hash]*subfetcher), // Active prefetchers use the fetchers map 61 62 deliveryMissMeter: metrics.GetOrRegisterMeter(prefix+"/deliverymiss", nil), 63 accountLoadMeter: metrics.GetOrRegisterMeter(prefix+"/account/load", nil), 64 accountDupMeter: metrics.GetOrRegisterMeter(prefix+"/account/dup", nil), 65 accountSkipMeter: metrics.GetOrRegisterMeter(prefix+"/account/skip", nil), 66 accountWasteMeter: metrics.GetOrRegisterMeter(prefix+"/account/waste", nil), 67 storageLoadMeter: metrics.GetOrRegisterMeter(prefix+"/storage/load", nil), 68 storageDupMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup", nil), 69 storageSkipMeter: metrics.GetOrRegisterMeter(prefix+"/storage/skip", nil), 70 storageWasteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/waste", nil), 71 } 72 return p 73 } 74 75 // close iterates over all the subfetchers, aborts any that were left spinning 76 // and reports the stats to the metrics subsystem. 77 func (p *triePrefetcher) close() { 78 for _, fetcher := range p.fetchers { 79 fetcher.abort() // safe to do multiple times 80 81 if metrics.Enabled { 82 if fetcher.root == p.root { 83 p.accountLoadMeter.Mark(int64(len(fetcher.seen))) 84 p.accountDupMeter.Mark(int64(fetcher.dups)) 85 p.accountSkipMeter.Mark(int64(len(fetcher.tasks))) 86 87 for _, key := range fetcher.used { 88 delete(fetcher.seen, string(key)) 89 } 90 p.accountWasteMeter.Mark(int64(len(fetcher.seen))) 91 } else { 92 p.storageLoadMeter.Mark(int64(len(fetcher.seen))) 93 p.storageDupMeter.Mark(int64(fetcher.dups)) 94 p.storageSkipMeter.Mark(int64(len(fetcher.tasks))) 95 96 for _, key := range fetcher.used { 97 delete(fetcher.seen, string(key)) 98 } 99 p.storageWasteMeter.Mark(int64(len(fetcher.seen))) 100 } 101 } 102 } 103 // Clear out all fetchers (will crash on a second call, deliberate) 104 p.fetchers = nil 105 } 106 107 // copy creates a deep-but-inactive copy of the trie prefetcher. Any trie data 108 // already loaded will be copied over, but no goroutines will be started. This 109 // is mostly used in the miner which creates a copy of it's actively mutated 110 // state to be sealed while it may further mutate the state. 111 func (p *triePrefetcher) copy() *triePrefetcher { 112 copy := &triePrefetcher{ 113 db: p.db, 114 root: p.root, 115 fetches: make(map[common.Hash]Trie), // Active prefetchers use the fetches map 116 117 deliveryMissMeter: p.deliveryMissMeter, 118 accountLoadMeter: p.accountLoadMeter, 119 accountDupMeter: p.accountDupMeter, 120 accountSkipMeter: p.accountSkipMeter, 121 accountWasteMeter: p.accountWasteMeter, 122 storageLoadMeter: p.storageLoadMeter, 123 storageDupMeter: p.storageDupMeter, 124 storageSkipMeter: p.storageSkipMeter, 125 storageWasteMeter: p.storageWasteMeter, 126 } 127 // If the prefetcher is already a copy, duplicate the data 128 if p.fetches != nil { 129 for root, fetch := range p.fetches { 130 if fetch != nil { 131 copy.fetches[root] = p.db.CopyTrie(fetch) 132 } 133 } 134 return copy 135 } 136 // Otherwise we're copying an active fetcher, retrieve the current states 137 for root, fetcher := range p.fetchers { 138 copy.fetches[root] = fetcher.peek() 139 } 140 return copy 141 } 142 143 // prefetch schedules a batch of trie items to prefetch. 144 func (p *triePrefetcher) prefetch(root common.Hash, 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 fetcher := p.fetchers[root] 151 if fetcher == nil { 152 fetcher = newSubfetcher(p.db, root) 153 p.fetchers[root] = fetcher 154 } 155 fetcher.schedule(keys) 156 } 157 158 // trie returns the trie matching the root hash, or nil if the prefetcher doesn't 159 // have it. 160 func (p *triePrefetcher) trie(root common.Hash) Trie { 161 // If the prefetcher is inactive, return from existing deep copies 162 if p.fetches != nil { 163 trie := p.fetches[root] 164 if trie == nil { 165 p.deliveryMissMeter.Mark(1) 166 return nil 167 } 168 return p.db.CopyTrie(trie) 169 } 170 // Otherwise the prefetcher is active, bail if no trie was prefetched for this root 171 fetcher := p.fetchers[root] 172 if fetcher == nil { 173 p.deliveryMissMeter.Mark(1) 174 return nil 175 } 176 // Interrupt the prefetcher if it's by any chance still running and return 177 // a copy of any pre-loaded trie. 178 fetcher.abort() // safe to do multiple times 179 180 trie := fetcher.peek() 181 if trie == nil { 182 p.deliveryMissMeter.Mark(1) 183 return nil 184 } 185 return trie 186 } 187 188 // used marks a batch of state items used to allow creating statistics as to 189 // how useful or wasteful the prefetcher is. 190 func (p *triePrefetcher) used(root common.Hash, used [][]byte) { 191 if fetcher := p.fetchers[root]; fetcher != nil { 192 fetcher.used = used 193 } 194 } 195 196 // subfetcher is a trie fetcher goroutine responsible for pulling entries for a 197 // single trie. It is spawned when a new root is encountered and lives until the 198 // main prefetcher is paused and either all requested items are processed or if 199 // the trie being worked on is retrieved from the prefetcher. 200 type subfetcher struct { 201 db Database // Database to load trie nodes through 202 root common.Hash // Root hash of the trie to prefetch 203 trie Trie // Trie being populated with nodes 204 205 tasks [][]byte // Items queued up for retrieval 206 lock sync.Mutex // Lock protecting the task queue 207 208 wake chan struct{} // Wake channel if a new task is scheduled 209 stop chan struct{} // Channel to interrupt processing 210 term chan struct{} // Channel to signal iterruption 211 copy chan chan Trie // Channel to request a copy of the current trie 212 213 seen map[string]struct{} // Tracks the entries already loaded 214 dups int // Number of duplicate preload tasks 215 used [][]byte // Tracks the entries used in the end 216 } 217 218 // newSubfetcher creates a goroutine to prefetch state items belonging to a 219 // particular root hash. 220 func newSubfetcher(db Database, root common.Hash) *subfetcher { 221 sf := &subfetcher{ 222 db: db, 223 root: root, 224 wake: make(chan struct{}, 1), 225 stop: make(chan struct{}), 226 term: make(chan struct{}), 227 copy: make(chan chan Trie), 228 seen: make(map[string]struct{}), 229 } 230 go sf.loop() 231 return sf 232 } 233 234 // schedule adds a batch of trie keys to the queue to prefetch. 235 func (sf *subfetcher) schedule(keys [][]byte) { 236 // Append the tasks to the current queue 237 sf.lock.Lock() 238 sf.tasks = append(sf.tasks, keys...) 239 sf.lock.Unlock() 240 241 // Notify the prefetcher, it's fine if it's already terminated 242 select { 243 case sf.wake <- struct{}{}: 244 default: 245 } 246 } 247 248 // peek tries to retrieve a deep copy of the fetcher's trie in whatever form it 249 // is currently. 250 func (sf *subfetcher) peek() Trie { 251 ch := make(chan Trie) 252 select { 253 case sf.copy <- ch: 254 // Subfetcher still alive, return copy from it 255 return <-ch 256 257 case <-sf.term: 258 // Subfetcher already terminated, return a copy directly 259 if sf.trie == nil { 260 return nil 261 } 262 return sf.db.CopyTrie(sf.trie) 263 } 264 } 265 266 // abort interrupts the subfetcher immediately. It is safe to call abort multiple 267 // times but it is not thread safe. 268 func (sf *subfetcher) abort() { 269 select { 270 case <-sf.stop: 271 default: 272 close(sf.stop) 273 } 274 <-sf.term 275 } 276 277 // loop waits for new tasks to be scheduled and keeps loading them until it runs 278 // out of tasks or its underlying trie is retrieved for committing. 279 func (sf *subfetcher) loop() { 280 // No matter how the loop stops, signal anyone waiting that it's terminated 281 defer close(sf.term) 282 283 // Start by opening the trie and stop processing if it fails 284 trie, err := sf.db.OpenTrie(sf.root) 285 if err != nil { 286 log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err) 287 return 288 } 289 sf.trie = trie 290 291 // Trie opened successfully, keep prefetching items 292 for { 293 select { 294 case <-sf.wake: 295 // Subfetcher was woken up, retrieve any tasks to avoid spinning the lock 296 sf.lock.Lock() 297 tasks := sf.tasks 298 sf.tasks = nil 299 sf.lock.Unlock() 300 301 // Prefetch any tasks until the loop is interrupted 302 for i, task := range tasks { 303 select { 304 case <-sf.stop: 305 // If termination is requested, add any leftover back and return 306 sf.lock.Lock() 307 sf.tasks = append(sf.tasks, tasks[i:]...) 308 sf.lock.Unlock() 309 return 310 311 case ch := <-sf.copy: 312 // Somebody wants a copy of the current trie, grant them 313 ch <- sf.db.CopyTrie(sf.trie) 314 315 default: 316 // No termination request yet, prefetch the next entry 317 taskid := string(task) 318 if _, ok := sf.seen[taskid]; ok { 319 sf.dups++ 320 } else { 321 sf.trie.TryGet(task) 322 sf.seen[taskid] = struct{}{} 323 } 324 } 325 } 326 327 case ch := <-sf.copy: 328 // Somebody wants a copy of the current trie, grant them 329 ch <- sf.db.CopyTrie(sf.trie) 330 331 case <-sf.stop: 332 // Termination is requested, abort and leave remaining tasks 333 return 334 } 335 } 336 }