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.
11 cmn "github.com/tendermint/tmlibs/common"
12 dbm "github.com/tendermint/tmlibs/db"
15 const defaultStorePeriodicSaveInterval = 1 * time.Minute
17 var trustMetricKey = []byte("trustMetricStore")
19 // TrustMetricStore - Manages all trust metrics for peers
20 type TrustMetricStore struct {
23 // Maps a Peer.Key to that peer's TrustMetric
24 peerMetrics map[string]*TrustMetric
26 // Mutex that protects the map and history data file
29 // The db where peer trust metric history data will be stored
32 // This configuration will be used when creating new TrustMetrics
33 config TrustMetricConfig
36 // NewTrustMetricStore returns a store that saves data to the DB
37 // and uses the config when creating new trust metrics.
38 // Use Start to to initialize the trust metric store
39 func NewTrustMetricStore(db dbm.DB, tmc TrustMetricConfig) *TrustMetricStore {
40 tms := &TrustMetricStore{
41 peerMetrics: make(map[string]*TrustMetric),
46 tms.BaseService = *cmn.NewBaseService(nil, "TrustMetricStore", tms)
50 // OnStart implements Service
51 func (tms *TrustMetricStore) OnStart() error {
52 if err := tms.BaseService.OnStart(); err != nil {
57 defer tms.mtx.Unlock()
64 // OnStop implements Service
65 func (tms *TrustMetricStore) OnStop() {
66 tms.BaseService.OnStop()
69 defer tms.mtx.Unlock()
71 // Stop all trust metric go-routines
72 for _, tm := range tms.peerMetrics {
76 // Make the final trust history data save
80 // Size returns the number of entries in the trust metric store
81 func (tms *TrustMetricStore) Size() int {
83 defer tms.mtx.Unlock()
88 // AddPeerTrustMetric takes an existing trust metric and associates it with a peer key.
89 // The caller is expected to call Start on the TrustMetric being added
90 func (tms *TrustMetricStore) AddPeerTrustMetric(key string, tm *TrustMetric) {
92 defer tms.mtx.Unlock()
94 if key == "" || tm == nil {
97 tms.peerMetrics[key] = tm
100 // GetPeerTrustMetric returns a trust metric by peer key
101 func (tms *TrustMetricStore) GetPeerTrustMetric(key string) *TrustMetric {
103 defer tms.mtx.Unlock()
105 tm, ok := tms.peerMetrics[key]
107 // If the metric is not available, we will create it
108 tm = NewMetricWithConfig(tms.config)
110 // The metric needs to be in the map
111 tms.peerMetrics[key] = tm
116 // PeerDisconnected pauses the trust metric associated with the peer identified by the key
117 func (tms *TrustMetricStore) PeerDisconnected(key string) {
119 defer tms.mtx.Unlock()
121 // If the Peer that disconnected has a metric, pause it
122 if tm, ok := tms.peerMetrics[key]; ok {
127 // Saves the history data for all peers to the store DB.
128 // This public method acquires the trust metric store lock
129 func (tms *TrustMetricStore) SaveToDB() {
131 defer tms.mtx.Unlock()
136 /* Private methods */
138 // size returns the number of entries in the store without acquiring the mutex
139 func (tms *TrustMetricStore) size() int {
140 return len(tms.peerMetrics)
143 /* Loading & Saving */
144 /* Both loadFromDB and savetoDB assume the mutex has been acquired */
146 // Loads the history data for all peers from the store DB
147 // cmn.Panics if file is corrupt
148 func (tms *TrustMetricStore) loadFromDB() bool {
149 // Obtain the history data we have so far
150 bytes := tms.db.Get(trustMetricKey)
155 peers := make(map[string]MetricHistoryJSON)
156 err := json.Unmarshal(bytes, &peers)
158 cmn.PanicCrisis(cmn.Fmt("Could not unmarshal Trust Metric Store DB data: %v", err))
161 // If history data exists in the file,
162 // load it into trust metric
163 for key, p := range peers {
164 tm := NewMetricWithConfig(tms.config)
168 // Load the peer trust metric into the store
169 tms.peerMetrics[key] = tm
174 // Saves the history data for all peers to the store DB
175 func (tms *TrustMetricStore) saveToDB() {
176 tms.Logger.Debug("Saving TrustHistory to DB", "size", tms.size())
178 peers := make(map[string]MetricHistoryJSON)
180 for key, tm := range tms.peerMetrics {
181 // Add an entry for the peer identified by key
182 peers[key] = tm.HistoryJSON()
185 // Write all the data back to the DB
186 bytes, err := json.Marshal(peers)
188 tms.Logger.Error("Failed to encode the TrustHistory", "err", err)
191 tms.db.SetSync(trustMetricKey, bytes)
194 // Periodically saves the trust history data to the DB
195 func (tms *TrustMetricStore) saveRoutine() {
196 t := time.NewTicker(defaultStorePeriodicSaveInterval)