github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/msg/producer/writer/message_writer_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package writer
    22  
    23  import (
    24  	"net"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/fortytw2/leaktest"
    30  	"github.com/golang/mock/gomock"
    31  	"github.com/stretchr/testify/require"
    32  	"github.com/uber-go/tally"
    33  
    34  	"github.com/m3db/m3/src/msg/producer"
    35  	"github.com/m3db/m3/src/x/instrument"
    36  	"github.com/m3db/m3/src/x/retry"
    37  	xtest "github.com/m3db/m3/src/x/test"
    38  )
    39  
    40  func TestMessageWriterRandomIndex(t *testing.T) {
    41  	indexes := make([]int, 10)
    42  	reset := func() {
    43  		for i := range indexes {
    44  			indexes[i] = i
    45  		}
    46  	}
    47  
    48  	reset()
    49  	firstIdx := randIndex(indexes, len(indexes)-1)
    50  	for {
    51  		reset()
    52  		newIdx := randIndex(indexes, len(indexes)-1)
    53  		// Make sure the first index is random.
    54  		if firstIdx != newIdx {
    55  			break
    56  		}
    57  		time.Sleep(50 * time.Millisecond)
    58  	}
    59  
    60  	reset()
    61  	idx1 := randIndex(indexes, len(indexes)-1)
    62  	idx2 := randIndex(indexes, len(indexes)-2)
    63  	for {
    64  		reset()
    65  		newIdx1 := randIndex(indexes, len(indexes)-1)
    66  		newIdx2 := randIndex(indexes, len(indexes)-2)
    67  		// Make sure the order is random.
    68  		if idx2-idx1 != newIdx2-newIdx1 {
    69  			break
    70  		}
    71  		time.Sleep(50 * time.Millisecond)
    72  	}
    73  }
    74  
    75  func TestMessageWriterRandomFullIteration(t *testing.T) {
    76  	indexes := make([]int, 100)
    77  	var indexMap map[int]struct{}
    78  	reset := func() {
    79  		for i := range indexes {
    80  			indexes[i] = i
    81  		}
    82  		indexMap = make(map[int]struct{}, 100)
    83  	}
    84  
    85  	for n := 0; n < 1000; n++ {
    86  		reset()
    87  		for i := len(indexes) - 1; i >= 0; i-- {
    88  			idx := randIndex(indexes, i)
    89  			indexMap[idx] = struct{}{}
    90  		}
    91  		require.Equal(t, 100, len(indexMap))
    92  	}
    93  }
    94  
    95  func TestMessageWriter(t *testing.T) {
    96  	defer leaktest.Check(t)()
    97  
    98  	lis, err := net.Listen("tcp", "127.0.0.1:0")
    99  	require.NoError(t, err)
   100  	defer lis.Close()
   101  
   102  	addr := lis.Addr().String()
   103  	opts := testOptions()
   104  
   105  	var wg sync.WaitGroup
   106  	defer wg.Wait()
   107  
   108  	wg.Add(1)
   109  	go func() {
   110  		testConsumeAndAckOnConnectionListener(t, lis, opts.EncoderOptions(), opts.DecoderOptions())
   111  		wg.Done()
   112  	}()
   113  
   114  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   115  	require.Equal(t, 200, int(w.ReplicatedShardID()))
   116  	w.Init()
   117  
   118  	a := newAckRouter(1)
   119  	a.Register(200, w)
   120  
   121  	cw := newConsumerWriter(addr, a, opts, testConsumerWriterMetrics())
   122  	cw.Init()
   123  	defer cw.Close()
   124  
   125  	w.AddConsumerWriter(cw)
   126  
   127  	ctrl := xtest.NewController(t)
   128  	defer ctrl.Finish()
   129  
   130  	mm1 := producer.NewMockMessage(ctrl)
   131  	mm1.EXPECT().Bytes().Return([]byte("foo")).Times(1)
   132  	mm1.EXPECT().Size().Return(3).Times(1)
   133  	mm1.EXPECT().Finalize(producer.Consumed)
   134  
   135  	w.Write(producer.NewRefCountedMessage(mm1, nil))
   136  
   137  	for {
   138  		w.RLock()
   139  		l := w.queue.Len()
   140  		w.RUnlock()
   141  		if l == 0 {
   142  			break
   143  		}
   144  		time.Sleep(100 * time.Millisecond)
   145  	}
   146  	require.Equal(t, 0, w.queue.Len())
   147  	w.RemoveConsumerWriter(addr)
   148  
   149  	mm2 := producer.NewMockMessage(ctrl)
   150  	mm2.EXPECT().Bytes().Return([]byte("bar")).Times(1)
   151  	mm2.EXPECT().Size().Return(3).Times(1)
   152  
   153  	w.Write(producer.NewRefCountedMessage(mm2, nil))
   154  	for {
   155  		if !isEmptyWithLock(w.acks) {
   156  			break
   157  		}
   158  		time.Sleep(100 * time.Millisecond)
   159  	}
   160  	require.Equal(t, 1, w.queue.Len())
   161  
   162  	mm2.EXPECT().Finalize(producer.Consumed)
   163  	w.Ack(metadata{metadataKey: metadataKey{shard: 200, id: 2}})
   164  	require.True(t, isEmptyWithLock(w.acks))
   165  	for {
   166  		w.RLock()
   167  		l := w.queue.Len()
   168  		w.RUnlock()
   169  		if l == 0 {
   170  			break
   171  		}
   172  		time.Sleep(100 * time.Millisecond)
   173  	}
   174  	w.Close()
   175  	w.Close()
   176  }
   177  
   178  func TestMessageWriterRetry(t *testing.T) {
   179  	defer leaktest.Check(t)()
   180  
   181  	lis, err := net.Listen("tcp", "127.0.0.1:0")
   182  	require.NoError(t, err)
   183  	defer lis.Close()
   184  
   185  	addr := lis.Addr().String()
   186  	opts := testOptions()
   187  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   188  	w.Init()
   189  	defer w.Close()
   190  
   191  	a := newAckRouter(1)
   192  	a.Register(200, w)
   193  
   194  	ctrl := xtest.NewController(t)
   195  	defer ctrl.Finish()
   196  
   197  	mm := producer.NewMockMessage(ctrl)
   198  	mm.EXPECT().Bytes().Return([]byte("foo")).AnyTimes()
   199  	mm.EXPECT().Size().Return(3).Times(1)
   200  	mm.EXPECT().Finalize(producer.Consumed)
   201  
   202  	rm := producer.NewRefCountedMessage(mm, nil)
   203  	w.Write(rm)
   204  
   205  	w.AddConsumerWriter(newConsumerWriter("bad", a, opts, testConsumerWriterMetrics()))
   206  	require.Equal(t, 1, w.queue.Len())
   207  
   208  	for {
   209  		if !isEmptyWithLock(w.acks) {
   210  			break
   211  		}
   212  		time.Sleep(100 * time.Millisecond)
   213  	}
   214  
   215  	require.Equal(t, 1, w.acks.size())
   216  	w.acks.mtx.Lock()
   217  	_, ok := w.acks.acks[uint64(1)]
   218  	require.True(t, ok)
   219  	w.acks.mtx.Unlock()
   220  
   221  	cw := newConsumerWriter(addr, a, opts, testConsumerWriterMetrics())
   222  	cw.Init()
   223  	defer cw.Close()
   224  
   225  	w.AddConsumerWriter(cw)
   226  	go func() {
   227  		testConsumeAndAckOnConnectionListener(t, lis, opts.EncoderOptions(), opts.DecoderOptions())
   228  	}()
   229  
   230  	for {
   231  		w.Lock()
   232  		l := w.queue.Len()
   233  		w.Unlock()
   234  		if l == 0 {
   235  			break
   236  		}
   237  		time.Sleep(100 * time.Millisecond)
   238  	}
   239  }
   240  
   241  func TestMessageWriterCleanupDroppedMessage(t *testing.T) {
   242  	defer leaktest.Check(t)()
   243  
   244  	opts := testOptions()
   245  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   246  
   247  	ctrl := xtest.NewController(t)
   248  	defer ctrl.Finish()
   249  
   250  	mm := producer.NewMockMessage(ctrl)
   251  
   252  	mm.EXPECT().Size().Return(3).Times(1)
   253  	rm := producer.NewRefCountedMessage(mm, nil)
   254  	mm.EXPECT().Finalize(producer.Dropped)
   255  	rm.Drop()
   256  	mm.EXPECT().Bytes().Return([]byte("foo"))
   257  	w.Write(rm)
   258  
   259  	// A get will allocate a new message because the old one has not been returned to pool yet.
   260  	m := w.mPool.Get()
   261  	require.Nil(t, m.RefCountedMessage)
   262  
   263  	require.Equal(t, 1, w.queue.Len())
   264  	w.Init()
   265  	defer w.Close()
   266  
   267  	for {
   268  		w.Lock()
   269  		l := w.queue.Len()
   270  		w.Unlock()
   271  		if l != 1 {
   272  			break
   273  		}
   274  		time.Sleep(100 * time.Millisecond)
   275  	}
   276  	require.True(t, isEmptyWithLock(w.acks))
   277  }
   278  
   279  func TestMessageWriterCleanupAckedMessage(t *testing.T) {
   280  	defer leaktest.Check(t)()
   281  
   282  	opts := testOptions()
   283  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   284  	w.Init()
   285  	defer w.Close()
   286  
   287  	ctrl := xtest.NewController(t)
   288  	defer ctrl.Finish()
   289  
   290  	mm := producer.NewMockMessage(ctrl)
   291  	mm.EXPECT().Bytes().Return([]byte("foo"))
   292  	mm.EXPECT().Size().Return(3).Times(1)
   293  
   294  	rm := producer.NewRefCountedMessage(mm, nil)
   295  	// Another message write also holds this message.
   296  	rm.IncRef()
   297  
   298  	w.Write(rm)
   299  	for {
   300  		if !isEmptyWithLock(w.acks) {
   301  			break
   302  		}
   303  		time.Sleep(100 * time.Millisecond)
   304  	}
   305  	acks := w.acks
   306  	meta := metadata{
   307  		metadataKey: metadataKey{
   308  			id:    1,
   309  			shard: 200,
   310  		},
   311  	}
   312  	// The message will not be finalized because it's still being hold by another message writer.
   313  	acks.ack(meta)
   314  	require.True(t, isEmptyWithLock(w.acks))
   315  
   316  	// A get will allocate a new message because the old one has not been returned to pool yet.
   317  	m := w.mPool.Get()
   318  	require.Nil(t, m.RefCountedMessage)
   319  	require.Equal(t, 1, w.queue.Len())
   320  
   321  	for {
   322  		w.Lock()
   323  		l := w.queue.Len()
   324  		w.Unlock()
   325  		if l != 1 {
   326  			break
   327  		}
   328  		time.Sleep(100 * time.Millisecond)
   329  	}
   330  }
   331  
   332  func TestMessageWriterCutoverCutoff(t *testing.T) {
   333  	ctrl := xtest.NewController(t)
   334  	defer ctrl.Finish()
   335  
   336  	w := newMessageWriter(200, newMessagePool(), nil, testMessageWriterMetrics())
   337  	now := time.Now()
   338  	w.nowFn = func() time.Time { return now }
   339  	met := w.Metrics()
   340  	require.True(t, w.isValidWriteWithLock(now.UnixNano(), met))
   341  	require.True(t, w.isValidWriteWithLock(now.UnixNano()+150, met))
   342  	require.True(t, w.isValidWriteWithLock(now.UnixNano()+250, met))
   343  	require.True(t, w.isValidWriteWithLock(now.UnixNano()+50, met))
   344  
   345  	w.SetCutoffNanos(now.UnixNano() + 200)
   346  	w.SetCutoverNanos(now.UnixNano() + 100)
   347  	require.True(t, w.isValidWriteWithLock(now.UnixNano()+150, met))
   348  	require.False(t, w.isValidWriteWithLock(now.UnixNano()+250, met))
   349  	require.False(t, w.isValidWriteWithLock(now.UnixNano()+50, met))
   350  	require.Equal(t, 0, w.queue.Len())
   351  
   352  	mm := producer.NewMockMessage(ctrl)
   353  	mm.EXPECT().Size().Return(3)
   354  	w.Write(producer.NewRefCountedMessage(mm, nil))
   355  	require.Equal(t, 0, w.queue.Len())
   356  }
   357  
   358  func TestMessageWriterIgnoreCutoverCutoff(t *testing.T) {
   359  	ctrl := xtest.NewController(t)
   360  	defer ctrl.Finish()
   361  
   362  	opts := NewOptions().SetIgnoreCutoffCutover(true)
   363  
   364  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   365  	now := time.Now()
   366  	w.nowFn = func() time.Time { return now }
   367  
   368  	w.SetCutoffNanos(now.UnixNano() + 200)
   369  	w.SetCutoverNanos(now.UnixNano() + 100)
   370  	met := w.Metrics()
   371  	require.True(t, w.isValidWriteWithLock(now.UnixNano()+150, met))
   372  	require.True(t, w.isValidWriteWithLock(now.UnixNano()+250, met))
   373  	require.True(t, w.isValidWriteWithLock(now.UnixNano()+50, met))
   374  	require.Equal(t, 0, w.queue.Len())
   375  
   376  	mm := producer.NewMockMessage(ctrl)
   377  	mm.EXPECT().Bytes().Return([]byte("foo"))
   378  	mm.EXPECT().Size().Return(3)
   379  	w.Write(producer.NewRefCountedMessage(mm, nil))
   380  	require.Equal(t, 1, w.queue.Len())
   381  }
   382  
   383  func TestMessageWriterKeepNewWritesInOrderInFrontOfTheQueue(t *testing.T) {
   384  	ctrl := xtest.NewController(t)
   385  	defer ctrl.Finish()
   386  
   387  	opts := testOptions().SetMessageRetryNanosFn(
   388  		NextRetryNanosFn(retry.NewOptions().SetInitialBackoff(2 * time.Nanosecond).SetMaxBackoff(5 * time.Nanosecond)),
   389  	)
   390  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   391  
   392  	now := time.Now()
   393  	w.nowFn = func() time.Time { return now }
   394  
   395  	mm1 := producer.NewMockMessage(ctrl)
   396  	mm1.EXPECT().Size().Return(3)
   397  	rm1 := producer.NewRefCountedMessage(mm1, nil)
   398  	mm1.EXPECT().Bytes().Return([]byte("1")).AnyTimes()
   399  	w.Write(rm1)
   400  	validateMessages(t, []*producer.RefCountedMessage{rm1}, w)
   401  	mm2 := producer.NewMockMessage(ctrl)
   402  	mm2.EXPECT().Size().Return(3)
   403  	rm2 := producer.NewRefCountedMessage(mm2, nil)
   404  	mm2.EXPECT().Bytes().Return([]byte("2")).AnyTimes()
   405  	w.Write(rm2)
   406  	validateMessages(t, []*producer.RefCountedMessage{rm1, rm2}, w)
   407  	w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), 2, true, &scanBatchMetrics{})
   408  
   409  	w.lastNewWrite = nil
   410  	mm3 := producer.NewMockMessage(ctrl)
   411  	mm3.EXPECT().Size().Return(3)
   412  	rm3 := producer.NewRefCountedMessage(mm3, nil)
   413  	mm3.EXPECT().Bytes().Return([]byte("3")).AnyTimes()
   414  	w.Write(rm3)
   415  	validateMessages(t, []*producer.RefCountedMessage{rm3, rm1, rm2}, w)
   416  
   417  	mm4 := producer.NewMockMessage(ctrl)
   418  	mm4.EXPECT().Size().Return(3)
   419  	rm4 := producer.NewRefCountedMessage(mm4, nil)
   420  	mm4.EXPECT().Bytes().Return([]byte("4")).AnyTimes()
   421  	w.Write(rm4)
   422  
   423  	validateMessages(t, []*producer.RefCountedMessage{rm3, rm4, rm1, rm2}, w)
   424  }
   425  
   426  func TestMessageWriterRetryIterateBatchFullScan(t *testing.T) {
   427  	ctrl := xtest.NewController(t)
   428  	defer ctrl.Finish()
   429  
   430  	retryBatchSize := 2
   431  	opts := testOptions().SetMessageQueueScanBatchSize(retryBatchSize).SetMessageRetryNanosFn(
   432  		NextRetryNanosFn(retry.NewOptions().SetInitialBackoff(2 * time.Nanosecond).SetMaxBackoff(5 * time.Nanosecond)),
   433  	)
   434  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   435  
   436  	now := time.Now()
   437  	w.nowFn = func() time.Time { return now }
   438  
   439  	mm1 := producer.NewMockMessage(ctrl)
   440  	mm1.EXPECT().Size().Return(3)
   441  	rm1 := producer.NewRefCountedMessage(mm1, nil)
   442  	mm1.EXPECT().Bytes().Return([]byte("1")).AnyTimes()
   443  	w.Write(rm1)
   444  
   445  	mm2 := producer.NewMockMessage(ctrl)
   446  	mm2.EXPECT().Size().Return(3)
   447  	rm2 := producer.NewRefCountedMessage(mm2, nil)
   448  	mm2.EXPECT().Bytes().Return([]byte("2")).AnyTimes()
   449  	w.Write(rm2)
   450  
   451  	mm3 := producer.NewMockMessage(ctrl)
   452  	mm3.EXPECT().Size().Return(3)
   453  	rm3 := producer.NewRefCountedMessage(mm3, nil)
   454  	mm3.EXPECT().Bytes().Return([]byte("3")).AnyTimes()
   455  	w.Write(rm3)
   456  
   457  	mm4 := producer.NewMockMessage(ctrl)
   458  	mm4.EXPECT().Size().Return(3)
   459  	rm4 := producer.NewRefCountedMessage(mm4, nil)
   460  	mm4.EXPECT().Bytes().Return([]byte("4")).AnyTimes()
   461  	w.Write(rm4)
   462  
   463  	validateMessages(t, []*producer.RefCountedMessage{rm1, rm2, rm3, rm4}, w)
   464  	mm1.EXPECT().Finalize(gomock.Eq(producer.Dropped))
   465  	rm1.Drop()
   466  	require.Equal(t, 4, w.queue.Len())
   467  	e, toBeRetried := w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{})
   468  	require.Equal(t, 1, len(toBeRetried))
   469  	require.Equal(t, 3, w.queue.Len())
   470  
   471  	// Make sure it stopped at rm3.
   472  	require.Equal(t, rm3, e.Value.(*message).RefCountedMessage)
   473  
   474  	require.Equal(t, 3, w.queue.Len())
   475  	e, toBeRetried = w.scanBatchWithLock(e, w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{})
   476  	require.Nil(t, e)
   477  	require.Equal(t, 2, len(toBeRetried))
   478  	require.Equal(t, 3, w.queue.Len())
   479  
   480  	e, toBeRetried = w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{})
   481  	// Make sure it stopped at rm4.
   482  	require.Equal(t, rm4, e.Value.(*message).RefCountedMessage)
   483  	require.Equal(t, 0, len(toBeRetried))
   484  
   485  	e, toBeRetried = w.scanBatchWithLock(e, w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{})
   486  	require.Nil(t, e)
   487  	require.Equal(t, 0, len(toBeRetried))
   488  }
   489  
   490  //nolint:lll
   491  func TestMessageWriterRetryIterateBatchFullScanWithMessageTTL(t *testing.T) {
   492  	ctrl := xtest.NewController(t)
   493  	defer ctrl.Finish()
   494  
   495  	retryBatchSize := 2
   496  	opts := testOptions().SetMessageQueueScanBatchSize(retryBatchSize).SetMessageRetryNanosFn(
   497  		NextRetryNanosFn(retry.NewOptions().SetInitialBackoff(2 * time.Nanosecond).SetMaxBackoff(5 * time.Nanosecond)),
   498  	)
   499  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   500  
   501  	now := time.Now()
   502  	w.nowFn = func() time.Time { return now }
   503  
   504  	mm1 := producer.NewMockMessage(ctrl)
   505  	mm1.EXPECT().Size().Return(3)
   506  	rm1 := producer.NewRefCountedMessage(mm1, nil)
   507  	mm1.EXPECT().Bytes().Return([]byte("1")).AnyTimes()
   508  	w.Write(rm1)
   509  
   510  	mm2 := producer.NewMockMessage(ctrl)
   511  	mm2.EXPECT().Size().Return(3)
   512  	rm2 := producer.NewRefCountedMessage(mm2, nil)
   513  	mm2.EXPECT().Bytes().Return([]byte("2")).AnyTimes()
   514  	w.Write(rm2)
   515  
   516  	mm3 := producer.NewMockMessage(ctrl)
   517  	mm3.EXPECT().Size().Return(3)
   518  	rm3 := producer.NewRefCountedMessage(mm3, nil)
   519  	mm3.EXPECT().Bytes().Return([]byte("3")).AnyTimes()
   520  	w.Write(rm3)
   521  
   522  	mm4 := producer.NewMockMessage(ctrl)
   523  	mm4.EXPECT().Size().Return(3)
   524  	rm4 := producer.NewRefCountedMessage(mm4, nil)
   525  	mm4.EXPECT().Bytes().Return([]byte("4")).AnyTimes()
   526  	w.Write(rm4)
   527  
   528  	mm1.EXPECT().Finalize(gomock.Eq(producer.Dropped))
   529  	rm1.Drop()
   530  	require.Equal(t, 4, w.queue.Len())
   531  	e, toBeRetried := w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{})
   532  	require.Equal(t, 1, len(toBeRetried))
   533  	require.Equal(t, 3, w.queue.Len())
   534  
   535  	require.Equal(t, rm2, e.Value.(*message).RefCountedMessage)
   536  
   537  	w.SetMessageTTLNanos(int64(time.Minute))
   538  	mm4.EXPECT().Finalize(gomock.Eq(producer.Consumed))
   539  	mm3.EXPECT().Finalize(gomock.Eq(producer.Consumed))
   540  	e, toBeRetried = w.scanBatchWithLock(e, w.nowFn().UnixNano()+int64(time.Hour), retryBatchSize, true, &scanBatchMetrics{})
   541  	require.Equal(t, 0, len(toBeRetried))
   542  	require.Equal(t, 1, w.queue.Len())
   543  	require.Nil(t, e)
   544  
   545  	mm2.EXPECT().Finalize(gomock.Eq(producer.Consumed))
   546  	e, toBeRetried = w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano()+int64(time.Hour), retryBatchSize, true, &scanBatchMetrics{})
   547  	require.Equal(t, 0, len(toBeRetried))
   548  	require.Equal(t, 0, w.queue.Len())
   549  	require.Nil(t, e)
   550  }
   551  
   552  //nolint:lll
   553  func TestMessageWriterRetryIterateBatchNotFullScan(t *testing.T) {
   554  	ctrl := xtest.NewController(t)
   555  	defer ctrl.Finish()
   556  
   557  	retryBatchSize := 100
   558  	opts := testOptions().SetMessageQueueScanBatchSize(retryBatchSize).SetMessageRetryNanosFn(
   559  		NextRetryNanosFn(retry.NewOptions().SetInitialBackoff(2 * time.Nanosecond).SetMaxBackoff(5 * time.Nanosecond)),
   560  	)
   561  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   562  
   563  	now := time.Now()
   564  	w.nowFn = func() time.Time { return now }
   565  
   566  	mm1 := producer.NewMockMessage(ctrl)
   567  	mm1.EXPECT().Size().Return(1)
   568  	rm1 := producer.NewRefCountedMessage(mm1, nil)
   569  	mm1.EXPECT().Bytes().Return([]byte("1")).AnyTimes()
   570  	w.Write(rm1)
   571  
   572  	mm2 := producer.NewMockMessage(ctrl)
   573  	mm2.EXPECT().Size().Return(1)
   574  	rm2 := producer.NewRefCountedMessage(mm2, nil)
   575  	mm2.EXPECT().Bytes().Return([]byte("2")).AnyTimes()
   576  	w.Write(rm2)
   577  
   578  	mm3 := producer.NewMockMessage(ctrl)
   579  	mm3.EXPECT().Size().Return(1)
   580  	rm3 := producer.NewRefCountedMessage(mm3, nil)
   581  	mm3.EXPECT().Bytes().Return([]byte("3")).AnyTimes()
   582  	w.Write(rm3)
   583  
   584  	mm4 := producer.NewMockMessage(ctrl)
   585  	mm4.EXPECT().Size().Return(1)
   586  	rm4 := producer.NewRefCountedMessage(mm4, nil)
   587  	mm4.EXPECT().Bytes().Return([]byte("4")).AnyTimes()
   588  	w.Write(rm4)
   589  
   590  	mm1.EXPECT().Finalize(gomock.Eq(producer.Dropped))
   591  	rm1.Drop()
   592  	require.Equal(t, 4, w.queue.Len())
   593  	e, toBeRetried := w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, false, &scanBatchMetrics{})
   594  	require.Equal(t, 3, len(toBeRetried))
   595  	require.Equal(t, 3, w.queue.Len())
   596  	require.Nil(t, e)
   597  	validateMessages(t, []*producer.RefCountedMessage{rm2, rm3, rm4}, w)
   598  	// Although mm4 is dropped, it will not be removed from the queue because
   599  	// it was not checked.
   600  	mm4.EXPECT().Finalize(gomock.Eq(producer.Dropped))
   601  	rm4.Drop()
   602  	require.Equal(t, 3, w.queue.Len())
   603  	e, toBeRetried = w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, false, &scanBatchMetrics{})
   604  	require.Equal(t, rm2, e.Value.(*message).RefCountedMessage)
   605  	require.Equal(t, 0, len(toBeRetried))
   606  	require.Equal(t, 3, w.queue.Len())
   607  	require.Equal(t, rm2, e.Value.(*message).RefCountedMessage)
   608  	validateMessages(t, []*producer.RefCountedMessage{rm2, rm3, rm4}, w)
   609  	w.lastNewWrite = nil
   610  
   611  	mm5 := producer.NewMockMessage(ctrl)
   612  	mm5.EXPECT().Size().Return(1)
   613  	rm5 := producer.NewRefCountedMessage(mm5, nil)
   614  	mm5.EXPECT().Bytes().Return([]byte("5")).AnyTimes()
   615  	w.Write(rm5)
   616  	validateMessages(t, []*producer.RefCountedMessage{rm5, rm2, rm3, rm4}, w)
   617  
   618  	require.Equal(t, 4, w.queue.Len())
   619  	e, toBeRetried = w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, false, &scanBatchMetrics{})
   620  	require.Equal(t, rm2, e.Value.(*message).RefCountedMessage)
   621  	require.Equal(t, 1, len(toBeRetried))
   622  	require.Equal(t, rm5, toBeRetried[0].RefCountedMessage)
   623  	require.Equal(t, 4, w.queue.Len())
   624  }
   625  
   626  func TestNextRetryAfterNanos(t *testing.T) {
   627  	backoffDuration := time.Minute
   628  	opts := testOptions().
   629  		SetMessageRetryNanosFn(
   630  			NextRetryNanosFn(
   631  				retry.NewOptions().
   632  					SetInitialBackoff(backoffDuration).
   633  					SetMaxBackoff(2 * backoffDuration).
   634  					SetJitter(true),
   635  			),
   636  		)
   637  	w := newMessageWriter(200, nil, opts, testMessageWriterMetrics())
   638  
   639  	nowNanos := time.Now().UnixNano()
   640  	m := newMessage()
   641  	m.IncWriteTimes()
   642  	retryAtNanos := w.nextRetryAfterNanos(m.WriteTimes()) + nowNanos
   643  	require.True(t, retryAtNanos > nowNanos)
   644  	require.True(t, retryAtNanos < nowNanos+int64(backoffDuration))
   645  
   646  	m.IncWriteTimes()
   647  	retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes()) + nowNanos
   648  	require.True(t, retryAtNanos >= nowNanos+int64(backoffDuration))
   649  	require.True(t, retryAtNanos < nowNanos+2*int64(backoffDuration))
   650  
   651  	m.IncWriteTimes()
   652  	retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes()) + nowNanos
   653  	require.True(t, retryAtNanos == nowNanos+2*int64(backoffDuration))
   654  }
   655  
   656  func TestStaticRetryAfterNanos(t *testing.T) {
   657  	fn, err := StaticRetryNanosFn([]time.Duration{time.Minute, 10 * time.Second, 5 * time.Second})
   658  	require.NoError(t, err)
   659  
   660  	opts := testOptions().SetMessageRetryNanosFn(fn)
   661  	w := newMessageWriter(200, nil, opts, testMessageWriterMetrics())
   662  
   663  	m := newMessage()
   664  	m.IncWriteTimes()
   665  	retryAtNanos := w.nextRetryAfterNanos(m.WriteTimes())
   666  	require.Equal(t, int64(time.Minute), retryAtNanos)
   667  
   668  	m.IncWriteTimes()
   669  	retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes())
   670  	require.Equal(t, int64(10*time.Second), retryAtNanos)
   671  
   672  	m.IncWriteTimes()
   673  	retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes())
   674  	require.Equal(t, int64(5*time.Second), retryAtNanos)
   675  
   676  	m.IncWriteTimes()
   677  	retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes())
   678  	require.Equal(t, int64(5*time.Second), retryAtNanos)
   679  }
   680  
   681  func TestExpectedProcessedAt(t *testing.T) {
   682  	m := newMessage()
   683  	m.initNanos = 100
   684  	m.SetRetryAtNanos(200)
   685  	require.Equal(t, int64(100), m.ExpectedProcessAtNanos())
   686  	m.SetRetryAtNanos(300)
   687  	require.Equal(t, int64(200), m.ExpectedProcessAtNanos())
   688  }
   689  
   690  func TestMessageWriterCloseCleanupAllMessages(t *testing.T) {
   691  	defer leaktest.Check(t)()
   692  
   693  	opts := testOptions()
   694  	w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics())
   695  
   696  	ctrl := xtest.NewController(t)
   697  	defer ctrl.Finish()
   698  
   699  	mm := producer.NewMockMessage(ctrl)
   700  	mm.EXPECT().Size().Return(3)
   701  
   702  	rm := producer.NewRefCountedMessage(mm, nil)
   703  	mm.EXPECT().Finalize(producer.Consumed)
   704  	mm.EXPECT().Bytes().Return([]byte("foo"))
   705  	w.Write(rm)
   706  	require.False(t, isEmptyWithLock(w.acks))
   707  	require.Equal(t, 1, w.queue.Len())
   708  	w.Init()
   709  	w.Close()
   710  	require.Equal(t, 0, w.queue.Len())
   711  	require.True(t, isEmptyWithLock(w.acks))
   712  }
   713  
   714  func TestMessageWriterQueueFullScanOnWriteErrors(t *testing.T) {
   715  	ctrl := xtest.NewController(t)
   716  	defer ctrl.Finish()
   717  
   718  	opts := testOptions().SetMessageQueueScanBatchSize(1)
   719  	scope := tally.NewTestScope("", nil)
   720  	metrics := testMessageWriterMetricsWithScope(scope).withConsumer("c1")
   721  	w := newMessageWriter(200, newMessagePool(), opts, metrics)
   722  	w.AddConsumerWriter(newConsumerWriter("bad", nil, opts, testConsumerWriterMetrics()))
   723  
   724  	mm1 := producer.NewMockMessage(ctrl)
   725  	mm1.EXPECT().Size().Return(3)
   726  	mm1.EXPECT().Bytes().Return([]byte("foo"))
   727  	rm1 := producer.NewRefCountedMessage(mm1, nil)
   728  	w.Write(rm1)
   729  	require.Equal(t, 1, w.queue.Len())
   730  
   731  	mm2 := producer.NewMockMessage(ctrl)
   732  	mm2.EXPECT().Size().Return(3)
   733  	mm2.EXPECT().Bytes().Return([]byte("foo"))
   734  	rm2 := producer.NewRefCountedMessage(mm2, nil)
   735  	w.Write(rm2)
   736  	require.Equal(t, 2, w.queue.Len())
   737  
   738  	mm1.EXPECT().Finalize(producer.Dropped)
   739  	rm1.Drop()
   740  	w.scanMessageQueue()
   741  	require.Equal(t, 1, w.queue.Len())
   742  
   743  	snapshot := scope.Snapshot()
   744  	counters := snapshot.Counters()
   745  	require.Equal(t, int64(1), counters["message-processed+consumer=c1,result=write"].Value())
   746  	require.Equal(t, int64(1), counters["message-processed+consumer=c1,result=drop"].Value())
   747  }
   748  
   749  func TestMessageWriter_WithoutConsumerScope(t *testing.T) {
   750  	ctrl := xtest.NewController(t)
   751  	defer ctrl.Finish()
   752  
   753  	opts := testOptions().SetMessageQueueScanBatchSize(1)
   754  	scope := tally.NewTestScope("", nil)
   755  	metrics := newMessageWriterMetrics(scope, instrument.TimerOptions{}, true)
   756  	w := newMessageWriter(200, nil, opts, metrics)
   757  	w.AddConsumerWriter(newConsumerWriter("bad", nil, opts, testConsumerWriterMetrics()))
   758  
   759  	snapshot := scope.Snapshot()
   760  	counters := snapshot.Counters()
   761  	require.Nil(t, counters["message-processed+consumer=c1,result=write"])
   762  	require.NotNil(t, counters["message-processed+result=write"])
   763  }
   764  
   765  func isEmptyWithLock(h *acks) bool {
   766  	return h.size() == 0
   767  }
   768  
   769  func testMessageWriterMetrics() *messageWriterMetrics {
   770  	return newMessageWriterMetrics(tally.NoopScope, instrument.TimerOptions{}, false)
   771  }
   772  
   773  func testMessageWriterMetricsWithScope(scope tally.TestScope) *messageWriterMetrics {
   774  	return newMessageWriterMetrics(scope, instrument.TimerOptions{}, false)
   775  }
   776  
   777  func validateMessages(t *testing.T, msgs []*producer.RefCountedMessage, w *messageWriter) {
   778  	w.RLock()
   779  	idx := 0
   780  	for e := w.queue.Front(); e != nil; e = e.Next() {
   781  		require.Equal(t, msgs[idx].Bytes(), e.Value.(*message).RefCountedMessage.Bytes())
   782  		idx++
   783  	}
   784  	w.RUnlock()
   785  	require.Equal(t, idx, len(msgs))
   786  }