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