@ -18,6 +18,7 @@
@@ -18,6 +18,7 @@
package kafka.coordinator.group
import java.util.Properties
import java.util.concurrent.locks. { Lock , ReentrantLock }
import java.util.concurrent. { ConcurrentHashMap , TimeUnit }
import kafka.common.OffsetAndMetadata
@ -60,16 +61,6 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -60,16 +61,6 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
new LeaveGroupOperation
)
private val allOperationsWithTxn = Seq (
new JoinGroupOperation ,
new SyncGroupOperation ,
new OffsetFetchOperation ,
new CommitTxnOffsetsOperation ,
new CompleteTxnOperation ,
new HeartbeatOperation ,
new LeaveGroupOperation
)
var heartbeatPurgatory : DelayedOperationPurgatory [ DelayedHeartbeat ] = _
var joinPurgatory : DelayedOperationPurgatory [ DelayedJoin ] = _
var groupCoordinator : GroupCoordinator = _
@ -119,12 +110,33 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -119,12 +110,33 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@Test
def testConcurrentTxnGoodPathSequence ( ) : Unit = {
verifyConcurrentOperations ( createGroupMembers , allOperationsWithTxn )
verifyConcurrentOperations ( createGroupMembers , Seq (
new JoinGroupOperation ,
new SyncGroupOperation ,
new OffsetFetchOperation ,
new CommitTxnOffsetsOperation ,
new CompleteTxnOperation ,
new HeartbeatOperation ,
new LeaveGroupOperation
) )
}
@Test
def testConcurrentRandomSequence ( ) : Unit = {
verifyConcurrentRandomSequences ( createGroupMembers , allOperationsWithTxn )
/* *
* handleTxnCommitOffsets does not complete delayed requests now so it causes error if handleTxnCompletion is executed
* before completing delayed request . In random mode , we use this global lock to prevent such an error .
*/
val lock = new ReentrantLock ( )
verifyConcurrentRandomSequences ( createGroupMembers , Seq (
new JoinGroupOperation ,
new SyncGroupOperation ,
new OffsetFetchOperation ,
new CommitTxnOffsetsOperation ( lock = Some ( lock ) ) ,
new CompleteTxnOperation ( lock = Some ( lock ) ) ,
new HeartbeatOperation ,
new LeaveGroupOperation
) )
}
@Test
@ -198,6 +210,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -198,6 +210,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
groupCoordinator . handleJoinGroup ( member . groupId , member . memberId , None , requireKnownMemberId = false , "clientId" , "clientHost" ,
DefaultRebalanceTimeout , DefaultSessionTimeout ,
protocolType , protocols , responseCallback )
replicaManager . tryCompleteActions ( )
}
override def awaitAndVerify ( member : GroupMember ) : Unit = {
val joinGroupResult = await ( member , DefaultRebalanceTimeout )
@ -221,6 +234,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -221,6 +234,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
groupCoordinator . handleSyncGroup ( member . groupId , member . generationId , member . memberId ,
Some ( protocolType ) , Some ( protocolName ) , member . groupInstanceId , Map . empty [ String , Array [ Byte ] ] , responseCallback )
}
replicaManager . tryCompleteActions ( )
}
override def awaitAndVerify ( member : GroupMember ) : Unit = {
val result = await ( member , DefaultSessionTimeout )
@ -238,6 +252,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -238,6 +252,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
override def runWithCallback ( member : GroupMember , responseCallback : HeartbeatCallback ) : Unit = {
groupCoordinator . handleHeartbeat ( member . groupId , member . memberId ,
member . groupInstanceId , member . generationId , responseCallback )
replicaManager . tryCompleteActions ( )
}
override def awaitAndVerify ( member : GroupMember ) : Unit = {
val error = await ( member , DefaultSessionTimeout )
@ -252,6 +267,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -252,6 +267,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
}
override def runWithCallback ( member : GroupMember , responseCallback : OffsetFetchCallback ) : Unit = {
val ( error , partitionData ) = groupCoordinator . handleFetchOffsets ( member . groupId , requireStable = true , None )
replicaManager . tryCompleteActions ( )
responseCallback ( error , partitionData )
}
override def awaitAndVerify ( member : GroupMember ) : Unit = {
@ -271,6 +287,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -271,6 +287,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
val offsets = immutable . Map ( tp -> OffsetAndMetadata ( 1 , "" , Time . SYSTEM . milliseconds ( ) ) )
groupCoordinator . handleCommitOffsets ( member . groupId , member . memberId ,
member . groupInstanceId , member . generationId , offsets , responseCallback )
replicaManager . tryCompleteActions ( )
}
override def awaitAndVerify ( member : GroupMember ) : Unit = {
val offsets = await ( member , 500 )
@ -278,7 +295,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -278,7 +295,7 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
}
}
class CommitTxnOffsetsOperation extends CommitOffsetsOperation {
class CommitTxnOffsetsOperation ( lock : Option [ Lock ] = None ) extends CommitOffsetsOperation {
override def runWithCallback ( member : GroupMember , responseCallback : CommitOffsetCallback ) : Unit = {
val tp = new TopicPartition ( "topic" , 0 )
val offsets = immutable . Map ( tp -> OffsetAndMetadata ( 1 , "" , Time . SYSTEM . milliseconds ( ) ) )
@ -293,13 +310,17 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -293,13 +310,17 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
offsetsPartitions . map ( _ . partition ) . toSet , isCommit = random . nextBoolean )
responseCallback ( errors )
}
lock . foreach ( _ . lock ( ) )
try {
groupCoordinator . handleTxnCommitOffsets ( member . group . groupId , producerId , producerEpoch ,
JoinGroupRequest . UNKNOWN_MEMBER_ID , Option . empty , JoinGroupRequest . UNKNOWN_GENERATION_ID ,
offsets , callbackWithTxnCompletion )
replicaManager . tryCompleteActions ( )
} finally lock . foreach ( _ . unlock ( ) )
}
}
class CompleteTxnOperation extends GroupOperation [ CompleteTxnCallbackParams , CompleteTxnCallback ] {
class CompleteTxnOperation ( lock : Option [ Lock ] = None ) extends GroupOperation [ CompleteTxnCallbackParams , CompleteTxnCallback ] {
override def responseCallback ( responsePromise : Promise [ CompleteTxnCallbackParams ] ) : CompleteTxnCallback = {
val callback : CompleteTxnCallback = error => responsePromise . success ( error )
callback
@ -307,9 +328,13 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
@@ -307,9 +328,13 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
override def runWithCallback ( member : GroupMember , responseCallback : CompleteTxnCallback ) : Unit = {
val producerId = 1000L
val offsetsPartitions = ( 0 to numPartitions ) . map ( new TopicPartition ( Topic . GROUP_METADATA_TOPIC_NAME , _ ) )
lock . foreach ( _ . lock ( ) )
try {
groupCoordinator . groupManager . handleTxnCompletion ( producerId ,
offsetsPartitions . map ( _ . partition ) . toSet , isCommit = random . nextBoolean )
responseCallback ( Errors . NONE )
} finally lock . foreach ( _ . unlock ( ) )
}
override def awaitAndVerify ( member : GroupMember ) : Unit = {
val error = await ( member , 500 )