-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-9212; Ensure LeaderAndIsr state updated in controller context during reassignment #7795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1081,14 +1081,16 @@ class KafkaController(val config: KafkaConfig, | |
| val UpdateLeaderAndIsrResult(finishedUpdates, _) = | ||
| zkClient.updateLeaderAndIsr(immutable.Map(partition -> newLeaderAndIsr), epoch, controllerContext.epochZkVersion) | ||
|
|
||
| finishedUpdates.headOption.map { | ||
| case (partition, Right(leaderAndIsr)) => | ||
| finalLeaderIsrAndControllerEpoch = Some(LeaderIsrAndControllerEpoch(leaderAndIsr, epoch)) | ||
| finishedUpdates.get(partition).exists { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: I think it would probably be clearer to just go with a Some/None pattern match given the side-effects we're doing below (throwing exceptions, mutations, etc). |
||
| case Right(leaderAndIsr) => | ||
| val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(leaderAndIsr, epoch) | ||
| controllerContext.partitionLeadershipInfo.put(partition, leaderIsrAndControllerEpoch) | ||
| finalLeaderIsrAndControllerEpoch = Some(leaderIsrAndControllerEpoch) | ||
| info(s"Updated leader epoch for partition $partition to ${leaderAndIsr.leaderEpoch}") | ||
| true | ||
| case (_, Left(e)) => | ||
| case Left(e) => | ||
| throw e | ||
| }.getOrElse(false) | ||
| } | ||
| case None => | ||
| throw new IllegalStateException(s"Cannot update leader epoch for partition $partition as " + | ||
| "leaderAndIsr path is empty. This could mean we somehow tried to reassign a partition that doesn't exist") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -750,6 +750,41 @@ class ReassignPartitionsClusterTest extends ZooKeeperTestHarness with Logging { | |
| assertEquals(Seq(101), zkClient.getReplicasForPartition(tp0)) | ||
| } | ||
|
|
||
| @Test | ||
| def testProduceAndConsumeWithReassignmentInProgress(): Unit = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice test! |
||
| startBrokers(Seq(100, 101)) | ||
| adminClient = createAdminClient(servers) | ||
| createTopic(zkClient, topicName, Map(tp0.partition() -> Seq(100)), servers = servers) | ||
|
|
||
| produceMessages(tp0.topic, 500, acks = -1, valueLength = 100 * 1000) | ||
|
|
||
| TestUtils.throttleAllBrokersReplication(adminClient, Seq(101), throttleBytes = 1) | ||
| TestUtils.assignThrottledPartitionReplicas(adminClient, Map(tp0 -> Seq(101))) | ||
|
|
||
| adminClient.alterPartitionReassignments( | ||
| Map(reassignmentEntry(tp0, Seq(100, 101))).asJava | ||
| ).all().get() | ||
|
|
||
| awaitReassignmentInProgress(tp0) | ||
|
|
||
| produceMessages(tp0.topic, 500, acks = -1, valueLength = 64) | ||
| val consumer = TestUtils.createConsumer(TestUtils.getBrokerListStrFromServers(servers)) | ||
| try { | ||
| consumer.assign(Seq(tp0).asJava) | ||
| pollUntilAtLeastNumRecords(consumer, numRecords = 1000) | ||
| } finally { | ||
| consumer.close() | ||
| } | ||
|
|
||
| assertTrue(isAssignmentInProgress(tp0)) | ||
|
|
||
| TestUtils.resetBrokersThrottle(adminClient, Seq(101)) | ||
| TestUtils.removePartitionReplicaThrottles(adminClient, Set(tp0)) | ||
|
|
||
| waitForAllReassignmentsToComplete() | ||
| assertEquals(Seq(100, 101), zkClient.getReplicasForPartition(tp0)) | ||
| } | ||
|
|
||
| @Test | ||
| def shouldListMovingPartitionsThroughApi(): Unit = { | ||
| startBrokers(Seq(100, 101)) | ||
|
|
@@ -1228,6 +1263,16 @@ class ReassignPartitionsClusterTest extends ZooKeeperTestHarness with Logging { | |
| s"Znode ${ReassignPartitionsZNode.path} wasn't deleted", pause = pause) | ||
| } | ||
|
|
||
| def awaitReassignmentInProgress(topicPartition: TopicPartition): Unit = { | ||
| waitUntilTrue(() => isAssignmentInProgress(topicPartition), | ||
| "Timed out waiting for expected reassignment to begin") | ||
| } | ||
|
|
||
| def isAssignmentInProgress(topicPartition: TopicPartition): Boolean = { | ||
| val reassignments = adminClient.listPartitionReassignments().reassignments().get() | ||
| reassignments.asScala.get(topicPartition).isDefined | ||
| } | ||
|
|
||
| def waitForAllReassignmentsToComplete(pause: Long = 100L): Unit = { | ||
| waitUntilTrue(() => adminClient.listPartitionReassignments().reassignments().get().isEmpty, | ||
| s"There still are ongoing reassignments", pause = pause) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note this was a bug. We were using the leader epoch from the response even if it was stale and we had taken the
PartitionInfofrom the previous update.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a test that covers this bug too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe
testStaleMetadata()does that