@@ -14,6 +14,7 @@ import (
14
14
"github.com/stretchr/testify/assert"
15
15
"github.com/stretchr/testify/mock"
16
16
"github.com/stretchr/testify/require"
17
+ "github.com/twmb/franz-go/pkg/kadm"
17
18
"github.com/twmb/franz-go/pkg/kgo"
18
19
19
20
"github.com/grafana/loki/v3/pkg/kafka"
@@ -232,3 +233,111 @@ func TestPartitionReader_ProcessCommits(t *testing.T) {
232
233
// We expect to have processed all the records, including initial + one per iteration.
233
234
assert .Equal (t , iterations + 1 , recordsCount )
234
235
}
236
+
237
+ func TestPartitionReader_StartsAtNextOffset (t * testing.T ) {
238
+ kaf , kafkaCfg := testkafka .CreateCluster (t , 1 , "test" )
239
+ consumer := newMockConsumer ()
240
+
241
+ kaf .CurrentNode ()
242
+ consumerFactory := func (_ Committer ) (Consumer , error ) {
243
+ return consumer , nil
244
+ }
245
+
246
+ // Produce some records
247
+ producer , err := client .NewWriterClient (kafkaCfg , 100 , log .NewNopLogger (), prometheus .NewRegistry ())
248
+ require .NoError (t , err )
249
+ stream := logproto.Stream {
250
+ Labels : labels .FromStrings ("foo" , "bar" ).String (),
251
+ }
252
+ for i := 0 ; i < 5 ; i ++ {
253
+ stream .Entries = []logproto.Entry {{Timestamp : time .Now (), Line : fmt .Sprintf ("test-%d" , i )}}
254
+ records , err := kafka .Encode (0 , "test-tenant" , stream , 10 << 20 )
255
+ require .NoError (t , err )
256
+ require .Len (t , records , 1 )
257
+
258
+ producer .ProduceSync (context .Background (), records ... )
259
+ }
260
+
261
+ // Set our offset part way through the records we just produced
262
+ offset := int64 (1 )
263
+ kafkaClient , err := client .NewReaderClient (kafkaCfg , nil , log .NewNopLogger ())
264
+ require .NoError (t , err )
265
+ admClient := kadm .NewClient (kafkaClient )
266
+ toCommit := kadm.Offsets {}
267
+ toCommit .AddOffset (kafkaCfg .Topic , 0 , offset , - 1 )
268
+ resp , err := admClient .CommitOffsets (context .Background (), "test-consumer-group" , toCommit )
269
+ require .NoError (t , err )
270
+ require .NoError (t , resp .Error ())
271
+
272
+ // Start reading
273
+ partitionReader , err := NewReader (kafkaCfg , 0 , "test-consumer-group" , consumerFactory , log .NewNopLogger (), prometheus .NewRegistry ())
274
+ require .NoError (t , err )
275
+ err = services .StartAndAwaitRunning (context .Background (), partitionReader )
276
+ require .NoError (t , err )
277
+
278
+ // Wait for records to be processed
279
+ require .Eventually (t , func () bool {
280
+ return len (consumer .recordsChan ) == 1 // All pending messages will be received in one batch
281
+ }, 10 * time .Second , 10 * time .Millisecond )
282
+
283
+ // Check we only received records from the last commit onwards, and the last committed offset is not reprocessed.
284
+ receivedRecords := <- consumer .recordsChan
285
+ require .Len (t , receivedRecords , 3 ) // Offsets are 0 based, so we should read offsets 2,3,4
286
+ for _ , record := range receivedRecords {
287
+ assert .NotContainsf (t , record .Content , "test-0" , "record %q should not contain test-0" , record .Content )
288
+ assert .NotContainsf (t , record .Content , "test-1" , "record %q should not contain test-1" , record .Content )
289
+ }
290
+
291
+ err = services .StopAndAwaitTerminated (context .Background (), partitionReader )
292
+ require .NoError (t , err )
293
+ }
294
+
295
+ func TestPartitionReader_StartsUpIfNoNewRecordsAreAvailable (t * testing.T ) {
296
+ kaf , kafkaCfg := testkafka .CreateCluster (t , 1 , "test" )
297
+ consumer := newMockConsumer ()
298
+
299
+ kaf .CurrentNode ()
300
+ consumerFactory := func (_ Committer ) (Consumer , error ) {
301
+ return consumer , nil
302
+ }
303
+
304
+ // Produce some records
305
+ producer , err := client .NewWriterClient (kafkaCfg , 100 , log .NewNopLogger (), prometheus .NewRegistry ())
306
+ require .NoError (t , err )
307
+ stream := logproto.Stream {
308
+ Labels : labels .FromStrings ("foo" , "bar" ).String (),
309
+ }
310
+ for i := 0 ; i < 5 ; i ++ {
311
+ stream .Entries = []logproto.Entry {{Timestamp : time .Now (), Line : fmt .Sprintf ("test-%d" , i )}}
312
+ records , err := kafka .Encode (0 , "test-tenant" , stream , 10 << 20 )
313
+ require .NoError (t , err )
314
+ require .Len (t , records , 1 )
315
+
316
+ producer .ProduceSync (context .Background (), records ... )
317
+ }
318
+
319
+ // Set our offset to the last record produced
320
+ offset := int64 (4 )
321
+ kafkaClient , err := client .NewReaderClient (kafkaCfg , nil , log .NewNopLogger ())
322
+ require .NoError (t , err )
323
+ admClient := kadm .NewClient (kafkaClient )
324
+ toCommit := kadm.Offsets {}
325
+ toCommit .AddOffset (kafkaCfg .Topic , 0 , offset , - 1 )
326
+ resp , err := admClient .CommitOffsets (context .Background (), "test-consumer-group" , toCommit )
327
+ require .NoError (t , err )
328
+ require .NoError (t , resp .Error ())
329
+
330
+ // Start reading
331
+ partitionReader , err := NewReader (kafkaCfg , 0 , "test-consumer-group" , consumerFactory , log .NewNopLogger (), prometheus .NewRegistry ())
332
+ require .NoError (t , err )
333
+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
334
+ defer cancel ()
335
+ err = services .StartAndAwaitRunning (ctx , partitionReader )
336
+ require .NoError (t , err )
337
+
338
+ // Check we didn't receive any records: This is a sanity check. We shouldn't get this far if we deadlock during startup.
339
+ require .Len (t , consumer .recordsChan , 0 )
340
+
341
+ err = services .StopAndAwaitTerminated (context .Background (), partitionReader )
342
+ require .NoError (t , err )
343
+ }
0 commit comments