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