github.com/core-coin/go-core/v2@v2.1.9/les/pruner.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package les
    18  
    19  import (
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/core-coin/go-core/v2/xcbdb"
    24  
    25  	"github.com/core-coin/go-core/v2/common/math"
    26  	"github.com/core-coin/go-core/v2/core"
    27  	"github.com/core-coin/go-core/v2/log"
    28  )
    29  
    30  // pruner is responsible for pruning historical light chain data.
    31  type pruner struct {
    32  	db       xcbdb.Database
    33  	indexers []*core.ChainIndexer
    34  	closeCh  chan struct{}
    35  	wg       sync.WaitGroup
    36  }
    37  
    38  // newPruner returns a light chain pruner instance.
    39  func newPruner(db xcbdb.Database, indexers ...*core.ChainIndexer) *pruner {
    40  	pruner := &pruner{
    41  		db:       db,
    42  		indexers: indexers,
    43  		closeCh:  make(chan struct{}),
    44  	}
    45  	pruner.wg.Add(1)
    46  	go pruner.loop()
    47  	return pruner
    48  }
    49  
    50  // close notifies all background goroutines belonging to pruner to exit.
    51  func (p *pruner) close() {
    52  	close(p.closeCh)
    53  	p.wg.Wait()
    54  }
    55  
    56  // loop periodically queries the status of chain indexers and prunes useless
    57  // historical chain data. Notably, whenever Gocore restarts, it will iterate
    58  // all historical sections even they don't exist at all(below checkpoint) so
    59  // that light client can prune cached chain data that was ODRed after pruning
    60  // that section.
    61  func (p *pruner) loop() {
    62  	defer p.wg.Done()
    63  
    64  	// cleanTicker is the ticker used to trigger a history clean 2 times a day.
    65  	var cleanTicker = time.NewTicker(12 * time.Hour)
    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. xcb.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  }