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  }