-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-12332: Error partitions from topics with invalid IDs in LISR requests #10143
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 |
|---|---|---|
|
|
@@ -1364,34 +1364,59 @@ class ReplicaManager(val config: KafkaConfig, | |
| Some(partition) | ||
| } | ||
|
|
||
| // Next check partition's leader epoch | ||
| // Next check the topic ID and the partition's leader epoch | ||
| partitionOpt.foreach { partition => | ||
| val currentLeaderEpoch = partition.getLeaderEpoch | ||
| val requestLeaderEpoch = partitionState.leaderEpoch | ||
| if (requestLeaderEpoch > currentLeaderEpoch) { | ||
| // If the leader epoch is valid record the epoch of the controller that made the leadership decision. | ||
| // This is useful while updating the isr to maintain the decision maker controller's epoch in the zookeeper path | ||
| if (partitionState.replicas.contains(localBrokerId)) | ||
| partitionStates.put(partition, partitionState) | ||
| else { | ||
| stateChangeLogger.warn(s"Ignoring LeaderAndIsr request from controller $controllerId with " + | ||
| s"correlation id $correlationId epoch $controllerEpoch for partition $topicPartition as itself is not " + | ||
| s"in assigned replica list ${partitionState.replicas.asScala.mkString(",")}") | ||
| responseMap.put(topicPartition, Errors.UNKNOWN_TOPIC_OR_PARTITION) | ||
| val id = topicIds.get(topicPartition.topic()) | ||
| var invalidId = false | ||
|
|
||
| // Ensure we have not received a request from an older protocol | ||
| if (id != null && id != Uuid.ZERO_UUID) { | ||
| partition.log.foreach { log => | ||
| // Check if topic ID is in memory, if not, it must be new to the broker and does not have a metadata file. | ||
| // This is because if the broker previously wrote it to file, it would be recovered on restart after failure. | ||
| if (log.topicId == Uuid.ZERO_UUID) { | ||
| log.partitionMetadataFile.write(id) | ||
| log.topicId = id | ||
| // Warn if the topic ID in the request does not match the log. | ||
| } else if (log.topicId != id) { | ||
| stateChangeLogger.warn(s"Topic Id in memory: ${log.topicId.toString} does not" + | ||
|
jolshan marked this conversation as resolved.
Outdated
|
||
| s" match the topic Id provided in the request: " + | ||
| s"${id.toString}.") | ||
| responseMap.put(topicPartition, Errors.UNKNOWN_TOPIC_ID) | ||
|
jolshan marked this conversation as resolved.
Outdated
|
||
| invalidId = true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // If we found an invalid ID, we don't need to check the leader epoch | ||
| if (!invalidId) { | ||
| if (requestLeaderEpoch > currentLeaderEpoch) { | ||
|
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. It's a minor thing, but we can avoid this nesting by restructuring the checks a little bit. For example, it would be a good idea to have a helper in class Partition {
// Update topicid if necessary.
// Return false if the update failed because the topicId is inconsistent
def maybeUpdateTopicId(topicId: Uuid): Boolean
}
// in ReplicaManager
if (!partition.maybeUpdateTopicId(requestTopicId)) {
error(...)
responseMap.put(topicPartition, Errors.UNKNOWN_TOPIC_ID)
} else if (requestLeaderEpoch > currentLeaderEpoch) {
...
Member
Author
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. I was struggling with this since I didn't think it was the best solution. I'll take a look at the version you have here. Would we want to ever update the topic ID though? Maybe just a method to check if they are equal.
Member
Author
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. Oh hmmm I see how this could be used on the path for actually setting it. I'll think of a good name
Member
Author
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. In this case, if log is None, does it make sense to error here? If it is None, we are unable to check the topic ID |
||
| // If the leader epoch is valid record the epoch of the controller that made the leadership decision. | ||
| // This is useful while updating the isr to maintain the decision maker controller's epoch in the zookeeper path | ||
| if (partitionState.replicas.contains(localBrokerId)) | ||
| partitionStates.put(partition, partitionState) | ||
| else { | ||
| stateChangeLogger.warn(s"Ignoring LeaderAndIsr request from controller $controllerId with " + | ||
| s"correlation id $correlationId epoch $controllerEpoch for partition $topicPartition as itself is not " + | ||
| s"in assigned replica list ${partitionState.replicas.asScala.mkString(",")}") | ||
| responseMap.put(topicPartition, Errors.UNKNOWN_TOPIC_OR_PARTITION) | ||
| } | ||
| } else if (requestLeaderEpoch < currentLeaderEpoch) { | ||
| stateChangeLogger.warn(s"Ignoring LeaderAndIsr request from " + | ||
| s"controller $controllerId with correlation id $correlationId " + | ||
| s"epoch $controllerEpoch for partition $topicPartition since its associated " + | ||
| s"leader epoch $requestLeaderEpoch is smaller than the current " + | ||
| s"leader epoch $currentLeaderEpoch") | ||
| responseMap.put(topicPartition, Errors.STALE_CONTROLLER_EPOCH) | ||
| } else { | ||
| stateChangeLogger.info(s"Ignoring LeaderAndIsr request from " + | ||
| s"controller $controllerId with correlation id $correlationId " + | ||
| s"epoch $controllerEpoch for partition $topicPartition since its associated " + | ||
| s"leader epoch $requestLeaderEpoch matches the current leader epoch") | ||
| responseMap.put(topicPartition, Errors.STALE_CONTROLLER_EPOCH) | ||
| } | ||
| } else if (requestLeaderEpoch < currentLeaderEpoch) { | ||
| stateChangeLogger.warn(s"Ignoring LeaderAndIsr request from " + | ||
| s"controller $controllerId with correlation id $correlationId " + | ||
| s"epoch $controllerEpoch for partition $topicPartition since its associated " + | ||
| s"leader epoch $requestLeaderEpoch is smaller than the current " + | ||
| s"leader epoch $currentLeaderEpoch") | ||
| responseMap.put(topicPartition, Errors.STALE_CONTROLLER_EPOCH) | ||
| } else { | ||
| stateChangeLogger.info(s"Ignoring LeaderAndIsr request from " + | ||
| s"controller $controllerId with correlation id $correlationId " + | ||
| s"epoch $controllerEpoch for partition $topicPartition since its associated " + | ||
| s"leader epoch $requestLeaderEpoch matches the current leader epoch") | ||
| responseMap.put(topicPartition, Errors.STALE_CONTROLLER_EPOCH) | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1424,27 +1449,8 @@ class ReplicaManager(val config: KafkaConfig, | |
| * In this case ReplicaManager.allPartitions will map this topic-partition to an empty Partition object. | ||
| * we need to map this topic-partition to OfflinePartition instead. | ||
| */ | ||
| val local = localLog(topicPartition) | ||
| if (local.isEmpty) | ||
| if (localLog(topicPartition).isEmpty) | ||
| markPartitionOffline(topicPartition) | ||
| else { | ||
| val id = topicIds.get(topicPartition.topic()) | ||
| // Ensure we have not received a request from an older protocol | ||
| if (id != null && !id.equals(Uuid.ZERO_UUID)) { | ||
| val log = local.get | ||
| // Check if topic ID is in memory, if not, it must be new to the broker and does not have a metadata file. | ||
| // This is because if the broker previously wrote it to file, it would be recovered on restart after failure. | ||
| if (log.topicId.equals(Uuid.ZERO_UUID)) { | ||
| log.partitionMetadataFile.write(id) | ||
| log.topicId = id | ||
| // Warn if the topic ID in the request does not match the log. | ||
| } else if (!log.topicId.equals(id)) { | ||
| stateChangeLogger.warn(s"Topic Id in memory: ${log.topicId.toString} does not" + | ||
| s" match the topic Id provided in the request: " + | ||
| s"${id.toString}.") | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // we initialize highwatermark thread after the first leaderisrrequest. This ensures that all the partitions | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.