github.com/pure-x-eth/consensus_tm@v0.0.0-20230502163723-e3c2ff987250/p2p/trust/store.go (about) 1 // Copyright 2017 Tendermint. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package trust 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "time" 10 11 dbm "github.com/tendermint/tm-db" 12 13 "github.com/pure-x-eth/consensus_tm/libs/service" 14 tmsync "github.com/pure-x-eth/consensus_tm/libs/sync" 15 ) 16 17 const defaultStorePeriodicSaveInterval = 1 * time.Minute 18 19 var trustMetricKey = []byte("trustMetricStore") 20 21 // MetricStore - Manages all trust metrics for peers 22 type MetricStore struct { 23 service.BaseService 24 25 // Maps a Peer.Key to that peer's TrustMetric 26 peerMetrics map[string]*Metric 27 28 // Mutex that protects the map and history data file 29 mtx tmsync.Mutex 30 31 // The db where peer trust metric history data will be stored 32 db dbm.DB 33 34 // This configuration will be used when creating new TrustMetrics 35 config MetricConfig 36 } 37 38 // NewTrustMetricStore returns a store that saves data to the DB 39 // and uses the config when creating new trust metrics. 40 // Use Start to to initialize the trust metric store 41 func NewTrustMetricStore(db dbm.DB, tmc MetricConfig) *MetricStore { 42 tms := &MetricStore{ 43 peerMetrics: make(map[string]*Metric), 44 db: db, 45 config: tmc, 46 } 47 48 tms.BaseService = *service.NewBaseService(nil, "MetricStore", tms) 49 return tms 50 } 51 52 // OnStart implements Service 53 func (tms *MetricStore) OnStart() error { 54 if err := tms.BaseService.OnStart(); err != nil { 55 return err 56 } 57 58 tms.mtx.Lock() 59 defer tms.mtx.Unlock() 60 61 tms.loadFromDB() 62 go tms.saveRoutine() 63 return nil 64 } 65 66 // OnStop implements Service 67 func (tms *MetricStore) OnStop() { 68 tms.BaseService.OnStop() 69 70 tms.mtx.Lock() 71 defer tms.mtx.Unlock() 72 73 // Stop all trust metric go-routines 74 for _, tm := range tms.peerMetrics { 75 if err := tm.Stop(); err != nil { 76 tms.Logger.Error("unable to stop metric store", "error", err) 77 } 78 } 79 80 // Make the final trust history data save 81 tms.saveToDB() 82 } 83 84 // Size returns the number of entries in the trust metric store 85 func (tms *MetricStore) Size() int { 86 tms.mtx.Lock() 87 defer tms.mtx.Unlock() 88 89 return tms.size() 90 } 91 92 // AddPeerTrustMetric takes an existing trust metric and associates it with a peer key. 93 // The caller is expected to call Start on the TrustMetric being added 94 func (tms *MetricStore) AddPeerTrustMetric(key string, tm *Metric) { 95 tms.mtx.Lock() 96 defer tms.mtx.Unlock() 97 98 if key == "" || tm == nil { 99 return 100 } 101 tms.peerMetrics[key] = tm 102 } 103 104 // GetPeerTrustMetric returns a trust metric by peer key 105 func (tms *MetricStore) GetPeerTrustMetric(key string) *Metric { 106 tms.mtx.Lock() 107 defer tms.mtx.Unlock() 108 109 tm, ok := tms.peerMetrics[key] 110 if !ok { 111 // If the metric is not available, we will create it 112 tm = NewMetricWithConfig(tms.config) 113 if err := tm.Start(); err != nil { 114 tms.Logger.Error("unable to start metric store", "error", err) 115 } 116 // The metric needs to be in the map 117 tms.peerMetrics[key] = tm 118 } 119 return tm 120 } 121 122 // PeerDisconnected pauses the trust metric associated with the peer identified by the key 123 func (tms *MetricStore) PeerDisconnected(key string) { 124 tms.mtx.Lock() 125 defer tms.mtx.Unlock() 126 127 // If the Peer that disconnected has a metric, pause it 128 if tm, ok := tms.peerMetrics[key]; ok { 129 tm.Pause() 130 } 131 } 132 133 // Saves the history data for all peers to the store DB. 134 // This public method acquires the trust metric store lock 135 func (tms *MetricStore) SaveToDB() { 136 tms.mtx.Lock() 137 defer tms.mtx.Unlock() 138 139 tms.saveToDB() 140 } 141 142 /* Private methods */ 143 144 // size returns the number of entries in the store without acquiring the mutex 145 func (tms *MetricStore) size() int { 146 return len(tms.peerMetrics) 147 } 148 149 /* Loading & Saving */ 150 /* Both loadFromDB and savetoDB assume the mutex has been acquired */ 151 152 // Loads the history data for all peers from the store DB 153 // cmn.Panics if file is corrupt 154 func (tms *MetricStore) loadFromDB() bool { 155 // Obtain the history data we have so far 156 bytes, err := tms.db.Get(trustMetricKey) 157 if err != nil { 158 panic(err) 159 } 160 if bytes == nil { 161 return false 162 } 163 164 peers := make(map[string]MetricHistoryJSON) 165 err = json.Unmarshal(bytes, &peers) 166 if err != nil { 167 panic(fmt.Sprintf("Could not unmarshal Trust Metric Store DB data: %v", err)) 168 } 169 170 // If history data exists in the file, 171 // load it into trust metric 172 for key, p := range peers { 173 tm := NewMetricWithConfig(tms.config) 174 175 if err := tm.Start(); err != nil { 176 tms.Logger.Error("unable to start metric", "error", err) 177 } 178 tm.Init(p) 179 // Load the peer trust metric into the store 180 tms.peerMetrics[key] = tm 181 } 182 return true 183 } 184 185 // Saves the history data for all peers to the store DB 186 func (tms *MetricStore) saveToDB() { 187 tms.Logger.Debug("Saving TrustHistory to DB", "size", tms.size()) 188 189 peers := make(map[string]MetricHistoryJSON) 190 191 for key, tm := range tms.peerMetrics { 192 // Add an entry for the peer identified by key 193 peers[key] = tm.HistoryJSON() 194 } 195 196 // Write all the data back to the DB 197 bytes, err := json.Marshal(peers) 198 if err != nil { 199 tms.Logger.Error("Failed to encode the TrustHistory", "err", err) 200 return 201 } 202 if err := tms.db.SetSync(trustMetricKey, bytes); err != nil { 203 tms.Logger.Error("failed to flush data to disk", "error", err) 204 } 205 } 206 207 // Periodically saves the trust history data to the DB 208 func (tms *MetricStore) saveRoutine() { 209 t := time.NewTicker(defaultStorePeriodicSaveInterval) 210 defer t.Stop() 211 loop: 212 for { 213 select { 214 case <-t.C: 215 tms.SaveToDB() 216 case <-tms.Quit(): 217 break loop 218 } 219 } 220 }