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  }