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  }