github.com/jimmyx0x/go-ethereum@v1.10.28/les/pruner.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 les
    18  
    19  import (
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/ethereum/go-ethereum/common/math"
    24  	"github.com/ethereum/go-ethereum/core"
    25  	"github.com/ethereum/go-ethereum/ethdb"
    26  	"github.com/ethereum/go-ethereum/log"
    27  )
    28  
    29  // pruner is responsible for pruning historical light chain data.
    30  type pruner struct {
    31  	db       ethdb.Database
    32  	indexers []*core.ChainIndexer
    33  	closeCh  chan struct{}
    34  	wg       sync.WaitGroup
    35  }
    36  
    37  // newPruner returns a light chain pruner instance.
    38  func newPruner(db ethdb.Database, indexers ...*core.ChainIndexer) *pruner {
    39  	pruner := &pruner{
    40  		db:       db,
    41  		indexers: indexers,
    42  		closeCh:  make(chan struct{}),
    43  	}
    44  	pruner.wg.Add(1)
    45  	go pruner.loop()
    46  	return pruner
    47  }
    48  
    49  // close notifies all background goroutines belonging to pruner to exit.
    50  func (p *pruner) close() {
    51  	close(p.closeCh)
    52  	p.wg.Wait()
    53  }
    54  
    55  // loop periodically queries the status of chain indexers and prunes useless
    56  // historical chain data. Notably, whenever Geth restarts, it will iterate
    57  // all historical sections even they don't exist at all(below checkpoint) so
    58  // that light client can prune cached chain data that was ODRed after pruning
    59  // that section.
    60  func (p *pruner) loop() {
    61  	defer p.wg.Done()
    62  
    63  	// cleanTicker is the ticker used to trigger a history clean 2 times a day.
    64  	var cleanTicker = time.NewTicker(12 * time.Hour)
    65  	defer cleanTicker.Stop()
    66  
    67  	// pruning finds the sections that have been processed by all indexers
    68  	// and deletes all historical chain data.
    69  	// Note, if some indexers don't support pruning(e.g. eth.BloomIndexer),
    70  	// pruning operations can be silently ignored.
    71  	pruning := func() {
    72  		min := uint64(math.MaxUint64)
    73  		for _, indexer := range p.indexers {
    74  			sections, _, _ := indexer.Sections()
    75  			if sections < min {
    76  				min = sections
    77  			}
    78  		}
    79  		// Always keep the latest section data in database.
    80  		if min < 2 || len(p.indexers) == 0 {
    81  			return
    82  		}
    83  		for _, indexer := range p.indexers {
    84  			if err := indexer.Prune(min - 2); err != nil {
    85  				log.Debug("Failed to prune historical data", "err", err)
    86  				return
    87  			}
    88  		}
    89  		p.db.Compact(nil, nil) // Compact entire database, ensure all removed data are deleted.
    90  	}
    91  	for {
    92  		pruning()
    93  		select {
    94  		case <-cleanTicker.C:
    95  		case <-p.closeCh:
    96  			return
    97  		}
    98  	}
    99  }