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