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
KAFKA-12403; Ensure local state deleted on RemoveTopicRecord
received
#10252
Changes from 5 commits
d0a0c65
1b6eec4
2bec718
9043cd9
89642e6
851e9c3
85c26fa
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 |
---|---|---|
|
@@ -116,21 +116,48 @@ class MetadataPartitionsBuilder(val brokerId: Int, | |
val prevPartitions: MetadataPartitions) { | ||
private var newNameMap = prevPartitions.copyNameMap() | ||
private var newIdMap = prevPartitions.copyIdMap() | ||
private var newReverseIdMap = prevPartitions.copyReverseIdMap() | ||
private val changed = Collections.newSetFromMap[Any](new util.IdentityHashMap()) | ||
private val _localChanged = new util.HashSet[MetadataPartition] | ||
private val _localRemoved = new util.HashSet[MetadataPartition] | ||
|
||
def topicIdToName(id: Uuid): Option[String] = Option(newIdMap.get(id)) | ||
|
||
def topicNameToId(name: String): Option[Uuid] = Option(newReverseIdMap.get(name)) | ||
|
||
def removeTopicById(id: Uuid): Iterable[MetadataPartition] = { | ||
Option(newIdMap.remove(id)) match { | ||
case None => throw new RuntimeException(s"Unable to locate topic with ID $id") | ||
case Some(name) => newNameMap.remove(name).values().asScala | ||
val name = Option(newIdMap.remove(id)).getOrElse { | ||
throw new RuntimeException(s"Unable to locate topic with ID $id") | ||
} | ||
|
||
newReverseIdMap.remove(name) | ||
|
||
val prevPartitionMap = newNameMap.remove(name) | ||
if (prevPartitionMap == null) { | ||
Seq.empty | ||
} else { | ||
changed.remove(prevPartitionMap) | ||
|
||
val removedPartitions = prevPartitionMap.values | ||
if (prevImageHasTopicId(id)) { | ||
removedPartitions.forEach { partition => | ||
if (partition.isReplicaFor(brokerId)) { | ||
_localRemoved.add(partition) | ||
} | ||
} | ||
} else { | ||
removedPartitions.forEach { partition => | ||
if (partition.isReplicaFor(brokerId)) { | ||
_localChanged.remove(partition) | ||
} | ||
} | ||
} | ||
removedPartitions.asScala | ||
} | ||
} | ||
|
||
def handleChange(record: PartitionChangeRecord): Unit = { | ||
Option(newIdMap.get(record.topicId())) match { | ||
topicIdToName(record.topicId) match { | ||
case None => throw new RuntimeException(s"Unable to locate topic with ID ${record.topicId()}") | ||
case Some(name) => Option(newNameMap.get(name)) match { | ||
case None => throw new RuntimeException(s"Unable to locate topic with name $name") | ||
|
@@ -144,10 +171,14 @@ class MetadataPartitionsBuilder(val brokerId: Int, | |
|
||
def addUuidMapping(name: String, id: Uuid): Unit = { | ||
newIdMap.put(id, name) | ||
newReverseIdMap.put(name, id) | ||
} | ||
|
||
def removeUuidMapping(id: Uuid): Unit = { | ||
newIdMap.remove(id) | ||
val topicName = newIdMap.remove(id) | ||
if (topicName != null) { | ||
newReverseIdMap.remove(topicName) | ||
} | ||
} | ||
|
||
def get(topicName: String, partitionId: Int): Option[MetadataPartition] = { | ||
|
@@ -171,25 +202,38 @@ class MetadataPartitionsBuilder(val brokerId: Int, | |
val prevPartition = newPartitionMap.put(partition.partitionIndex, partition) | ||
if (partition.isReplicaFor(brokerId)) { | ||
_localChanged.add(partition) | ||
chia7712 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else if (prevPartition != null && prevPartition.isReplicaFor(brokerId)) { | ||
_localRemoved.add(prevPartition) | ||
} else if (prevPartition != null) { | ||
maybeAddToLocalRemoved(prevPartition) | ||
} | ||
newNameMap.put(partition.topicName, newPartitionMap) | ||
} | ||
|
||
private def maybeAddToLocalRemoved(partition: MetadataPartition): Unit = { | ||
if (partition.isReplicaFor(brokerId)) { | ||
val currentTopicId = newReverseIdMap.get(partition.topicName) | ||
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. Is it possible that 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. For another, could it be replaced by 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. The intent is to only return the change in It's worth noting that this is strictly more defensive than the current replay logic requires. A new image is built for each batch of records from the controller, and we would never see a topic deleted and recreated (or vice versa) in the same batch. This is an implicit contract though and not protected by the builder API, so I thought we might as well try to make the logic more resilient. 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. Thanks for nice explanation. I have another question for this check. It seems to me three collections in |
||
val prevImageHasTopic = if (currentTopicId != null) { | ||
prevImageHasTopicId(currentTopicId) | ||
} else { | ||
prevPartitions.allTopicNames().contains(partition.topicName) | ||
} | ||
|
||
if (prevImageHasTopic) { | ||
_localRemoved.add(partition) | ||
} | ||
} | ||
} | ||
|
||
private def prevImageHasTopicId(topicId: Uuid): Boolean = { | ||
prevPartitions.topicIdToName(topicId).isDefined | ||
} | ||
|
||
def remove(topicName: String, partitionId: Int): Unit = { | ||
val prevPartitionMap = newNameMap.get(topicName) | ||
if (prevPartitionMap != null) { | ||
if (changed.contains(prevPartitionMap)) { | ||
val prevPartition = prevPartitionMap.remove(partitionId) | ||
if (prevPartition.isReplicaFor(brokerId)) { | ||
_localRemoved.add(prevPartition) | ||
} | ||
val removedPartition = if (changed.contains(prevPartitionMap)) { | ||
Option(prevPartitionMap.remove(partitionId)) | ||
} else { | ||
Option(prevPartitionMap.get(partitionId)).foreach { prevPartition => | ||
if (prevPartition.isReplicaFor(brokerId)) { | ||
_localRemoved.add(prevPartition) | ||
} | ||
Option(prevPartitionMap.get(partitionId)).map { prevPartition => | ||
val newPartitionMap = new util.HashMap[Int, MetadataPartition](prevPartitionMap.size() - 1) | ||
prevPartitionMap.forEach { (prevPartitionId, prevPartition) => | ||
if (!prevPartitionId.equals(partitionId)) { | ||
hachikuji marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
@@ -198,15 +242,18 @@ class MetadataPartitionsBuilder(val brokerId: Int, | |
} | ||
changed.add(newPartitionMap) | ||
newNameMap.put(topicName, newPartitionMap) | ||
prevPartition | ||
} | ||
} | ||
removedPartition.foreach(maybeAddToLocalRemoved) | ||
} | ||
} | ||
|
||
def build(): MetadataPartitions = { | ||
val result = MetadataPartitions(newNameMap, newIdMap) | ||
val result = new MetadataPartitions(newNameMap, newIdMap, newReverseIdMap) | ||
chia7712 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
newNameMap = Collections.unmodifiableMap(newNameMap) | ||
chia7712 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
newIdMap = Collections.unmodifiableMap(newIdMap) | ||
newReverseIdMap = Collections.unmodifiableMap(newReverseIdMap) | ||
result | ||
} | ||
|
||
|
@@ -232,15 +279,15 @@ case class MetadataPartitions(private val nameMap: util.Map[String, util.Map[Int | |
def topicNameToId(name: String): Option[Uuid] = Option(reverseIdMap.get(name)) | ||
|
||
def copyNameMap(): util.Map[String, util.Map[Int, MetadataPartition]] = { | ||
val copy = new util.HashMap[String, util.Map[Int, MetadataPartition]](nameMap.size()) | ||
copy.putAll(nameMap) | ||
copy | ||
new util.HashMap(nameMap) | ||
} | ||
|
||
def copyIdMap(): util.Map[Uuid, String] = { | ||
val copy = new util.HashMap[Uuid, String](idMap.size()) | ||
copy.putAll(idMap) | ||
copy | ||
new util.HashMap(idMap) | ||
} | ||
|
||
def copyReverseIdMap(): util.Map[String, Uuid] = { | ||
hachikuji marked this conversation as resolved.
Show resolved
Hide resolved
|
||
new util.HashMap(reverseIdMap) | ||
} | ||
|
||
def allPartitions(): Iterator[MetadataPartition] = new AllPartitionsIterator(nameMap).asScala | ||
|
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.
Is this TODO no longer relevant?