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 }