KAFKA-9724 Newer clients not always sending fetch request to older brokers#8376
Conversation
|
@hachikuji this change might be too superficial. It should fix the problem when talking to older brokers, but ideally we shouldn't enter the |
|
ok to test |
|
Note that |
| * @param epochTest a predicate to determine if the old epoch should be replaced | ||
| * @return true if the epoch was updated, false otherwise | ||
| */ | ||
| private synchronized boolean updateLastSeenEpoch(TopicPartition topicPartition, |
There was a problem hiding this comment.
I removed this since at this point the two callers have different needs for updating the epoch and adding a flag felt pretty kludgy
| return false; | ||
| } | ||
| } else { | ||
| return assignedState(tp).maybeValidatePosition(leaderAndEpoch); |
There was a problem hiding this comment.
I wonder, do we really need this call here? If the leader is not present the epoch shouldn't be present either -- right? If that's the case, then the call to maybeValidatePosition will short circuit
private boolean maybeValidatePosition(Metadata.LeaderAndEpoch currentLeaderAndEpoch) {
if (this.fetchState.equals(FetchStates.AWAIT_RESET)) {
return false;
}
if (!currentLeaderAndEpoch.leader.isPresent() && !currentLeaderAndEpoch.epoch.isPresent()) {
return false;
}
if (position != null && !position.currentLeader.equals(currentLeaderAndEpoch)) {
FetchPosition newPosition = new FetchPosition(position.offset, position.offsetEpoch, currentLeaderAndEpoch);
validatePosition(newPosition);
preferredReadReplica = null;
}
return this.fetchState.equals(FetchStates.AWAIT_VALIDATION);
}There was a problem hiding this comment.
Oh, actually looking at the javadoc for LeaderAndEpoch, I see
It is also possible that we know of the leader epoch, but not the leader when it is derived from an external source (e.g. a committed offset).
Also in Metadata, we do return a LeaderAndEpoch with the last-seen epoch, but no leader if the metadata is stale. So, I guess it makes sense to keep this call in maybeValidatePositionForCurrentLeader
hachikuji
left a comment
There was a problem hiding this comment.
LGTM (assuming tests pass after checkstyle is fixed)
|
Do we think this is safe to backport or is it best to stick with 2.6.0? |
|
@ijuma I think it is safe. I was planning to backport to 2.5. |
|
Sounds good. |
|
retest this please |
1 similar comment
|
retest this please |
|
Test failures are known flaky tests
|
…okers (apache#8376) Newer clients were getting stuck entering the validation phase even when a broker didn't support it. This commit will bypass the AWAITING_VALIDATION state when the broker is on an older version of the OffsetsForLeaderEpoch RPC.
…okers (#8376) Newer clients were getting stuck entering the validation phase even when a broker didn't support it. This commit will bypass the AWAITING_VALIDATION state when the broker is on an older version of the OffsetsForLeaderEpoch RPC.
## Background When a partition subscription is initialized it has a `null` position and is in the INITIALIZING state. Depending on the consumer, it will then transition to one of the other states. Typically a consumer will either reset the offset to earliest/latest, or it will provide an offset (with or without offset metadata). For the reset case, we still have no position to act on so fetches should not occur. Recently we made changes for KAFKA-9724 (#8376) to prevent clients from entering the AWAIT_VALIDATION state when targeting older brokers. New logic to bypass offset validation as part of this change exposed this new issue. ## Bug and Fix In the partition subscriptions, the AWAIT_RESET state was incorrectly reporting that it had a position. In some cases a position might actually exist (e.g., if we were resetting offsets during a fetch after a truncation), but in the initialization case no position had been set. We saw this issue in system tests where there is a race between the offset reset completing and the first fetch request being issued. Since AWAIT_RESET#hasPosition was incorrectly returning `true`, the new logic to bypass offset validation was transitioning the subscription to FETCHING (even though no position existed). The fix was simply to have AWAIT_RESET#hasPosition to return `false` which should have been the case from the start. Additionally, this fix includes some guards against NPE when reading the position from the subscription. Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, Jason Gustafson <jason@confluent.io>
## Background When a partition subscription is initialized it has a `null` position and is in the INITIALIZING state. Depending on the consumer, it will then transition to one of the other states. Typically a consumer will either reset the offset to earliest/latest, or it will provide an offset (with or without offset metadata). For the reset case, we still have no position to act on so fetches should not occur. Recently we made changes for KAFKA-9724 (#8376) to prevent clients from entering the AWAIT_VALIDATION state when targeting older brokers. New logic to bypass offset validation as part of this change exposed this new issue. ## Bug and Fix In the partition subscriptions, the AWAIT_RESET state was incorrectly reporting that it had a position. In some cases a position might actually exist (e.g., if we were resetting offsets during a fetch after a truncation), but in the initialization case no position had been set. We saw this issue in system tests where there is a race between the offset reset completing and the first fetch request being issued. Since AWAIT_RESET#hasPosition was incorrectly returning `true`, the new logic to bypass offset validation was transitioning the subscription to FETCHING (even though no position existed). The fix was simply to have AWAIT_RESET#hasPosition to return `false` which should have been the case from the start. Additionally, this fix includes some guards against NPE when reading the position from the subscription. Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, Jason Gustafson <jason@confluent.io>
When a partition subscription is initialized it has a `null` position and is in the INITIALIZING state. Depending on the consumer, it will then transition to one of the other states. Typically a consumer will either reset the offset to earliest/latest, or it will provide an offset (with or without offset metadata). For the reset case, we still have no position to act on so fetches should not occur. Recently we made changes for KAFKA-9724 (#8376) to prevent clients from entering the AWAIT_VALIDATION state when targeting older brokers. New logic to bypass offset validation as part of this change exposed this new issue. In the partition subscriptions, the AWAIT_RESET state was incorrectly reporting that it had a position. In some cases a position might actually exist (e.g., if we were resetting offsets during a fetch after a truncation), but in the initialization case no position had been set. We saw this issue in system tests where there is a race between the offset reset completing and the first fetch request being issued. Since AWAIT_RESET#hasPosition was incorrectly returning `true`, the new logic to bypass offset validation was transitioning the subscription to FETCHING (even though no position existed). The fix was simply to have AWAIT_RESET#hasPosition to return `false` which should have been the case from the start. Additionally, this fix includes some guards against NPE when reading the position from the subscription. Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, Jason Gustafson <jason@confluent.io>
We had a similar case previously with KAFKA-8422 (#6806) where we would skip the validation step if the broker was on a version older than 2.3.
This PR makes a similar change on theprepareFetchRequestside. If the broker is older than 2.3, we will skip the transition to AWAITING_VALIDATION but also we will clear that state if it had been set by a call toseekUnvalidated.This PR adds a check for an appropriate version of OFFSETS_FOR_LEADER_EPOCH before entering the validation state in the consumer. If the broker does not support version 3+ of this API, we skip validation.
Also cleaned up the logic behind updating last-seen leader epochs. Now, if the leader epoch is
null, we will not update it unless we are processing a metadata response. This prevents the client from learning about an epoch before it knows anything about the leader.