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 }