-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-15260: RLM Task should handle uninitialized RLMM for the associated topic-parititon #14113
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 |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ | |
| import org.apache.kafka.common.TopicPartition; | ||
| import org.apache.kafka.common.Uuid; | ||
| import org.apache.kafka.common.errors.OffsetOutOfRangeException; | ||
| import org.apache.kafka.common.errors.RetriableException; | ||
| import org.apache.kafka.common.message.FetchResponseData; | ||
| import org.apache.kafka.common.record.FileRecords; | ||
| import org.apache.kafka.common.record.MemoryRecords; | ||
|
|
@@ -673,6 +674,9 @@ public void copyLogSegmentsToRemote(UnifiedLog log) throws InterruptedException | |
| this.cancel(); | ||
| } catch (InterruptedException ex) { | ||
| throw ex; | ||
| } catch (RetriableException ex) { | ||
| logger.debug("Encountered a retryable error while copying log segments of partition: {}", topicIdPartition, ex); | ||
| throw ex; | ||
| } catch (Exception ex) { | ||
| if (!isCancelled()) { | ||
| brokerTopicStats.topicStats(log.topicPartition().topic()).failedRemoteCopyRequestRate().mark(); | ||
|
|
@@ -776,6 +780,9 @@ public void run() { | |
| if (!isCancelled()) { | ||
| logger.warn("Current thread for topic-partition-id {} is interrupted. Reason: {}", topicIdPartition, ex.getMessage()); | ||
| } | ||
| } catch (RetriableException ex) { | ||
| logger.debug("Encountered a retryable error while executing current task for topic-partition {}. " + | ||
| "Reason: {}", topicIdPartition, ex.getMessage()); | ||
|
Member
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. Let us print the complete exception, which prints stack trace too. That will be helpful while debugging. |
||
| } catch (Exception ex) { | ||
| if (!isCancelled()) { | ||
| logger.warn("Current task for topic-partition {} received error but it will be scheduled. " + | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ | |
| import org.apache.kafka.common.TopicPartition; | ||
| import org.apache.kafka.common.Uuid; | ||
| import org.apache.kafka.common.config.AbstractConfig; | ||
| import org.apache.kafka.common.errors.ReplicaNotAvailableException; | ||
| import org.apache.kafka.common.network.ListenerName; | ||
| import org.apache.kafka.common.record.CompressionType; | ||
| import org.apache.kafka.common.record.FileRecords; | ||
|
|
@@ -754,6 +755,61 @@ void testCopyLogSegmentsToRemoteShouldNotCopySegmentForFollower() throws Excepti | |
| verify(mockLog, never()).updateHighestOffsetInRemoteStorage(anyLong()); | ||
| } | ||
|
|
||
| @Test | ||
| void testRLMTaskDoesNotUploadSegmentsWhenRemoteLogMetadataManagerIsNotInitialized() throws Exception { | ||
| long oldSegmentStartOffset = 0L; | ||
| long nextSegmentStartOffset = 150L; | ||
|
|
||
| when(mockLog.topicPartition()).thenReturn(leaderTopicIdPartition.topicPartition()); | ||
|
|
||
| // leader epoch preparation | ||
| checkpoint.write(totalEpochEntries); | ||
| LeaderEpochFileCache cache = new LeaderEpochFileCache(leaderTopicIdPartition.topicPartition(), checkpoint); | ||
| when(mockLog.leaderEpochCache()).thenReturn(Option.apply(cache)); | ||
|
|
||
| // Throw a retryable exception so indicate that the remote log metadata manager is not initialized yet | ||
| when(remoteLogMetadataManager.highestOffsetForEpoch(any(TopicIdPartition.class), anyInt())) | ||
| .thenThrow(new ReplicaNotAvailableException("Remote log metadata cache is not initialized for partition: " + leaderTopicIdPartition)); | ||
|
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. can we have another invocation where it returns the value instead of exception? (eg) once the partition is initialized, the copy segments should work as expected: when(remoteLogMetadataManager.highestOffsetForEpoch(any(TopicIdPartition.class), anyInt()))
.thenThrow(new ReplicaNotAvailableException("Remote log metadata cache is not initialized for partition: " + leaderTopicIdPartition))
.thenReturn(-1);
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. There is already a test for this behaviour |
||
|
|
||
| // create 2 log segments, with 0 and 150 as log start offset | ||
| LogSegment oldSegment = mock(LogSegment.class); | ||
| LogSegment activeSegment = mock(LogSegment.class); | ||
|
|
||
| when(oldSegment.baseOffset()).thenReturn(oldSegmentStartOffset); | ||
| when(activeSegment.baseOffset()).thenReturn(nextSegmentStartOffset); | ||
|
|
||
| when(mockLog.activeSegment()).thenReturn(activeSegment); | ||
| when(mockLog.logStartOffset()).thenReturn(oldSegmentStartOffset); | ||
| when(mockLog.logSegments(anyLong(), anyLong())).thenReturn(JavaConverters.collectionAsScalaIterable(Arrays.asList(oldSegment, activeSegment))); | ||
| when(mockLog.lastStableOffset()).thenReturn(250L); | ||
|
|
||
| // Ensure the metrics for remote write requests/failures is zero before attempt to copy log segment | ||
| assertEquals(0, brokerTopicStats.topicStats(leaderTopicIdPartition.topic()).remoteCopyRequestRate().count()); | ||
| assertEquals(0, brokerTopicStats.topicStats(leaderTopicIdPartition.topic()).failedRemoteCopyRequestRate().count()); | ||
| // Ensure aggregate metrics | ||
| assertEquals(0, brokerTopicStats.allTopicsStats().remoteCopyRequestRate().count()); | ||
| assertEquals(0, brokerTopicStats.allTopicsStats().failedRemoteCopyRequestRate().count()); | ||
|
|
||
| RemoteLogManager.RLMTask task = remoteLogManager.new RLMTask(leaderTopicIdPartition, 128); | ||
| task.convertToLeader(0); | ||
|
|
||
| RemoteLogManager.RLMTask spyTask = spy(task); | ||
|
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. Why the RLMTask is wrapped with
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. Yes, we don't need this anymore. Previously I was verifying a behaviour on the spy but that is removed now.
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. Added it back to verify a behaviour. |
||
| spyTask.run(); | ||
|
|
||
| // verify the remoteLogMetadataManager never add any metadata and remoteStorageManager never copy log segments | ||
| verify(remoteLogMetadataManager, never()).addRemoteLogSegmentMetadata(any(RemoteLogSegmentMetadata.class)); | ||
| verify(remoteStorageManager, never()).copyLogSegmentData(any(RemoteLogSegmentMetadata.class), any(LogSegmentData.class)); | ||
|
Comment on lines
+795
to
+799
Member
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. We can't assure if the RLMTask thread is already run once or not when verifying these things under current testing. Could we just invoke
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. I added a verification so that |
||
| verify(remoteLogMetadataManager, never()).updateRemoteLogSegmentMetadata(any(RemoteLogSegmentMetadataUpdate.class)); | ||
| verify(mockLog, never()).updateHighestOffsetInRemoteStorage(anyLong()); | ||
|
|
||
| // Verify the metric for remote write requests/failures was not updated. | ||
| assertEquals(0, brokerTopicStats.topicStats(leaderTopicIdPartition.topic()).remoteCopyRequestRate().count()); | ||
| assertEquals(0, brokerTopicStats.topicStats(leaderTopicIdPartition.topic()).failedRemoteCopyRequestRate().count()); | ||
| // Verify aggregate metrics | ||
| assertEquals(0, brokerTopicStats.allTopicsStats().remoteCopyRequestRate().count()); | ||
| assertEquals(0, brokerTopicStats.allTopicsStats().failedRemoteCopyRequestRate().count()); | ||
| } | ||
|
|
||
| private void verifyRemoteLogSegmentMetadata(RemoteLogSegmentMetadata remoteLogSegmentMetadata, | ||
| long oldSegmentStartOffset, | ||
| long oldSegmentEndOffset, | ||
|
|
||
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.
Why are we logging it twice in L678 and L784?
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.
removing from the line 678