github.com/ewagmig/fabric@v2.1.1+incompatible/gossip/state/payloads_buffer_test.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 "crypto/rand" 11 "sync" 12 "sync/atomic" 13 "testing" 14 "time" 15 16 proto "github.com/hyperledger/fabric-protos-go/gossip" 17 "github.com/hyperledger/fabric/gossip/util" 18 "github.com/stretchr/testify/assert" 19 ) 20 21 func init() { 22 util.SetupTestLogging() 23 } 24 25 func randomPayloadWithSeqNum(seqNum uint64) (*proto.Payload, error) { 26 data := make([]byte, 64) 27 _, err := rand.Read(data) 28 if err != nil { 29 return nil, err 30 } 31 return &proto.Payload{ 32 SeqNum: seqNum, 33 Data: data, 34 }, nil 35 } 36 37 func TestNewPayloadsBuffer(t *testing.T) { 38 payloadsBuffer := NewPayloadsBuffer(10) 39 assert.Equal(t, payloadsBuffer.Next(), uint64(10)) 40 } 41 42 func TestPayloadsBufferImpl_Push(t *testing.T) { 43 buffer := NewPayloadsBuffer(5) 44 45 payload, err := randomPayloadWithSeqNum(4) 46 47 if err != nil { 48 t.Fatal("Wasn't able to generate random payload for test") 49 } 50 51 t.Log("Pushing new payload into buffer") 52 buffer.Push(payload) 53 54 // Payloads with sequence number less than buffer top 55 // index should not be accepted 56 t.Log("Getting next block sequence number") 57 assert.Equal(t, buffer.Next(), uint64(5)) 58 t.Log("Check block buffer size") 59 assert.Equal(t, buffer.Size(), 0) 60 61 // Adding new payload with seq. number equal to top 62 // payload should not be added 63 payload, err = randomPayloadWithSeqNum(5) 64 if err != nil { 65 t.Fatal("Wasn't able to generate random payload for test") 66 } 67 68 t.Log("Pushing new payload into buffer") 69 buffer.Push(payload) 70 t.Log("Getting next block sequence number") 71 assert.Equal(t, buffer.Next(), uint64(5)) 72 t.Log("Check block buffer size") 73 assert.Equal(t, buffer.Size(), 1) 74 } 75 76 func TestPayloadsBufferImpl_Ready(t *testing.T) { 77 fin := make(chan struct{}) 78 buffer := NewPayloadsBuffer(1) 79 assert.Equal(t, buffer.Next(), uint64(1)) 80 81 go func() { 82 <-buffer.Ready() 83 fin <- struct{}{} 84 }() 85 86 time.AfterFunc(100*time.Millisecond, func() { 87 payload, err := randomPayloadWithSeqNum(1) 88 89 if err != nil { 90 t.Fatal("Wasn't able to generate random payload for test") 91 } 92 buffer.Push(payload) 93 }) 94 95 select { 96 case <-fin: 97 payload := buffer.Pop() 98 assert.Equal(t, payload.SeqNum, uint64(1)) 99 case <-time.After(500 * time.Millisecond): 100 t.Fail() 101 } 102 } 103 104 // Test to push several concurrent blocks into the buffer 105 // with same sequence number, only one expected to succeed 106 func TestPayloadsBufferImpl_ConcurrentPush(t *testing.T) { 107 108 // Test setup, next block num to expect and 109 // how many concurrent pushes to simulate 110 nextSeqNum := uint64(7) 111 concurrency := 10 112 113 buffer := NewPayloadsBuffer(nextSeqNum) 114 assert.Equal(t, buffer.Next(), uint64(nextSeqNum)) 115 116 startWG := sync.WaitGroup{} 117 startWG.Add(1) 118 119 finishWG := sync.WaitGroup{} 120 finishWG.Add(concurrency) 121 122 payload, err := randomPayloadWithSeqNum(nextSeqNum) 123 assert.NoError(t, err) 124 125 ready := int32(0) 126 readyWG := sync.WaitGroup{} 127 readyWG.Add(1) 128 go func() { 129 // Wait for next expected block to arrive 130 <-buffer.Ready() 131 atomic.AddInt32(&ready, 1) 132 readyWG.Done() 133 }() 134 135 for i := 0; i < concurrency; i++ { 136 go func() { 137 buffer.Push(payload) 138 startWG.Wait() 139 finishWG.Done() 140 }() 141 } 142 startWG.Done() 143 finishWG.Wait() 144 145 readyWG.Wait() 146 assert.Equal(t, int32(1), atomic.LoadInt32(&ready)) 147 // Buffer size has to be only one 148 assert.Equal(t, 1, buffer.Size()) 149 } 150 151 // Tests the scenario where payload pushes and pops are interleaved after a Ready() signal. 152 func TestPayloadsBufferImpl_Interleave(t *testing.T) { 153 buffer := NewPayloadsBuffer(1) 154 assert.Equal(t, buffer.Next(), uint64(1)) 155 156 // 157 // First two sequences arrives and the buffer is emptied without interleave. 158 // 159 // This is also an example of the produce/consumer pattern in Fabric. 160 // Producer: 161 // 162 // Payloads are pushed into the buffer. These payloads can be out of order. 163 // When the buffer has a sequence of payloads ready (in order), it fires a signal 164 // on it's Ready() channel. 165 // 166 // The consumer waits for the signal and then drains all ready payloads. 167 168 payload, err := randomPayloadWithSeqNum(1) 169 assert.NoError(t, err, "generating random payload failed") 170 buffer.Push(payload) 171 172 payload, err = randomPayloadWithSeqNum(2) 173 assert.NoError(t, err, "generating random payload failed") 174 buffer.Push(payload) 175 176 select { 177 case <-buffer.Ready(): 178 case <-time.After(500 * time.Millisecond): 179 t.Error("buffer wasn't ready after 500 ms for first sequence") 180 } 181 182 // The consumer empties the buffer. 183 for payload := buffer.Pop(); payload != nil; payload = buffer.Pop() { 184 } 185 186 // The buffer isn't ready since no new sequences have come since emptying the buffer. 187 select { 188 case <-buffer.Ready(): 189 t.Error("buffer should not be ready as no new sequences have come") 190 case <-time.After(500 * time.Millisecond): 191 } 192 193 // 194 // Next sequences are incoming at the same time the buffer is being emptied by the consumer. 195 // 196 payload, err = randomPayloadWithSeqNum(3) 197 assert.NoError(t, err, "generating random payload failed") 198 buffer.Push(payload) 199 200 select { 201 case <-buffer.Ready(): 202 case <-time.After(500 * time.Millisecond): 203 t.Error("buffer wasn't ready after 500 ms for second sequence") 204 } 205 payload = buffer.Pop() 206 assert.NotNil(t, payload, "payload should not be nil") 207 208 // ... Block processing now happens on sequence 3. 209 210 // In the mean time, sequence 4 is pushed into the queue. 211 payload, err = randomPayloadWithSeqNum(4) 212 assert.NoError(t, err, "generating random payload failed") 213 buffer.Push(payload) 214 215 // ... Block processing completes on sequence 3, the consumer loop grabs the next one (4). 216 payload = buffer.Pop() 217 assert.NotNil(t, payload, "payload should not be nil") 218 219 // In the mean time, sequence 5 is pushed into the queue. 220 payload, err = randomPayloadWithSeqNum(5) 221 assert.NoError(t, err, "generating random payload failed") 222 buffer.Push(payload) 223 224 // ... Block processing completes on sequence 4, the consumer loop grabs the next one (5). 225 payload = buffer.Pop() 226 assert.NotNil(t, payload, "payload should not be nil") 227 228 // 229 // Now we see that goroutines are building up due to the interleaved push and pops above. 230 // 231 select { 232 case <-buffer.Ready(): 233 // 234 // Should be error - no payloads are ready 235 // 236 t.Log("buffer ready (1) -- should be error") 237 t.Fail() 238 case <-time.After(500 * time.Millisecond): 239 t.Log("buffer not ready (1)") 240 } 241 payload = buffer.Pop() 242 t.Logf("payload: %v", payload) 243 assert.Nil(t, payload, "payload should be nil") 244 245 select { 246 case <-buffer.Ready(): 247 // 248 // Should be error - no payloads are ready 249 // 250 t.Log("buffer ready (2) -- should be error") 251 t.Fail() 252 case <-time.After(500 * time.Millisecond): 253 t.Log("buffer not ready (2)") 254 } 255 payload = buffer.Pop() 256 assert.Nil(t, payload, "payload should be nil") 257 t.Logf("payload: %v", payload) 258 259 select { 260 case <-buffer.Ready(): 261 t.Error("buffer ready (3)") 262 case <-time.After(500 * time.Millisecond): 263 t.Log("buffer not ready (3) -- good") 264 } 265 }