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  }