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