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  }