You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kafka.apache.org by sh...@apache.org on 2023/12/15 11:21:45 UTC
(kafka) branch trunk updated: KAFKA-16013: Add metric for expiration rate of delayed remote fetch (#15014)
This is an automated email from the ASF dual-hosted git repository.
showuon pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/trunk by this push:
new 52496dcd385 KAFKA-16013: Add metric for expiration rate of delayed remote fetch (#15014)
52496dcd385 is described below
commit 52496dcd385acc6fcbae93396c47f90c0cf5cfb6
Author: Nikhil Ramakrishnan <ni...@amazon.com>
AuthorDate: Fri Dec 15 11:21:39 2023 +0000
KAFKA-16013: Add metric for expiration rate of delayed remote fetch (#15014)
Add metric for the number of expired remote fetches per second, and corresponding unit test to verify that the metric is marked on expiration.
kafka.server:type=DelayedRemoteFetchMetrics,name=ExpiresPerSec
Reviewers: Luke Chen <sh...@gmail.com>, Satish Duggana <sa...@apache.org>, Kamal Chandraprakash<ka...@gmail.com>, Christo Lolov <lo...@amazon.com>
---
.../scala/kafka/server/DelayedRemoteFetch.scala | 15 +++++-
.../kafka/server/DelayedRemoteFetchTest.scala | 59 ++++++++++++++++++++--
2 files changed, 69 insertions(+), 5 deletions(-)
diff --git a/core/src/main/scala/kafka/server/DelayedRemoteFetch.scala b/core/src/main/scala/kafka/server/DelayedRemoteFetch.scala
index 5fcf851fe83..143201f3d83 100644
--- a/core/src/main/scala/kafka/server/DelayedRemoteFetch.scala
+++ b/core/src/main/scala/kafka/server/DelayedRemoteFetch.scala
@@ -20,9 +20,10 @@ package kafka.server
import org.apache.kafka.common.TopicIdPartition
import org.apache.kafka.common.errors._
import org.apache.kafka.common.protocol.Errors
+import org.apache.kafka.server.metrics.KafkaMetricsGroup
import org.apache.kafka.storage.internals.log.{FetchParams, FetchPartitionData, LogOffsetMetadata, RemoteLogReadResult, RemoteStorageFetchInfo}
-import java.util.concurrent.{CompletableFuture, Future}
+import java.util.concurrent.{CompletableFuture, Future, TimeUnit}
import java.util.{Optional, OptionalInt, OptionalLong}
import scala.collection._
@@ -79,7 +80,12 @@ class DelayedRemoteFetch(remoteFetchTask: Future[Void],
override def onExpiration(): Unit = {
// cancel the remote storage read task, if it has not been executed yet
val cancelled = remoteFetchTask.cancel(true)
- if (!cancelled) debug(s"Remote fetch task for for RemoteStorageFetchInfo: $remoteFetchInfo could not be cancelled and its isDone value is ${remoteFetchTask.isDone}")
+ if (!cancelled) debug(s"Remote fetch task for RemoteStorageFetchInfo: $remoteFetchInfo could not be cancelled and its isDone value is ${remoteFetchTask.isDone}")
+
+ if (fetchParams.isFromFollower)
+ warn(s"The follower should not invoke remote fetch. Fetch params are: $fetchParams")
+ else
+ DelayedRemoteFetchMetrics.expiredRequestMeter.mark()
}
/**
@@ -114,3 +120,8 @@ class DelayedRemoteFetch(remoteFetchTask: Future[Void],
responseCallback(fetchPartitionData)
}
}
+
+object DelayedRemoteFetchMetrics {
+ private val metricsGroup = new KafkaMetricsGroup(DelayedRemoteFetchMetrics.getClass)
+ val expiredRequestMeter = metricsGroup.newMeter("ExpiresPerSec", "requests", TimeUnit.SECONDS)
+}
diff --git a/core/src/test/scala/integration/kafka/server/DelayedRemoteFetchTest.scala b/core/src/test/scala/integration/kafka/server/DelayedRemoteFetchTest.scala
index 007154f46de..c943258da8f 100644
--- a/core/src/test/scala/integration/kafka/server/DelayedRemoteFetchTest.scala
+++ b/core/src/test/scala/integration/kafka/server/DelayedRemoteFetchTest.scala
@@ -22,15 +22,16 @@ import org.apache.kafka.common.protocol.{ApiKeys, Errors}
import org.apache.kafka.common.record.MemoryRecords
import org.apache.kafka.common.requests.FetchRequest
import org.apache.kafka.common.{TopicIdPartition, Uuid}
+import org.apache.kafka.server.metrics.KafkaYammerMetrics
import org.apache.kafka.storage.internals.log._
import org.junit.jupiter.api.Assertions._
import org.junit.jupiter.api.Test
-import org.mockito.Mockito.{mock, when}
+import org.mockito.Mockito.{mock, verify, when}
import java.util.Optional
-import java.util.concurrent.CompletableFuture
-
+import java.util.concurrent.{CompletableFuture, Future}
import scala.collection._
+import scala.jdk.CollectionConverters._
class DelayedRemoteFetchTest {
private val maxBytes = 1024
@@ -143,6 +144,58 @@ class DelayedRemoteFetchTest {
assertEquals(Errors.FENCED_LEADER_EPOCH, fetchResultOpt.get.error)
}
+ @Test
+ def testRequestExpiry(): Unit = {
+ var actualTopicPartition: Option[TopicIdPartition] = None
+ var fetchResultOpt: Option[FetchPartitionData] = None
+
+ def callback(responses: Seq[(TopicIdPartition, FetchPartitionData)]): Unit = {
+ assertEquals(1, responses.size)
+ actualTopicPartition = Some(responses.head._1)
+ fetchResultOpt = Some(responses.head._2)
+ }
+
+ val replicaId = -1
+ val highWatermark = 100
+ val leaderLogStartOffset = 10
+
+ val remoteFetchTask = mock(classOf[Future[Void]])
+ val future: CompletableFuture[RemoteLogReadResult] = new CompletableFuture[RemoteLogReadResult]()
+ val fetchInfo: RemoteStorageFetchInfo = new RemoteStorageFetchInfo(0, false, topicIdPartition.topicPartition(), null, null, false)
+ val fetchParams = buildFollowerFetchParams(replicaId, maxWaitMs = 500)
+ val logReadInfo = buildReadResult(Errors.NONE, highWatermark, leaderLogStartOffset)
+
+ val delayedRemoteFetch = new DelayedRemoteFetch(remoteFetchTask, future, fetchInfo, Seq(topicIdPartition -> fetchStatus), fetchParams,
+ Seq(topicIdPartition -> logReadInfo), replicaManager, callback)
+
+ when(replicaManager.getPartitionOrException(topicIdPartition.topicPartition))
+ .thenReturn(mock(classOf[Partition]))
+
+ // Verify that the ExpiresPerSec metric is zero before fetching
+ val metrics = KafkaYammerMetrics.defaultRegistry.allMetrics
+ assertEquals(0, metrics.keySet.asScala.count(_.getMBeanName == "kafka.server:type=DelayedRemoteFetchMetrics,name=ExpiresPerSec"))
+
+ // Force the delayed remote fetch to expire
+ delayedRemoteFetch.run()
+
+ // Check that the task was cancelled and force-completed
+ verify(remoteFetchTask).cancel(true)
+ assertTrue(delayedRemoteFetch.isCompleted)
+
+ // Check that the ExpiresPerSec metric was incremented
+ assertEquals(1, metrics.keySet.asScala.count(_.getMBeanName == "kafka.server:type=DelayedRemoteFetchMetrics,name=ExpiresPerSec"))
+
+ // Fetch results should still include local read results
+ assertTrue(actualTopicPartition.isDefined)
+ assertEquals(topicIdPartition, actualTopicPartition.get)
+ assertTrue(fetchResultOpt.isDefined)
+
+ val fetchResult = fetchResultOpt.get
+ assertEquals(Errors.NONE, fetchResult.error)
+ assertEquals(highWatermark, fetchResult.highWatermark)
+ assertEquals(leaderLogStartOffset, fetchResult.logStartOffset)
+ }
+
private def buildFollowerFetchParams(replicaId: Int,
maxWaitMs: Int): FetchParams = {
new FetchParams(