github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/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/hardtosaygoodbye/go-ethereum/common/math" 24 "github.com/hardtosaygoodbye/go-ethereum/core" 25 "github.com/hardtosaygoodbye/go-ethereum/ethdb" 26 "github.com/hardtosaygoodbye/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 }