-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-16452: Don't throw OOORE when converting the offset to metadata #15825
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 all commits
33bf18b
5fc04f6
9143e45
5345bd3
d23f426
a4e31e0
afca50e
ff3ab0e
5945dec
9f22047
f26d381
14efe65
503732a
06ee554
56604d6
ce09bcd
7e97904
eb75573
f5ce2a6
6e3f113
7ce211e
5d080ad
34d8ca0
59e6a7b
21a6d27
91bd96b
d4668be
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 |
|---|---|---|
|
|
@@ -28,6 +28,8 @@ import org.apache.kafka.common.requests.FetchRequest | |
| import org.apache.kafka.storage.internals.log.{FetchDataInfo, FetchIsolation, FetchParams, FetchPartitionData, LogOffsetMetadata, LogOffsetSnapshot} | ||
| import org.junit.jupiter.api.Test | ||
| import org.junit.jupiter.api.Assertions._ | ||
| import org.junit.jupiter.params.ParameterizedTest | ||
| import org.junit.jupiter.params.provider.ValueSource | ||
| import org.mockito.ArgumentMatchers.{any, anyInt} | ||
| import org.mockito.Mockito.{mock, when} | ||
|
|
||
|
|
@@ -46,7 +48,7 @@ class DelayedFetchTest { | |
|
|
||
| val fetchStatus = FetchPartitionStatus( | ||
| startOffsetMetadata = new LogOffsetMetadata(fetchOffset), | ||
| fetchInfo = new FetchRequest.PartitionData(Uuid.ZERO_UUID, fetchOffset, logStartOffset, maxBytes, currentLeaderEpoch)) | ||
| fetchInfo = new FetchRequest.PartitionData(topicIdPartition.topicId(), fetchOffset, logStartOffset, maxBytes, currentLeaderEpoch)) | ||
| val fetchParams = buildFollowerFetchParams(replicaId, maxWaitMs = 500) | ||
|
|
||
| var fetchResultOpt: Option[FetchPartitionData] = None | ||
|
|
@@ -92,7 +94,7 @@ class DelayedFetchTest { | |
|
|
||
| val fetchStatus = FetchPartitionStatus( | ||
| startOffsetMetadata = new LogOffsetMetadata(fetchOffset), | ||
| fetchInfo = new FetchRequest.PartitionData(Uuid.ZERO_UUID, fetchOffset, logStartOffset, maxBytes, currentLeaderEpoch)) | ||
| fetchInfo = new FetchRequest.PartitionData(topicIdPartition.topicId(), fetchOffset, logStartOffset, maxBytes, currentLeaderEpoch)) | ||
| val fetchParams = buildFollowerFetchParams(replicaId, maxWaitMs = 500) | ||
|
|
||
| var fetchResultOpt: Option[FetchPartitionData] = None | ||
|
|
@@ -116,6 +118,9 @@ class DelayedFetchTest { | |
| assertTrue(delayedFetch.tryComplete()) | ||
| assertTrue(delayedFetch.isCompleted) | ||
| assertTrue(fetchResultOpt.isDefined) | ||
|
|
||
| val fetchResult = fetchResultOpt.get | ||
| assertEquals(Errors.NOT_LEADER_OR_FOLLOWER, fetchResult.error) | ||
| } | ||
|
|
||
| @Test | ||
|
|
@@ -164,18 +169,71 @@ class DelayedFetchTest { | |
| assertTrue(delayedFetch.tryComplete()) | ||
| assertTrue(delayedFetch.isCompleted) | ||
| assertTrue(fetchResultOpt.isDefined) | ||
|
|
||
| val fetchResult = fetchResultOpt.get | ||
| assertEquals(Errors.NONE, fetchResult.error) | ||
| } | ||
|
|
||
| @ParameterizedTest(name = "testDelayedFetchWithMessageOnlyHighWatermark endOffset={0}") | ||
| @ValueSource(longs = Array(0, 500)) | ||
| def testDelayedFetchWithMessageOnlyHighWatermark(endOffset: Long): Unit = { | ||
| val topicIdPartition = new TopicIdPartition(Uuid.randomUuid(), 0, "topic") | ||
| val fetchOffset = 450L | ||
| val logStartOffset = 5L | ||
| val currentLeaderEpoch = Optional.of[Integer](10) | ||
| val replicaId = 1 | ||
|
|
||
| val fetchStatus = FetchPartitionStatus( | ||
| startOffsetMetadata = new LogOffsetMetadata(fetchOffset), | ||
| fetchInfo = new FetchRequest.PartitionData(topicIdPartition.topicId, fetchOffset, logStartOffset, maxBytes, currentLeaderEpoch)) | ||
| val fetchParams = buildFollowerFetchParams(replicaId, maxWaitMs = 500) | ||
|
|
||
| var fetchResultOpt: Option[FetchPartitionData] = None | ||
| def callback(responses: Seq[(TopicIdPartition, FetchPartitionData)]): Unit = { | ||
| fetchResultOpt = Some(responses.head._2) | ||
| } | ||
|
|
||
| val delayedFetch = new DelayedFetch( | ||
| params = fetchParams, | ||
| fetchPartitionStatus = Seq(topicIdPartition -> fetchStatus), | ||
| replicaManager = replicaManager, | ||
| quota = replicaQuota, | ||
| responseCallback = callback | ||
| ) | ||
|
|
||
| val partition: Partition = mock(classOf[Partition]) | ||
| when(replicaManager.getPartitionOrException(topicIdPartition.topicPartition)).thenReturn(partition) | ||
| // Note that the high-watermark does not contain the complete metadata | ||
| val endOffsetMetadata = new LogOffsetMetadata(endOffset, -1L, -1) | ||
| when(partition.fetchOffsetSnapshot( | ||
| currentLeaderEpoch, | ||
| fetchOnlyFromLeader = true)) | ||
| .thenReturn(new LogOffsetSnapshot(0L, endOffsetMetadata, endOffsetMetadata, endOffsetMetadata)) | ||
| when(replicaManager.isAddingReplica(any(), anyInt())).thenReturn(false) | ||
| expectReadFromReplica(fetchParams, topicIdPartition, fetchStatus.fetchInfo, Errors.NONE) | ||
|
|
||
| // 1. When `endOffset` is 0, it refers to the truncation case | ||
| // 2. When `endOffset` is 500, we won't complete because it doesn't contain offset metadata | ||
| val expected = endOffset == 0 | ||
|
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. Hmm, in both cases, we are not forcing the completion of the delayed fetch, right?
Contributor
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. when fetchOffset > endOffset, This is the only behavior change post the refactor. Previously when fetchOffset > endOffset:
We can retain the same behavior if required.
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. Got it. We won't complete when |
||
| assertEquals(expected, delayedFetch.tryComplete()) | ||
|
kamalcph marked this conversation as resolved.
|
||
| assertEquals(expected, delayedFetch.isCompleted) | ||
|
kamalcph marked this conversation as resolved.
|
||
| assertEquals(expected, fetchResultOpt.isDefined) | ||
| if (fetchResultOpt.isDefined) { | ||
| assertEquals(Errors.NONE, fetchResultOpt.get.error) | ||
| } | ||
| } | ||
|
|
||
| private def buildFollowerFetchParams( | ||
| replicaId: Int, | ||
| maxWaitMs: Int | ||
| maxWaitMs: Int, | ||
| minBytes: Int = 1, | ||
| ): FetchParams = { | ||
| new FetchParams( | ||
| ApiKeys.FETCH.latestVersion, | ||
| replicaId, | ||
| 1, | ||
| maxWaitMs, | ||
| 1, | ||
| minBytes, | ||
| maxBytes, | ||
| FetchIsolation.LOG_END, | ||
| Optional.empty() | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.