github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/nomad/stream/event_buffer_test.go (about)

     1  package stream
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/rand"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/hashicorp/nomad/nomad/structs"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestEventBufferFuzz(t *testing.T) {
    17  	nReaders := 1000
    18  	nMessages := 1000
    19  
    20  	b := newEventBuffer(1000)
    21  
    22  	// Start a write goroutine that will publish 10000 messages with sequential
    23  	// indexes and some jitter in timing (to allow clients to "catch up" and block
    24  	// waiting for updates).
    25  	go func() {
    26  		seed := time.Now().UnixNano()
    27  		t.Logf("Using seed %d", seed)
    28  		// z is a Zipfian distribution that gives us a number of milliseconds to
    29  		// sleep which are mostly low - near zero but occasionally spike up to near
    30  		// 100.
    31  		z := rand.NewZipf(rand.New(rand.NewSource(seed)), 1.5, 1.5, 50)
    32  
    33  		for i := 0; i < nMessages; i++ {
    34  			// Event content is arbitrary and not valid for our use of buffers in
    35  			// streaming - here we only care about the semantics of the buffer.
    36  			e := structs.Event{
    37  				Index: uint64(i), // Indexes should be contiguous
    38  			}
    39  			b.Append(&structs.Events{Index: uint64(i), Events: []structs.Event{e}})
    40  			// Sleep sometimes for a while to let some subscribers catch up
    41  			wait := time.Duration(z.Uint64()) * time.Millisecond
    42  			time.Sleep(wait)
    43  		}
    44  	}()
    45  
    46  	// Run n subscribers following and verifying
    47  	errCh := make(chan error, nReaders)
    48  
    49  	// Load head here so all subscribers start from the same point or they might
    50  	// not run until several appends have already happened.
    51  	head := b.Head()
    52  
    53  	for i := 0; i < nReaders; i++ {
    54  		go func(i int) {
    55  			expect := uint64(0)
    56  			item := head
    57  			var err error
    58  			for {
    59  				item, err = item.Next(context.Background(), nil)
    60  				if err != nil {
    61  					errCh <- fmt.Errorf("subscriber %05d failed getting next %d: %s", i,
    62  						expect, err)
    63  					return
    64  				}
    65  				if item.Events.Events[0].Index != expect {
    66  					errCh <- fmt.Errorf("subscriber %05d got bad event want=%d, got=%d", i,
    67  						expect, item.Events.Events[0].Index)
    68  					return
    69  				}
    70  				expect++
    71  				if expect == uint64(nMessages) {
    72  					// Succeeded
    73  					errCh <- nil
    74  					return
    75  				}
    76  			}
    77  		}(i)
    78  	}
    79  
    80  	// Wait for all readers to finish one way or other
    81  	for i := 0; i < nReaders; i++ {
    82  		err := <-errCh
    83  		assert.NoError(t, err)
    84  	}
    85  }
    86  
    87  func TestEventBuffer_Slow_Reader(t *testing.T) {
    88  	b := newEventBuffer(10)
    89  
    90  	for i := 1; i < 11; i++ {
    91  		e := structs.Event{
    92  			Index: uint64(i), // Indexes should be contiguous
    93  		}
    94  		b.Append(&structs.Events{Index: uint64(i), Events: []structs.Event{e}})
    95  	}
    96  
    97  	require.Equal(t, 10, b.Len())
    98  
    99  	head := b.Head()
   100  
   101  	for i := 10; i < 15; i++ {
   102  		e := structs.Event{
   103  			Index: uint64(i), // Indexes should be contiguous
   104  		}
   105  		b.Append(&structs.Events{Index: uint64(i), Events: []structs.Event{e}})
   106  	}
   107  
   108  	// Ensure the slow reader errors to handle dropped events and
   109  	// fetch latest head
   110  	ev, err := head.Next(context.Background(), nil)
   111  	require.Error(t, err)
   112  	require.Nil(t, ev)
   113  
   114  	newHead := b.Head()
   115  	require.Equal(t, 5, int(newHead.Events.Index))
   116  }
   117  
   118  func TestEventBuffer_Size(t *testing.T) {
   119  	b := newEventBuffer(100)
   120  
   121  	for i := 0; i < 10; i++ {
   122  		e := structs.Event{
   123  			Index: uint64(i), // Indexes should be contiguous
   124  		}
   125  		b.Append(&structs.Events{Index: uint64(i), Events: []structs.Event{e}})
   126  	}
   127  
   128  	require.Equal(t, 10, b.Len())
   129  }
   130  
   131  func TestEventBuffer_MaxSize(t *testing.T) {
   132  	b := newEventBuffer(10)
   133  
   134  	var events []structs.Event
   135  	for i := 0; i < 100; i++ {
   136  		events = append(events, structs.Event{})
   137  	}
   138  
   139  	b.Append(&structs.Events{Index: uint64(1), Events: events})
   140  	require.Equal(t, 1, b.Len())
   141  }
   142  
   143  // TestEventBuffer_Emptying_Buffer tests the behavior when all items
   144  // are removed, the event buffer should advance its head down to the last message
   145  // and insert a placeholder sentinel value.
   146  func TestEventBuffer_Emptying_Buffer(t *testing.T) {
   147  	b := newEventBuffer(10)
   148  
   149  	for i := 0; i < 10; i++ {
   150  		e := structs.Event{
   151  			Index: uint64(i), // Indexes should be contiguous
   152  		}
   153  		b.Append(&structs.Events{Index: uint64(i), Events: []structs.Event{e}})
   154  	}
   155  
   156  	require.Equal(t, 10, int(b.Len()))
   157  
   158  	// empty the buffer, which will bring the event buffer down
   159  	// to a single sentinel value
   160  	for i := 0; i < 16; i++ {
   161  		b.advanceHead()
   162  	}
   163  
   164  	// head and tail are now a sentinel value
   165  	head := b.Head()
   166  	tail := b.Tail()
   167  	require.Equal(t, 0, int(head.Events.Index))
   168  	require.Equal(t, 0, b.Len())
   169  	require.Equal(t, head, tail)
   170  
   171  	e := structs.Event{
   172  		Index: uint64(100),
   173  	}
   174  	b.Append(&structs.Events{Index: uint64(100), Events: []structs.Event{e}})
   175  
   176  	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Second))
   177  	defer cancel()
   178  
   179  	next, err := head.Next(ctx, make(chan struct{}))
   180  	require.NoError(t, err)
   181  	require.NotNil(t, next)
   182  	require.Equal(t, uint64(100), next.Events.Index)
   183  
   184  }
   185  
   186  func TestEventBuffer_StartAt_CurrentIdx_Past_Start(t *testing.T) {
   187  	cases := []struct {
   188  		desc     string
   189  		req      uint64
   190  		expected uint64
   191  		offset   int
   192  	}{
   193  		{
   194  			desc:     "requested index less than head receives head",
   195  			req:      10,
   196  			expected: 11,
   197  			offset:   1,
   198  		},
   199  		{
   200  			desc:     "requested exact match head",
   201  			req:      11,
   202  			expected: 11,
   203  			offset:   0,
   204  		},
   205  		{
   206  			desc:     "requested exact match",
   207  			req:      42,
   208  			expected: 42,
   209  			offset:   0,
   210  		},
   211  		{
   212  			desc:     "requested index greater than tail receives tail",
   213  			req:      500,
   214  			expected: 100,
   215  			offset:   400,
   216  		},
   217  	}
   218  
   219  	// buffer starts at index 11 goes to 100
   220  	b := newEventBuffer(100)
   221  
   222  	for i := 11; i <= 100; i++ {
   223  		e := structs.Event{
   224  			Index: uint64(i), // Indexes should be contiguous
   225  		}
   226  		b.Append(&structs.Events{Index: uint64(i), Events: []structs.Event{e}})
   227  	}
   228  
   229  	for _, tc := range cases {
   230  		t.Run(tc.desc, func(t *testing.T) {
   231  			got, offset := b.StartAtClosest(tc.req)
   232  			require.Equal(t, int(tc.expected), int(got.Events.Index))
   233  			require.Equal(t, tc.offset, offset)
   234  		})
   235  	}
   236  }