github.com/phillinzzz/newBsc@v1.1.6/trie/sync_bloom.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 trie 18 19 import ( 20 "encoding/binary" 21 "fmt" 22 "sync" 23 "sync/atomic" 24 "time" 25 26 "github.com/phillinzzz/newBsc/common" 27 "github.com/phillinzzz/newBsc/common/gopool" 28 "github.com/phillinzzz/newBsc/core/rawdb" 29 "github.com/phillinzzz/newBsc/ethdb" 30 "github.com/phillinzzz/newBsc/log" 31 "github.com/phillinzzz/newBsc/metrics" 32 bloomfilter "github.com/holiman/bloomfilter/v2" 33 ) 34 35 var ( 36 bloomAddMeter = metrics.NewRegisteredMeter("trie/bloom/add", nil) 37 bloomLoadMeter = metrics.NewRegisteredMeter("trie/bloom/load", nil) 38 bloomTestMeter = metrics.NewRegisteredMeter("trie/bloom/test", nil) 39 bloomMissMeter = metrics.NewRegisteredMeter("trie/bloom/miss", nil) 40 bloomFaultMeter = metrics.NewRegisteredMeter("trie/bloom/fault", nil) 41 bloomErrorGauge = metrics.NewRegisteredGauge("trie/bloom/error", nil) 42 ) 43 44 // SyncBloom is a bloom filter used during fast sync to quickly decide if a trie 45 // node or contract code already exists on disk or not. It self populates from the 46 // provided disk database on creation in a background thread and will only start 47 // returning live results once that's finished. 48 type SyncBloom struct { 49 bloom *bloomfilter.Filter 50 inited uint32 51 closer sync.Once 52 closed uint32 53 pend sync.WaitGroup 54 } 55 56 // NewSyncBloom creates a new bloom filter of the given size (in megabytes) and 57 // initializes it from the database. The bloom is hard coded to use 3 filters. 58 func NewSyncBloom(memory uint64, database ethdb.Iteratee) *SyncBloom { 59 // Create the bloom filter to track known trie nodes 60 bloom, err := bloomfilter.New(memory*1024*1024*8, 4) 61 if err != nil { 62 panic(fmt.Sprintf("failed to create bloom: %v", err)) 63 } 64 log.Info("Allocated fast sync bloom", "size", common.StorageSize(memory*1024*1024)) 65 66 // Assemble the fast sync bloom and init it from previous sessions 67 b := &SyncBloom{ 68 bloom: bloom, 69 } 70 b.pend.Add(2) 71 gopool.Submit(func() { 72 defer b.pend.Done() 73 b.init(database) 74 }) 75 76 gopool.Submit(func() { 77 defer b.pend.Done() 78 b.meter() 79 }) 80 return b 81 } 82 83 // init iterates over the database, pushing every trie hash into the bloom filter. 84 func (b *SyncBloom) init(database ethdb.Iteratee) { 85 // Iterate over the database, but restart every now and again to avoid holding 86 // a persistent snapshot since fast sync can push a ton of data concurrently, 87 // bloating the disk. 88 // 89 // Note, this is fine, because everything inserted into leveldb by fast sync is 90 // also pushed into the bloom directly, so we're not missing anything when the 91 // iterator is swapped out for a new one. 92 it := database.NewIterator(nil, nil) 93 94 var ( 95 start = time.Now() 96 swap = time.Now() 97 ) 98 for it.Next() && atomic.LoadUint32(&b.closed) == 0 { 99 // If the database entry is a trie node, add it to the bloom 100 key := it.Key() 101 if len(key) == common.HashLength { 102 b.bloom.AddHash(binary.BigEndian.Uint64(key)) 103 bloomLoadMeter.Mark(1) 104 } else if ok, hash := rawdb.IsCodeKey(key); ok { 105 // If the database entry is a contract code, add it to the bloom 106 b.bloom.AddHash(binary.BigEndian.Uint64(hash)) 107 bloomLoadMeter.Mark(1) 108 } 109 // If enough time elapsed since the last iterator swap, restart 110 if time.Since(swap) > 8*time.Second { 111 key := common.CopyBytes(it.Key()) 112 113 it.Release() 114 it = database.NewIterator(nil, key) 115 116 log.Info("Initializing state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability(), "elapsed", common.PrettyDuration(time.Since(start))) 117 swap = time.Now() 118 } 119 } 120 it.Release() 121 122 // Mark the bloom filter inited and return 123 log.Info("Initialized state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability(), "elapsed", common.PrettyDuration(time.Since(start))) 124 atomic.StoreUint32(&b.inited, 1) 125 } 126 127 // meter periodically recalculates the false positive error rate of the bloom 128 // filter and reports it in a metric. 129 func (b *SyncBloom) meter() { 130 for { 131 // Report the current error ration. No floats, lame, scale it up. 132 bloomErrorGauge.Update(int64(b.bloom.FalsePosititveProbability() * 100000)) 133 134 // Wait one second, but check termination more frequently 135 for i := 0; i < 10; i++ { 136 if atomic.LoadUint32(&b.closed) == 1 { 137 return 138 } 139 time.Sleep(100 * time.Millisecond) 140 } 141 } 142 } 143 144 // Close terminates any background initializer still running and releases all the 145 // memory allocated for the bloom. 146 func (b *SyncBloom) Close() error { 147 b.closer.Do(func() { 148 // Ensure the initializer is stopped 149 atomic.StoreUint32(&b.closed, 1) 150 b.pend.Wait() 151 152 // Wipe the bloom, but mark it "uninited" just in case someone attempts an access 153 log.Info("Deallocated state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability()) 154 155 atomic.StoreUint32(&b.inited, 0) 156 b.bloom = nil 157 }) 158 return nil 159 } 160 161 // Add inserts a new trie node hash into the bloom filter. 162 func (b *SyncBloom) Add(hash []byte) { 163 if atomic.LoadUint32(&b.closed) == 1 { 164 return 165 } 166 b.bloom.AddHash(binary.BigEndian.Uint64(hash)) 167 bloomAddMeter.Mark(1) 168 } 169 170 // Contains tests if the bloom filter contains the given hash: 171 // - false: the bloom definitely does not contain hash 172 // - true: the bloom maybe contains hash 173 // 174 // While the bloom is being initialized, any query will return true. 175 func (b *SyncBloom) Contains(hash []byte) bool { 176 bloomTestMeter.Mark(1) 177 if atomic.LoadUint32(&b.inited) == 0 { 178 // We didn't load all the trie nodes from the previous run of Geth yet. As 179 // such, we can't say for sure if a hash is not present for anything. Until 180 // the init is done, we're faking "possible presence" for everything. 181 return true 182 } 183 // Bloom initialized, check the real one and report any successful misses 184 maybe := b.bloom.ContainsHash(binary.BigEndian.Uint64(hash)) 185 if !maybe { 186 bloomMissMeter.Mark(1) 187 } 188 return maybe 189 }