Jax created KAFKA-21190:
---------------------------

             Summary: Empty follower fails to start when the KIP-1023 fetch 
start offset equals the leader's log start offset
                 Key: KAFKA-21190
                 URL: https://issues.apache.org/jira/browse/KAFKA-21190
             Project: Kafka
          Issue Type: Bug
          Components: Tiered-Storage
    Affects Versions: 4.3.0
            Reporter: Jax
            Assignee: Jax


h3. Problem

With {{{}follower.fetch.last.tiered.offset.enable=true{}}}, an empty follower 
can select a fetch start offset that equals the leader's log start offset. In 
that case the remote range to rebuild auxiliary state from, [logStartOffset, 
fetchStartOffset), is empty.

{{TierStateMachine.buildRemoteLogAuxState}} still looks up remote segment 
metadata for {{{}fetchStartOffset - 1{}}}. That offset is below the log start 
offset, and its remote log segment may already be deleted, so the lookup can 
return empty. When it does, the follower fails with 
{{{}RemoteStorageException{}}}, retries, and stays out of the ISR until the 
selected offset changes (for example, after the next segment upload). On a 
low-volume topic this can take a long time.
h3. Observed on 4.3.0

We rebuilt a follower on an empty disk. The selected fetch start offset and the 
leader's log start offset were both 18203:
{noformat}
ERROR [ReplicaFetcher replicaId=0, leaderId=7, fetcherId=0] Error getting 
offset for partition <TOPIC-PARTITION>
org.apache.kafka.server.log.remote.storage.RemoteStorageException: Couldn't 
build the state from remote store for partition: <TOPIC-PARTITION>, 
currentLeaderEpoch: 406, leaderLocalLogStartOffset: 18203, 
leaderLogStartOffset: 18203, epoch: 405 as the previous remote log segment 
metadata was not found
    at 
kafka.server.TierStateMachine.buildRemoteStorageException(TierStateMachine.java:278)
    at 
kafka.server.TierStateMachine.lambda$buildRemoteLogAuxState$0(TierStateMachine.java:237)
    at java.base/java.util.Optional.orElseThrow(Optional.java:403)
    at 
kafka.server.TierStateMachine.buildRemoteLogAuxState(TierStateMachine.java:237)
    at kafka.server.TierStateMachine.start(TierStateMachine.java:113)
    at 
kafka.server.AbstractFetcherThread.fetchOffsetAndTruncate(AbstractFetcherThread.scala:679)
    at 
kafka.server.AbstractFetcherThread.handleOutOfRangeError(AbstractFetcherThread.scala:749)
{noformat}
On this call path, the {{leaderLocalLogStartOffset}} field in the message is 
the selected fetch start offset.

Dynamically setting {{follower.fetch.last.tiered.offset.enable=false}} bypassed 
this path and the follower recovered without a restart.
h3. Code

[TierStateMachine.java#L201-L238|https://github.com/apache/kafka/blob/4.3.0/core/src/main/java/kafka/server/TierStateMachine.java#L201-L238]
 (same on trunk):
{code:java}
long previousOffsetToLeaderLocalLogStartOffset = leaderLocalLogStartOffset - 1;
...
RemoteLogSegmentMetadata remoteLogSegmentMetadata = 
rlm.fetchRemoteLogSegmentMetadata(topicPartition, targetEpoch, 
previousOffsetToLeaderLocalLogStartOffset)
        .orElseThrow(() -> buildRemoteStorageException(...));
{code}
{{UnifiedLog.fetchEarliestPendingUploadOffset}} clamps its result to 
{{{}max(highestRemoteOffset + 1, logStartOffset){}}}, and 
{{AbstractFetcherThread.fetchEarliestPendingUploadOffset}} returns the local 
log start offset when nothing is uploaded and it equals the log start offset. 
So {{TierStateMachine}} can receive equal offsets during normal operation.

KIP-1023 describes this case as a normal local fetch: "it simply means that 
there are no valid log segments on the remote yet. Hence the ReplicaFetcher 
simply truncates its log and starts replicating all data the leader has 
locally."
h3. Test gap

{{testLastTieredOffsetWithNonZeroLSOOnNewLeader}} and 
{{testLastTieredOffsetWithSlowUploadNoLocalDeletion}} in 
{{AbstractFetcherThreadTest}} cover equal positive start offsets, but both use 
{{{}MockTierStateMachine{}}}, which truncates directly to the selected offset 
and skips the metadata lookup. No test runs the real {{TierStateMachine}} in 
this case.
h3. Proposed fix

In {{{}TierStateMachine.buildRemoteLogAuxState{}}}, when the fetch start offset 
equals the leader's log start offset, skip the earlier-epoch and remote 
metadata lookups, truncate fully to the leader's log start offset, and fetch 
from there. Keep the existing error when the remote range is not empty.
h3. Related
 * KIP-1023 discussion: 
[https://lists.apache.org/thread/ypwqnwyn7zn6xo781yjq4v99p44gv62h]
 * Question on dev@: 
[https://lists.apache.org/thread/94hv8gsn7hqsrsn2os7nh6qxz6lwp6y1]



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to