You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
5.0 KiB

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