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