@@ -6479,6 +6479,103 @@ public void testFindLastOffsetAcknowledgedWhenGapAtBeginning() {
6479
6479
assertEquals (-1 , lastOffsetAcknowledged );
6480
6480
}
6481
6481
6482
+ @ Test
6483
+ public void testCacheUpdateWhenBatchHasOngoingTransition () {
6484
+ Persister persister = Mockito .mock (Persister .class );
6485
+
6486
+ SharePartition sharePartition = SharePartitionBuilder .builder ()
6487
+ .withState (SharePartitionState .ACTIVE )
6488
+ .withPersister (persister )
6489
+ .build ();
6490
+ // Acquire a single batch.
6491
+ fetchAcquiredRecords (
6492
+ sharePartition .acquire (MEMBER_ID , BATCH_SIZE , MAX_FETCH_RECORDS , 21 ,
6493
+ fetchPartitionData (memoryRecords (10 , 21 )), FETCH_ISOLATION_HWM
6494
+ ), 10
6495
+ );
6496
+
6497
+ // Validate that there is no ongoing transition.
6498
+ assertFalse (sharePartition .cachedState ().get (21L ).batchHasOngoingStateTransition ());
6499
+ // Return a future which will be completed later, so the batch state has ongoing transition.
6500
+ CompletableFuture <WriteShareGroupStateResult > future = new CompletableFuture <>();
6501
+ Mockito .when (persister .writeState (Mockito .any ())).thenReturn (future );
6502
+ // Acknowledge batch to create ongoing transition.
6503
+ sharePartition .acknowledge (MEMBER_ID , List .of (new ShareAcknowledgementBatch (21 , 30 , List .of (AcknowledgeType .ACCEPT .id ))));
6504
+
6505
+ // Assert the start offset has not moved and batch has ongoing transition.
6506
+ assertEquals (21L , sharePartition .startOffset ());
6507
+ assertEquals (1 , sharePartition .cachedState ().size ());
6508
+ assertTrue (sharePartition .cachedState ().get (21L ).batchHasOngoingStateTransition ());
6509
+
6510
+ // Validate that offset can't be moved because batch has ongoing transition.
6511
+ assertFalse (sharePartition .canMoveStartOffset ());
6512
+ assertEquals (-1 , sharePartition .findLastOffsetAcknowledged ());
6513
+
6514
+ // Complete the future so acknowledge API can be completed, which updates the cache.
6515
+ WriteShareGroupStateResult writeShareGroupStateResult = Mockito .mock (WriteShareGroupStateResult .class );
6516
+ Mockito .when (writeShareGroupStateResult .topicsData ()).thenReturn (List .of (
6517
+ new TopicData <>(TOPIC_ID_PARTITION .topicId (), List .of (
6518
+ PartitionFactory .newPartitionErrorData (0 , Errors .NONE .code (), Errors .NONE .message ())))));
6519
+ future .complete (writeShareGroupStateResult );
6520
+
6521
+ // Validate the cache has been updated.
6522
+ assertEquals (31L , sharePartition .startOffset ());
6523
+ assertTrue (sharePartition .cachedState ().isEmpty ());
6524
+ }
6525
+
6526
+ @ Test
6527
+ public void testCacheUpdateWhenOffsetStateHasOngoingTransition () {
6528
+ Persister persister = Mockito .mock (Persister .class );
6529
+
6530
+ SharePartition sharePartition = SharePartitionBuilder .builder ()
6531
+ .withState (SharePartitionState .ACTIVE )
6532
+ .withPersister (persister )
6533
+ .build ();
6534
+ // Acquire a single batch.
6535
+ fetchAcquiredRecords (
6536
+ sharePartition .acquire (MEMBER_ID , BATCH_SIZE , MAX_FETCH_RECORDS , 21 ,
6537
+ fetchPartitionData (memoryRecords (10 , 21 )), FETCH_ISOLATION_HWM
6538
+ ), 10
6539
+ );
6540
+
6541
+ // Validate that there is no ongoing transition.
6542
+ assertFalse (sharePartition .cachedState ().get (21L ).batchHasOngoingStateTransition ());
6543
+ assertNull (sharePartition .cachedState ().get (21L ).offsetState ());
6544
+ // Return a future which will be completed later, so the batch state has ongoing transition.
6545
+ CompletableFuture <WriteShareGroupStateResult > future = new CompletableFuture <>();
6546
+ Mockito .when (persister .writeState (Mockito .any ())).thenReturn (future );
6547
+ // Acknowledge offsets to create ongoing transition.
6548
+ sharePartition .acknowledge (MEMBER_ID , List .of (new ShareAcknowledgementBatch (21 , 23 , List .of (AcknowledgeType .ACCEPT .id ))));
6549
+
6550
+ // Assert the start offset has not moved and offset state is now maintained. Offset state should
6551
+ // have ongoing transition.
6552
+ assertEquals (21L , sharePartition .startOffset ());
6553
+ assertEquals (1 , sharePartition .cachedState ().size ());
6554
+ assertNotNull (sharePartition .cachedState ().get (21L ).offsetState ());
6555
+ assertTrue (sharePartition .cachedState ().get (21L ).offsetState ().get (21L ).hasOngoingStateTransition ());
6556
+ assertTrue (sharePartition .cachedState ().get (21L ).offsetState ().get (22L ).hasOngoingStateTransition ());
6557
+ assertTrue (sharePartition .cachedState ().get (21L ).offsetState ().get (23L ).hasOngoingStateTransition ());
6558
+ // Only 21, 22 and 23 offsets should have ongoing state transition as the acknowledge request
6559
+ // contains 21-23 offsets.
6560
+ assertFalse (sharePartition .cachedState ().get (21L ).offsetState ().get (24L ).hasOngoingStateTransition ());
6561
+
6562
+ // Validate that offset can't be moved because batch has ongoing transition.
6563
+ assertFalse (sharePartition .canMoveStartOffset ());
6564
+ assertEquals (-1 , sharePartition .findLastOffsetAcknowledged ());
6565
+
6566
+ // Complete the future so acknowledge API can be completed, which updates the cache.
6567
+ WriteShareGroupStateResult writeShareGroupStateResult = Mockito .mock (WriteShareGroupStateResult .class );
6568
+ Mockito .when (writeShareGroupStateResult .topicsData ()).thenReturn (List .of (
6569
+ new TopicData <>(TOPIC_ID_PARTITION .topicId (), List .of (
6570
+ PartitionFactory .newPartitionErrorData (0 , Errors .NONE .code (), Errors .NONE .message ())))));
6571
+ future .complete (writeShareGroupStateResult );
6572
+
6573
+ // Validate the cache has been updated.
6574
+ assertEquals (24L , sharePartition .startOffset ());
6575
+ assertEquals (1 , sharePartition .cachedState ().size ());
6576
+ assertNotNull (sharePartition .cachedState ().get (21L ));
6577
+ }
6578
+
6482
6579
/**
6483
6580
* Test the case where the fetch batch has first record offset greater than the record batch start offset.
6484
6581
* Such batches can exist for compacted topics.
0 commit comments