github.com/ewagmig/fabric@v2.1.1+incompatible/gossip/state/payloads_buffer.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package state 8 9 import ( 10 "sync" 11 "sync/atomic" 12 13 proto "github.com/hyperledger/fabric-protos-go/gossip" 14 "github.com/hyperledger/fabric/common/metrics" 15 "github.com/hyperledger/fabric/gossip/util" 16 ) 17 18 // PayloadsBuffer is used to store payloads into which used to 19 // support payloads with blocks reordering according to the 20 // sequence numbers. It also will provide the capability 21 // to signal whenever expected block has arrived. 22 type PayloadsBuffer interface { 23 // Adds new block into the buffer 24 Push(payload *proto.Payload) 25 26 // Returns next expected sequence number 27 Next() uint64 28 29 // Remove and return payload with given sequence number 30 Pop() *proto.Payload 31 32 // Get current buffer size 33 Size() int 34 35 // Channel to indicate event when new payload pushed with sequence 36 // number equal to the next expected value. 37 Ready() chan struct{} 38 39 Close() 40 } 41 42 // PayloadsBufferImpl structure to implement PayloadsBuffer interface 43 // store inner state of available payloads and sequence numbers 44 type PayloadsBufferImpl struct { 45 next uint64 46 47 buf map[uint64]*proto.Payload 48 49 readyChan chan struct{} 50 51 mutex sync.RWMutex 52 53 logger util.Logger 54 } 55 56 // NewPayloadsBuffer is factory function to create new payloads buffer 57 func NewPayloadsBuffer(next uint64) PayloadsBuffer { 58 return &PayloadsBufferImpl{ 59 buf: make(map[uint64]*proto.Payload), 60 readyChan: make(chan struct{}, 1), 61 next: next, 62 logger: util.GetLogger(util.StateLogger, ""), 63 } 64 } 65 66 // Ready function returns the channel which indicates whenever expected 67 // next block has arrived and one could safely pop out 68 // next sequence of blocks 69 func (b *PayloadsBufferImpl) Ready() chan struct{} { 70 return b.readyChan 71 } 72 73 // Push new payload into the buffer structure in case new arrived payload 74 // sequence number is below the expected next block number payload will be 75 // thrown away. 76 // TODO return bool to indicate if payload was added or not, so that caller can log result. 77 func (b *PayloadsBufferImpl) Push(payload *proto.Payload) { 78 b.mutex.Lock() 79 defer b.mutex.Unlock() 80 81 seqNum := payload.SeqNum 82 83 if seqNum < b.next || b.buf[seqNum] != nil { 84 b.logger.Debugf("Payload with sequence number = %d has been already processed", payload.SeqNum) 85 return 86 } 87 88 b.buf[seqNum] = payload 89 90 // Send notification that next sequence has arrived 91 if seqNum == b.next && len(b.readyChan) == 0 { 92 b.readyChan <- struct{}{} 93 } 94 } 95 96 // Next function provides the number of the next expected block 97 func (b *PayloadsBufferImpl) Next() uint64 { 98 // Atomically read the value of the top sequence number 99 return atomic.LoadUint64(&b.next) 100 } 101 102 // Pop function extracts the payload according to the next expected block 103 // number, if no next block arrived yet, function returns nil. 104 func (b *PayloadsBufferImpl) Pop() *proto.Payload { 105 b.mutex.Lock() 106 defer b.mutex.Unlock() 107 108 result := b.buf[b.Next()] 109 110 if result != nil { 111 // If there is such sequence in the buffer need to delete it 112 delete(b.buf, b.Next()) 113 // Increment next expect block index 114 atomic.AddUint64(&b.next, 1) 115 116 b.drainReadChannel() 117 118 } 119 120 return result 121 } 122 123 // drainReadChannel empties ready channel in case last 124 // payload has been poped up and there are still awaiting 125 // notifications in the channel 126 func (b *PayloadsBufferImpl) drainReadChannel() { 127 if len(b.buf) == 0 { 128 for { 129 if len(b.readyChan) > 0 { 130 <-b.readyChan 131 } else { 132 break 133 } 134 } 135 } 136 } 137 138 // Size returns current number of payloads stored within buffer 139 func (b *PayloadsBufferImpl) Size() int { 140 b.mutex.RLock() 141 defer b.mutex.RUnlock() 142 return len(b.buf) 143 } 144 145 // Close cleanups resources and channels in maintained 146 func (b *PayloadsBufferImpl) Close() { 147 close(b.readyChan) 148 } 149 150 type metricsBuffer struct { 151 PayloadsBuffer 152 sizeMetrics metrics.Gauge 153 chainID string 154 } 155 156 func (mb *metricsBuffer) Push(payload *proto.Payload) { 157 mb.PayloadsBuffer.Push(payload) 158 mb.reportSize() 159 } 160 161 func (mb *metricsBuffer) Pop() *proto.Payload { 162 pl := mb.PayloadsBuffer.Pop() 163 mb.reportSize() 164 return pl 165 } 166 167 func (mb *metricsBuffer) reportSize() { 168 mb.sizeMetrics.With("channel", mb.chainID).Set(float64(mb.Size())) 169 }