Skip to content

Commit 42d1272

Browse files
feat(metadata): return leader info after partition success failover (#2051)
* feat(metadata): change KVImage state to TimelineHashMap * feat(metadata): change KVImage state to TimelineHashMap * feat(metadata): change KVImage state to TimelineHashMap * feat(metadata): return leader info after partition success failover --------- Signed-off-by: lifepuzzlefun <[email protected]>
1 parent bc63e6b commit 42d1272

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

core/src/main/scala/kafka/log/streamaspect/ElasticLog.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ object ElasticLog extends Logging {
602602

603603
private val META_SCHEDULE_EXECUTOR = Executors.newScheduledThreadPool(1, ThreadUtils.createThreadFactory("log-meta-schedule-executor", true))
604604

605-
private def formatStreamKey(namespace: String, topicPartition: TopicPartition, topicId: Option[Uuid]): String = {
605+
def formatStreamKey(namespace: String, topicPartition: TopicPartition, topicId: Option[Uuid]): String = {
606606
if (topicId.isEmpty) {
607607
namespace + "/" + topicPartition.topic() + "/" + topicPartition.partition()
608608
} else {

core/src/main/scala/kafka/server/metadata/KRaftMetadataCache.scala

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717

1818
package kafka.server.metadata
1919

20+
import com.automq.stream.api.KeyValue
21+
import com.automq.stream.s3.metadata.StreamState
22+
import io.netty.buffer.Unpooled
23+
import kafka.log.streamaspect.{ElasticLog, ElasticLogManager}
2024
import kafka.server.{CachedControllerId, KRaftCachedControllerId, MetadataCache}
2125
import kafka.utils.Logging
2226
import org.apache.kafka.admin.BrokerMetadata
@@ -31,7 +35,7 @@ import org.apache.kafka.common.message._
3135
import org.apache.kafka.common.network.ListenerName
3236
import org.apache.kafka.common.protocol.Errors
3337
import org.apache.kafka.common.requests.MetadataResponse
34-
import org.apache.kafka.image.MetadataImage
38+
import org.apache.kafka.image.{MetadataImage, S3StreamMetadataImage}
3539
import org.apache.kafka.metadata.{BrokerRegistration, PartitionRegistration, Replicas}
3640
import org.apache.kafka.server.common.automq.AutoMQVersion
3741
import org.apache.kafka.server.common.{FinalizedFeatures, KRaftVersion, MetadataVersion}
@@ -87,6 +91,42 @@ class KRaftMetadataCache(
8791

8892
def currentImage(): MetadataImage = _currentImage
8993

94+
95+
// AutoMQ inject start
96+
private def checkFailoverSuccess(topicPartition: TopicPartition, topicId: Uuid, tpRegistration: PartitionRegistration): Boolean = {
97+
safeRun((image: MetadataImage) => {
98+
val key = ElasticLog.formatStreamKey(ElasticLogManager.NAMESPACE, topicPartition, Some(topicId))
99+
val buffer = image.kv().getValue(key)
100+
if (buffer == null) {
101+
error(s"topic ${topicPartition} topicId ${topicId.toString} key $key not found ")
102+
return false
103+
}
104+
105+
val metaStreamId = Unpooled.wrappedBuffer(KeyValue.Value.of(buffer).get).readLong
106+
val streamMetadata = image.streamsMetadata.getStreamMetadata(metaStreamId)
107+
if (streamMetadata == null) {
108+
false
109+
} else {
110+
return topicFailoverSuccess(topicPartition, tpRegistration, streamMetadata)
111+
}
112+
})
113+
}
114+
115+
private def topicFailoverSuccess(topicPartition: TopicPartition, tpRegistration: PartitionRegistration, streamMetadata: S3StreamMetadataImage): Boolean = {
116+
// the partition leaderEpoch may be different from the stream epoch
117+
val result = tpRegistration.leaderEpoch >= streamMetadata.getEpoch &&
118+
streamMetadata.state == StreamState.OPENED &&
119+
streamMetadata.lastRange().nodeId() == tpRegistration.leader
120+
121+
if (!result) {
122+
debug(s"Failover failed for topicPartition $topicPartition, tpEpoch $tpRegistration, streamMetadata ${streamMetadata}")
123+
}
124+
125+
result
126+
}
127+
128+
// AutoMQ inject end
129+
90130
// errorUnavailableEndpoints exists to support v0 MetadataResponses
91131
// If errorUnavailableListeners=true, return LISTENER_NOT_FOUND if listener is missing on the broker.
92132
// Otherwise, return LEADER_NOT_AVAILABLE for broker unavailable and missing listener (Metadata response v5 and below).
@@ -102,10 +142,17 @@ class KRaftMetadataCache(
102142
val filteredIsr = maybeFilterAliveReplicas(image, partition.isr, listenerName,
103143
errorUnavailableEndpoints)
104144
val offlineReplicas = getOfflineReplicas(image, partition, listenerName)
105-
val maybeLeader = getAliveEndpoint(image, partition.leader, listenerName)
145+
// AutoMQ inject start
146+
val failoverSuccess = checkFailoverSuccess(new TopicPartition(topicName, partitionId), topic.id(), partition)
147+
val maybeLeader = if (!failoverSuccess) {
148+
None
149+
} else {
150+
getAliveEndpoint(image, partition.leader, listenerName)
151+
}
152+
// AutoMQ inject end
106153
maybeLeader match {
107154
case None =>
108-
val error = if (!image.cluster().brokers.containsKey(partition.leader)) {
155+
val error = if (!failoverSuccess || !image.cluster().brokers.containsKey(partition.leader)) {
109156
debug(s"Error while fetching metadata for $topicName-$partitionId: leader not available")
110157
Errors.LEADER_NOT_AVAILABLE
111158
} else {

0 commit comments

Comments
 (0)