github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/topic/topicreaderinternal/batcher_test.go (about)

     1  package topicreaderinternal
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/empty"
    13  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon"
    14  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicreader"
    15  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/topic/topicreadercommon"
    16  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
    17  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    18  )
    19  
    20  func TestBatcher_PushBatch(t *testing.T) {
    21  	session1 := &topicreadercommon.PartitionSession{}
    22  	session2 := &topicreadercommon.PartitionSession{}
    23  
    24  	m11 := topicreadercommon.MessageWithSetCommitRangeForTest(&topicreadercommon.PublicMessage{
    25  		WrittenAt: testTime(1),
    26  	}, topicreadercommon.CommitRange{PartitionSession: session1})
    27  
    28  	m12 := topicreadercommon.MessageWithSetCommitRangeForTest(&topicreadercommon.PublicMessage{
    29  		WrittenAt: testTime(2),
    30  	}, topicreadercommon.CommitRange{PartitionSession: session1})
    31  
    32  	m21 := topicreadercommon.MessageWithSetCommitRangeForTest(&topicreadercommon.PublicMessage{
    33  		WrittenAt: testTime(3),
    34  	}, topicreadercommon.CommitRange{PartitionSession: session2})
    35  
    36  	m22 := topicreadercommon.MessageWithSetCommitRangeForTest(&topicreadercommon.PublicMessage{
    37  		WrittenAt: testTime(4),
    38  	}, topicreadercommon.CommitRange{PartitionSession: session2})
    39  
    40  	batch1 := mustNewBatch(session1, []*topicreadercommon.PublicMessage{m11, m12})
    41  	batch2 := mustNewBatch(session2, []*topicreadercommon.PublicMessage{m21})
    42  	batch3 := mustNewBatch(session2, []*topicreadercommon.PublicMessage{m22})
    43  
    44  	b := newBatcher()
    45  	require.NoError(t, b.PushBatches(batch1))
    46  	require.NoError(t, b.PushBatches(batch2))
    47  	require.NoError(t, b.PushBatches(batch3))
    48  
    49  	expectedSession1 := newBatcherItemBatch(mustNewBatch(session1, []*topicreadercommon.PublicMessage{m11, m12}))
    50  	expectedSession2 := newBatcherItemBatch(mustNewBatch(session2, []*topicreadercommon.PublicMessage{m21, m22}))
    51  
    52  	expected := batcherMessagesMap{
    53  		session1: batcherMessageOrderItems{expectedSession1},
    54  		session2: batcherMessageOrderItems{expectedSession2},
    55  	}
    56  	require.Equal(t, expected, b.messages)
    57  }
    58  
    59  func TestBatcher_PushRawMessage(t *testing.T) {
    60  	t.Run("Empty", func(t *testing.T) {
    61  		b := newBatcher()
    62  		session := &topicreadercommon.PartitionSession{}
    63  		m := &rawtopicreader.StopPartitionSessionRequest{
    64  			PartitionSessionID: 1,
    65  		}
    66  		require.NoError(t, b.PushRawMessage(session, m))
    67  
    68  		expectedMap := batcherMessagesMap{session: batcherMessageOrderItems{newBatcherItemRawMessage(m)}}
    69  		require.Equal(t, expectedMap, b.messages)
    70  	})
    71  	t.Run("AddRawAfterBatch", func(t *testing.T) {
    72  		b := newBatcher()
    73  		session := &topicreadercommon.PartitionSession{}
    74  		batch := mustNewBatch(session, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
    75  		m := &rawtopicreader.StopPartitionSessionRequest{
    76  			PartitionSessionID: 1,
    77  		}
    78  
    79  		require.NoError(t, b.PushBatches(batch))
    80  		require.NoError(t, b.PushRawMessage(session, m))
    81  
    82  		expectedMap := batcherMessagesMap{session: batcherMessageOrderItems{
    83  			newBatcherItemBatch(batch),
    84  			newBatcherItemRawMessage(m),
    85  		}}
    86  		require.Equal(t, expectedMap, b.messages)
    87  	})
    88  
    89  	t.Run("AddBatchRawBatchBatch", func(t *testing.T) {
    90  		b := newBatcher()
    91  		session := &topicreadercommon.PartitionSession{}
    92  		batch1 := mustNewBatch(session, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
    93  		batch2 := mustNewBatch(session, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(2)}})
    94  		batch3 := mustNewBatch(session, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(3)}})
    95  		m := &rawtopicreader.StopPartitionSessionRequest{
    96  			PartitionSessionID: 1,
    97  		}
    98  
    99  		require.NoError(t, b.PushBatches(batch1))
   100  		require.NoError(t, b.PushRawMessage(session, m))
   101  		require.NoError(t, b.PushBatches(batch2))
   102  		require.NoError(t, b.PushBatches(batch3))
   103  
   104  		expectedMap := batcherMessagesMap{session: batcherMessageOrderItems{
   105  			newBatcherItemBatch(batch1),
   106  			newBatcherItemRawMessage(m),
   107  			newBatcherItemBatch(mustNewBatch(
   108  				session,
   109  				[]*topicreadercommon.PublicMessage{{WrittenAt: testTime(2)}, {WrittenAt: testTime(3)}},
   110  			)),
   111  		}}
   112  		require.Equal(t, expectedMap, b.messages)
   113  	})
   114  }
   115  
   116  func TestBatcher_Pop(t *testing.T) {
   117  	t.Run("SimpleGet", func(t *testing.T) {
   118  		ctx := context.Background()
   119  		batch := mustNewBatch(nil, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
   120  
   121  		b := newBatcher()
   122  		require.NoError(t, b.PushBatches(batch))
   123  
   124  		res, err := b.Pop(ctx, batcherGetOptions{})
   125  		require.NoError(t, err)
   126  		require.Equal(t, newBatcherItemBatch(batch), res)
   127  	})
   128  
   129  	t.Run("SimpleOneOfTwo", func(t *testing.T) {
   130  		ctx := context.Background()
   131  		session1 := &topicreadercommon.PartitionSession{}
   132  		session2 := &topicreadercommon.PartitionSession{}
   133  
   134  		mess1 := &topicreadercommon.PublicMessage{WrittenAt: testTime(1)}
   135  		topicreadercommon.MessageWithSetCommitRangeForTest(mess1, topicreadercommon.CommitRange{PartitionSession: session1})
   136  		batch := mustNewBatch(
   137  			session1,
   138  			[]*topicreadercommon.PublicMessage{mess1},
   139  		)
   140  
   141  		mess2 := &topicreadercommon.PublicMessage{WrittenAt: testTime(2)}
   142  		topicreadercommon.MessageWithSetCommitRangeForTest(mess2, topicreadercommon.CommitRange{PartitionSession: session2})
   143  		batch2 := mustNewBatch(
   144  			session2,
   145  			[]*topicreadercommon.PublicMessage{mess2},
   146  		)
   147  
   148  		b := newBatcher()
   149  		require.NoError(t, b.PushBatches(batch))
   150  		require.NoError(t, b.PushBatches(batch2))
   151  
   152  		possibleResults := []batcherMessageOrderItem{newBatcherItemBatch(batch), newBatcherItemBatch(batch2)}
   153  
   154  		res, err := b.Pop(ctx, batcherGetOptions{})
   155  		require.NoError(t, err)
   156  		require.Contains(t, possibleResults, res)
   157  		require.Len(t, b.messages, 1)
   158  
   159  		res2, err := b.Pop(ctx, batcherGetOptions{})
   160  		require.NoError(t, err)
   161  		require.Contains(t, possibleResults, res2)
   162  		require.NotEqual(t, res, res2)
   163  		require.Empty(t, b.messages)
   164  	})
   165  
   166  	xtest.TestManyTimesWithName(t, "GetBeforePut", func(t testing.TB) {
   167  		ctx := context.Background()
   168  		batch := mustNewBatch(nil, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
   169  
   170  		b := newBatcher()
   171  		b.notifyAboutNewMessages()
   172  
   173  		go func() {
   174  			xtest.SpinWaitCondition(t, &b.m, func() bool {
   175  				return len(b.hasNewMessages) == 0
   176  			})
   177  			_ = b.PushBatches(batch)
   178  		}()
   179  
   180  		res, err := b.Pop(ctx, batcherGetOptions{})
   181  		require.NoError(t, err)
   182  		require.Equal(t, newBatcherItemBatch(batch), res)
   183  		require.Empty(t, b.messages)
   184  	})
   185  
   186  	t.Run("GetMaxOne", func(t *testing.T) {
   187  		ctx := context.Background()
   188  
   189  		m1 := &topicreadercommon.PublicMessage{WrittenAt: testTime(1)}
   190  		m2 := &topicreadercommon.PublicMessage{WrittenAt: testTime(2)}
   191  		batch := mustNewBatch(nil, []*topicreadercommon.PublicMessage{m1, m2})
   192  
   193  		b := newBatcher()
   194  		require.NoError(t, b.PushBatches(batch))
   195  
   196  		res, err := b.Pop(ctx, batcherGetOptions{MaxCount: 1})
   197  		require.NoError(t, err)
   198  
   199  		expectedResult := newBatcherItemBatch(mustNewBatch(nil, []*topicreadercommon.PublicMessage{m1}))
   200  		require.Equal(t, expectedResult, res)
   201  
   202  		expectedMessages := batcherMessagesMap{
   203  			nil: batcherMessageOrderItems{newBatcherItemBatch(mustNewBatch(nil, []*topicreadercommon.PublicMessage{m2}))},
   204  		}
   205  		require.Equal(t, expectedMessages, b.messages)
   206  	})
   207  
   208  	t.Run("GetFirstMessageFromSameSession", func(t *testing.T) {
   209  		b := newBatcher()
   210  		batch := mustNewBatch(nil, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
   211  		require.NoError(t, b.PushBatches(batch))
   212  		require.NoError(t, b.PushRawMessage(nil, &rawtopicreader.StopPartitionSessionRequest{PartitionSessionID: 1}))
   213  
   214  		res, err := b.Pop(context.Background(), batcherGetOptions{})
   215  		require.NoError(t, err)
   216  		require.Equal(t, newBatcherItemBatch(batch), res)
   217  	})
   218  
   219  	t.Run("PreferFirstRawMessageFromDifferentSessions", func(t *testing.T) {
   220  		session1 := &topicreadercommon.PartitionSession{}
   221  		session2 := &topicreadercommon.PartitionSession{}
   222  
   223  		b := newBatcher()
   224  		m := &rawtopicreader.StopPartitionSessionRequest{PartitionSessionID: 1}
   225  
   226  		require.NoError(t, b.PushBatches(mustNewBatch(
   227  			session1,
   228  			[]*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}},
   229  		)))
   230  		require.NoError(t, b.PushRawMessage(session2, m))
   231  
   232  		res, err := b.Pop(context.Background(), batcherGetOptions{})
   233  		require.NoError(t, err)
   234  		require.Equal(t, newBatcherItemRawMessage(m), res)
   235  	})
   236  
   237  	xtest.TestManyTimesWithName(t, "CloseBatcherWhilePopWait", func(t testing.TB) {
   238  		ctx := xtest.Context(t)
   239  		testErr := errors.New("test")
   240  
   241  		b := newBatcher()
   242  		b.notifyAboutNewMessages()
   243  
   244  		require.Len(t, b.hasNewMessages, 1)
   245  
   246  		popFinished := make(empty.Chan)
   247  		popGoroutineStarted := make(empty.Chan)
   248  		go func() {
   249  			close(popGoroutineStarted)
   250  
   251  			_, popErr := b.Pop(ctx, batcherGetOptions{MinCount: 1})
   252  			require.ErrorIs(t, popErr, testErr)
   253  			close(popFinished)
   254  		}()
   255  
   256  		xtest.WaitChannelClosed(t, popGoroutineStarted)
   257  
   258  		// loop for wait Pop start wait message
   259  		xtest.SpinWaitCondition(t, &b.m, func() bool {
   260  			return len(b.hasNewMessages) == 0
   261  		})
   262  
   263  		require.NoError(t, b.Close(testErr))
   264  		require.Error(t, b.Close(errors.New("second close")))
   265  
   266  		xtest.WaitChannelClosed(t, popFinished)
   267  	})
   268  }
   269  
   270  func TestBatcher_PopMinIgnored(t *testing.T) {
   271  	t.Run("PopAfterForce", func(t *testing.T) {
   272  		b := newBatcher()
   273  		require.NoError(t, b.PushBatches(&topicreadercommon.PublicBatch{Messages: []*topicreadercommon.PublicMessage{
   274  			{
   275  				SeqNo: 1,
   276  			},
   277  		}}))
   278  
   279  		b.IgnoreMinRestrictionsOnNextPop()
   280  
   281  		ctx, cancel := xcontext.WithTimeout(context.Background(), time.Second)
   282  		defer cancel()
   283  
   284  		batch, err := b.Pop(ctx, batcherGetOptions{MinCount: 2})
   285  		require.NoError(t, err)
   286  		require.Len(t, batch.Batch.Messages, 1)
   287  		require.False(t, b.forceIgnoreMinRestrictionsOnNextMessagesBatch)
   288  	})
   289  
   290  	xtest.TestManyTimesWithName(t, "ForceAfterPop", func(t testing.TB) {
   291  		b := newBatcher()
   292  		require.NoError(t, b.PushBatches(&topicreadercommon.PublicBatch{Messages: []*topicreadercommon.PublicMessage{
   293  			{
   294  				SeqNo: 1,
   295  			},
   296  		}}))
   297  
   298  		var IgnoreMinRestrictionsOnNextPopDone atomic.Int64
   299  		go func() {
   300  			defer IgnoreMinRestrictionsOnNextPopDone.Add(1)
   301  
   302  			xtest.SpinWaitCondition(t, &b.m, func() bool {
   303  				return len(b.hasNewMessages) == 0
   304  			})
   305  			b.IgnoreMinRestrictionsOnNextPop()
   306  		}()
   307  
   308  		ctx, cancel := xcontext.WithTimeout(context.Background(), time.Second)
   309  		defer cancel()
   310  
   311  		batch, err := b.Pop(ctx, batcherGetOptions{MinCount: 2})
   312  
   313  		require.NoError(t, err, IgnoreMinRestrictionsOnNextPopDone.Load())
   314  		require.Len(t, batch.Batch.Messages, 1)
   315  		require.False(t, b.forceIgnoreMinRestrictionsOnNextMessagesBatch)
   316  	})
   317  }
   318  
   319  func TestBatcherConcurency(t *testing.T) {
   320  	xtest.TestManyTimesWithName(t, "OneBatch", func(tb testing.TB) {
   321  		b := newBatcher()
   322  
   323  		go func() {
   324  			_ = b.PushBatches(&topicreadercommon.PublicBatch{Messages: []*topicreadercommon.PublicMessage{{SeqNo: 1}}})
   325  		}()
   326  
   327  		ctx, cancel := xcontext.WithTimeout(context.Background(), time.Second)
   328  		defer cancel()
   329  
   330  		batch, err := b.Pop(ctx, batcherGetOptions{MinCount: 1})
   331  		require.NoError(tb, err)
   332  		require.Equal(tb, int64(1), batch.Batch.Messages[0].SeqNo)
   333  	})
   334  
   335  	xtest.TestManyTimesWithName(t, "ManyRawMessages", func(tb testing.TB) {
   336  		const count = 10
   337  		b := newBatcher()
   338  		session := &topicreadercommon.PartitionSession{}
   339  
   340  		go func() {
   341  			for i := 0; i < count; i++ {
   342  				_ = b.PushRawMessage(session, &rawtopicreader.StartPartitionSessionRequest{
   343  					CommittedOffset:  rawtopiccommon.NewOffset(int64(i)),
   344  					PartitionOffsets: rawtopiccommon.OffsetRange{},
   345  				})
   346  			}
   347  		}()
   348  
   349  		ctx, cancel := xcontext.WithTimeout(context.Background(), time.Second)
   350  		defer cancel()
   351  
   352  		for i := 0; i < count; i++ {
   353  			res, err := b.Pop(ctx, batcherGetOptions{MinCount: 1})
   354  			require.NoError(tb, err)
   355  			require.Equal(
   356  				tb,
   357  				rawtopiccommon.NewOffset(int64(i)),
   358  				res.RawMessage.(*rawtopicreader.StartPartitionSessionRequest).CommittedOffset,
   359  			)
   360  		}
   361  	})
   362  }
   363  
   364  func TestBatcher_Find(t *testing.T) {
   365  	t.Run("Empty", func(t *testing.T) {
   366  		b := newBatcher()
   367  		findRes := b.findNeedLock(batcherGetOptions{})
   368  		require.False(t, findRes.Ok)
   369  	})
   370  	t.Run("FoundEmptyFilter", func(t *testing.T) {
   371  		session := &topicreadercommon.PartitionSession{}
   372  		batch := mustNewBatch(session, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
   373  
   374  		b := newBatcher()
   375  
   376  		require.NoError(t, b.PushBatches(batch))
   377  
   378  		findRes := b.findNeedLock(batcherGetOptions{})
   379  		expectedResult := batcherResultCandidate{
   380  			Key:         session,
   381  			Result:      newBatcherItemBatch(batch),
   382  			Rest:        batcherMessageOrderItems{},
   383  			WaiterIndex: 0,
   384  			Ok:          true,
   385  		}
   386  		require.Equal(t, expectedResult, findRes)
   387  	})
   388  
   389  	t.Run("FoundPartialBatchFilter", func(t *testing.T) {
   390  		session := &topicreadercommon.PartitionSession{}
   391  		batch := mustNewBatch(
   392  			session,
   393  			[]*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}, {WrittenAt: testTime(2)}},
   394  		)
   395  
   396  		b := newBatcher()
   397  
   398  		require.NoError(t, b.PushBatches(batch))
   399  
   400  		findRes := b.findNeedLock(batcherGetOptions{MaxCount: 1})
   401  
   402  		expectedResult := newBatcherItemBatch(mustNewBatch(
   403  			session,
   404  			[]*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}},
   405  		))
   406  		expectedRestBatch := newBatcherItemBatch(mustNewBatch(
   407  			session,
   408  			[]*topicreadercommon.PublicMessage{{WrittenAt: testTime(2)}},
   409  		))
   410  
   411  		expectedCandidate := batcherResultCandidate{
   412  			Key:         session,
   413  			Result:      expectedResult,
   414  			Rest:        batcherMessageOrderItems{expectedRestBatch},
   415  			WaiterIndex: 0,
   416  			Ok:          true,
   417  		}
   418  		require.Equal(t, expectedCandidate, findRes)
   419  	})
   420  }
   421  
   422  func TestBatcher_Apply(t *testing.T) {
   423  	t.Run("New", func(t *testing.T) {
   424  		session := &topicreadercommon.PartitionSession{}
   425  		b := newBatcher()
   426  
   427  		batch := mustNewBatch(session, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
   428  		foundRes := batcherResultCandidate{
   429  			Key:  session,
   430  			Rest: batcherMessageOrderItems{newBatcherItemBatch(batch)},
   431  		}
   432  		b.applyNeedLock(&foundRes)
   433  
   434  		expectedMap := batcherMessagesMap{session: batcherMessageOrderItems{newBatcherItemBatch(batch)}}
   435  		require.Equal(t, expectedMap, b.messages)
   436  	})
   437  
   438  	t.Run("Delete", func(t *testing.T) {
   439  		session := &topicreadercommon.PartitionSession{}
   440  		b := newBatcher()
   441  
   442  		batch := mustNewBatch(session, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
   443  
   444  		foundRes := batcherResultCandidate{
   445  			Key:  session,
   446  			Rest: batcherMessageOrderItems{},
   447  		}
   448  
   449  		b.messages = batcherMessagesMap{session: batcherMessageOrderItems{newBatcherItemBatch(batch)}}
   450  
   451  		b.applyNeedLock(&foundRes)
   452  
   453  		require.Empty(t, b.messages)
   454  	})
   455  }
   456  
   457  func TestBatcherGetOptions_Split(t *testing.T) {
   458  	t.Run("Empty", func(t *testing.T) {
   459  		opts := batcherGetOptions{}
   460  		batch := mustNewBatch(nil, []*topicreadercommon.PublicMessage{
   461  			{WrittenAt: testTime(1)},
   462  			{WrittenAt: testTime(2)},
   463  		},
   464  		)
   465  		head, rest, ok := opts.splitBatch(batch)
   466  
   467  		require.Equal(t, batch, head)
   468  		require.True(t, topicreadercommon.BatchIsEmpty(rest))
   469  		require.True(t, ok)
   470  	})
   471  	t.Run("MinCount", func(t *testing.T) {
   472  		opts := batcherGetOptions{MinCount: 2}
   473  		batch1 := mustNewBatch(nil, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
   474  		batch2 := mustNewBatch(nil, []*topicreadercommon.PublicMessage{
   475  			{WrittenAt: testTime(1)},
   476  			{WrittenAt: testTime(2)},
   477  		},
   478  		)
   479  
   480  		head, rest, ok := opts.splitBatch(batch1)
   481  		require.True(t, topicreadercommon.BatchIsEmpty(head))
   482  		require.True(t, topicreadercommon.BatchIsEmpty(rest))
   483  		require.False(t, ok)
   484  
   485  		head, rest, ok = opts.splitBatch(batch2)
   486  		require.Equal(t, batch2, head)
   487  		require.True(t, topicreadercommon.BatchIsEmpty(rest))
   488  		require.True(t, ok)
   489  	})
   490  	t.Run("MaxCount", func(t *testing.T) {
   491  		opts := batcherGetOptions{MaxCount: 2}
   492  		batch1 := mustNewBatch(nil, []*topicreadercommon.PublicMessage{{WrittenAt: testTime(1)}})
   493  		batch2 := mustNewBatch(nil, []*topicreadercommon.PublicMessage{
   494  			{WrittenAt: testTime(1)},
   495  			{WrittenAt: testTime(2)},
   496  		},
   497  		)
   498  		batch3 := mustNewBatch(
   499  			nil,
   500  			[]*topicreadercommon.PublicMessage{
   501  				{WrittenAt: testTime(11)},
   502  				{WrittenAt: testTime(12)},
   503  				{WrittenAt: testTime(13)},
   504  				{WrittenAt: testTime(14)},
   505  			},
   506  		)
   507  
   508  		head, rest, ok := opts.splitBatch(batch1)
   509  		require.Equal(t, batch1, head)
   510  		require.True(t, topicreadercommon.BatchIsEmpty(rest))
   511  		require.True(t, ok)
   512  
   513  		head, rest, ok = opts.splitBatch(batch2)
   514  		require.Equal(t, batch2, head)
   515  		require.True(t, topicreadercommon.BatchIsEmpty(rest))
   516  		require.True(t, ok)
   517  
   518  		head, rest, ok = opts.splitBatch(batch3)
   519  		expectedHead := mustNewBatch(
   520  			nil,
   521  			[]*topicreadercommon.PublicMessage{
   522  				{WrittenAt: testTime(11)},
   523  				{WrittenAt: testTime(12)},
   524  			},
   525  		)
   526  		expectedRest := mustNewBatch(
   527  			nil,
   528  			[]*topicreadercommon.PublicMessage{
   529  				{WrittenAt: testTime(13)},
   530  				{WrittenAt: testTime(14)},
   531  			},
   532  		)
   533  		require.Equal(t, expectedHead, head)
   534  		require.Equal(t, expectedRest, rest)
   535  		require.True(t, ok)
   536  	})
   537  }
   538  
   539  func TestBatcher_Fire(t *testing.T) {
   540  	t.Run("Empty", func(t *testing.T) {
   541  		b := newBatcher()
   542  		b.notifyAboutNewMessages()
   543  	})
   544  }
   545  
   546  func mustNewBatch(
   547  	session *topicreadercommon.PartitionSession,
   548  	messages []*topicreadercommon.PublicMessage,
   549  ) *topicreadercommon.PublicBatch {
   550  	return xtest.Must(topicreadercommon.NewBatch(session, messages))
   551  }
   552  
   553  func testTime(num int) time.Time {
   554  	return time.Date(2022, 6, 17, 0, 0, 0, num, time.UTC)
   555  }