github.com/iotexproject/iotex-core@v1.14.1-rc1/p2p/qos.go (about)

     1  // Copyright (c) 2021 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package p2p
     7  
     8  import (
     9  	"sync"
    10  	"sync/atomic"
    11  	"time"
    12  )
    13  
    14  type (
    15  	// Qos metrics
    16  	Qos struct {
    17  		lock                  sync.RWMutex
    18  		broadcastSendCount    uint64
    19  		broadcastSendSuccess  uint64
    20  		broadcastRecvCount    uint64
    21  		lastActiveBroadcastTs int64 // in nano-second
    22  		lastActiveUnicastTs   int64 // in nano-second
    23  		timeout               time.Duration
    24  		metrics               map[string]*transmitMetric
    25  	}
    26  
    27  	transmitMetric struct {
    28  		unicastSendCount   uint64
    29  		unicastSendSuccess uint64
    30  		unicastRecvCount   uint64
    31  	}
    32  )
    33  
    34  // NewQoS returns the Qos metrics
    35  func NewQoS(now time.Time, timeout time.Duration) *Qos {
    36  	return &Qos{
    37  		lastActiveBroadcastTs: now.UnixNano(),
    38  		lastActiveUnicastTs:   now.UnixNano(),
    39  		timeout:               timeout,
    40  		metrics:               make(map[string]*transmitMetric),
    41  	}
    42  }
    43  
    44  func (q *Qos) lostConnection() bool {
    45  	t := time.Now()
    46  	return q.lastBroadcastTime().Add(q.timeout).Before(t) && q.lastUnicastTime().Add(q.timeout).Before(t)
    47  }
    48  
    49  func (q *Qos) lastBroadcastTime() time.Time {
    50  	return time.Unix(0, atomic.LoadInt64(&q.lastActiveBroadcastTs))
    51  }
    52  
    53  func (q *Qos) lastUnicastTime() time.Time {
    54  	return time.Unix(0, atomic.LoadInt64(&q.lastActiveUnicastTs))
    55  }
    56  
    57  func (q *Qos) updateSendBroadcast(t time.Time, success bool) {
    58  	atomic.AddUint64(&q.broadcastSendCount, 1)
    59  	if success {
    60  		atomic.AddUint64(&q.broadcastSendSuccess, 1)
    61  		atomic.StoreInt64(&q.lastActiveBroadcastTs, t.UnixNano())
    62  	}
    63  }
    64  
    65  func (q *Qos) updateRecvBroadcast(t time.Time) {
    66  	atomic.AddUint64(&q.broadcastRecvCount, 1)
    67  	atomic.StoreInt64(&q.lastActiveBroadcastTs, t.UnixNano())
    68  }
    69  
    70  func (q *Qos) updateSendUnicast(peername string, t time.Time, success bool) {
    71  	q.lock.Lock()
    72  	defer q.lock.Unlock()
    73  	peer, exist := q.metrics[peername]
    74  	if !exist {
    75  		peer = new(transmitMetric)
    76  		q.metrics[peername] = peer
    77  	}
    78  	peer.unicastSendCount++
    79  	if success {
    80  		peer.unicastSendSuccess++
    81  		q.lastActiveUnicastTs = t.UnixNano()
    82  	}
    83  }
    84  
    85  func (q *Qos) updateRecvUnicast(peername string, t time.Time) {
    86  	q.lock.Lock()
    87  	defer q.lock.Unlock()
    88  	peer, exist := q.metrics[peername]
    89  	if !exist {
    90  		peer = new(transmitMetric)
    91  		q.metrics[peername] = peer
    92  	}
    93  	peer.unicastRecvCount++
    94  	q.lastActiveUnicastTs = t.UnixNano()
    95  }
    96  
    97  // BroadcastSendTotal returns the total amount of broadcast sent
    98  func (q *Qos) BroadcastSendTotal() uint64 {
    99  	return atomic.LoadUint64(&q.broadcastSendCount)
   100  }
   101  
   102  // BroadcastSendSuccessRate returns the broadcast send success rate
   103  func (q *Qos) BroadcastSendSuccessRate() float64 {
   104  	success := atomic.LoadUint64(&q.broadcastSendSuccess)
   105  	total := atomic.LoadUint64(&q.broadcastSendCount)
   106  	return float64(success) / float64(total)
   107  }
   108  
   109  // BroadcastRecvTotal returns the total amount of broadcast received
   110  func (q *Qos) BroadcastRecvTotal() uint64 {
   111  	return atomic.LoadUint64(&q.broadcastRecvCount)
   112  }
   113  
   114  // UnicastSendTotal returns the total amount of unicast sent to peer
   115  func (q *Qos) UnicastSendTotal(peername string) (uint64, bool) {
   116  	q.lock.RLock()
   117  	peer, exist := q.metrics[peername]
   118  	if !exist {
   119  		q.lock.RUnlock()
   120  		return 0, false
   121  	}
   122  	q.lock.RUnlock()
   123  	return atomic.LoadUint64(&peer.unicastSendCount), true
   124  }
   125  
   126  // UnicastSendSuccessRate returns the unicast send success rate
   127  func (q *Qos) UnicastSendSuccessRate(peername string) (float64, bool) {
   128  	q.lock.RLock()
   129  	peer, exist := q.metrics[peername]
   130  	if !exist {
   131  		q.lock.RUnlock()
   132  		return 0, false
   133  	}
   134  	q.lock.RUnlock()
   135  	success := atomic.LoadUint64(&peer.unicastSendSuccess)
   136  	total := atomic.LoadUint64(&peer.unicastSendCount)
   137  	return float64(success) / float64(total), true
   138  }
   139  
   140  // UnicastRecvTotal returns the total amount of unicast received from peer
   141  func (q *Qos) UnicastRecvTotal(peername string) (uint64, bool) {
   142  	q.lock.RLock()
   143  	peer, exist := q.metrics[peername]
   144  	if !exist {
   145  		q.lock.RUnlock()
   146  		return 0, false
   147  	}
   148  	q.lock.RUnlock()
   149  	return atomic.LoadUint64(&peer.unicastRecvCount), true
   150  }