github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/les/pruner.go (about)

     1  // Copyright 2019 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/kisexp/xdchain/common/math"
    24  	"github.com/kisexp/xdchain/core"
    25  	"github.com/kisexp/xdchain/ethdb"
    26  	"github.com/kisexp/xdchain/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  
    66  	// pruning finds the sections that have been processed by all indexers
    67  	// and deletes all historical chain data.
    68  	// Note, if some indexers don't support pruning(e.g. eth.BloomIndexer),
    69  	// pruning operations can be silently ignored.
    70  	pruning := func() {
    71  		min := uint64(math.MaxUint64)
    72  		for _, indexer := range p.indexers {
    73  			sections, _, _ := indexer.Sections()
    74  			if sections < min {
    75  				min = sections
    76  			}
    77  		}
    78  		// Always keep the latest section data in database.
    79  		if min < 2 || len(p.indexers) == 0 {
    80  			return
    81  		}
    82  		for _, indexer := range p.indexers {
    83  			if err := indexer.Prune(min - 2); err != nil {
    84  				log.Debug("Failed to prune historical data", "err", err)
    85  				return
    86  			}
    87  		}
    88  		p.db.Compact(nil, nil) // Compact entire database, ensure all removed data are deleted.
    89  	}
    90  	for {
    91  		pruning()
    92  		select {
    93  		case <-cleanTicker.C:
    94  		case <-p.closeCh:
    95  			return
    96  		}
    97  	}
    98  }