github.com/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  }