Skip to content

KAFKA-9840: Skip End Offset validation when the leader epoch is not reliable#8486

Merged
hachikuji merged 8 commits into
apache:trunkfrom
abbccdda:KAFKA-9840
Jun 5, 2020
Merged

KAFKA-9840: Skip End Offset validation when the leader epoch is not reliable#8486
hachikuji merged 8 commits into
apache:trunkfrom
abbccdda:KAFKA-9840

Conversation

@abbccdda

@abbccdda abbccdda commented Apr 14, 2020

Copy link
Copy Markdown

This PR provides two fixes:

  1. Complete offset validation if the leader epoch is not reliable
  2. Refresh metadata when the return offset or epoch is not defined

Committer Checklist (excluded from commit message)

  • Verify design and implementation
  • Verify test coverage and CI build status
  • Verify documentation (including upgrade notes)

@abbccdda
abbccdda marked this pull request as draft April 14, 2020 17:04
@abbccdda
abbccdda force-pushed the KAFKA-9840 branch 3 times, most recently from 0910d36 to 22d7e20 Compare April 21, 2020 15:42
@abbccdda
abbccdda marked this pull request as ready for review April 22, 2020 02:55

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the function closer to its caller.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change one: immediately complete the validation when the leader epoch is not reliable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you not move the function while changing it so that the diff is easier to check -- e.g. here I'd have to blindly trust you that there are only changes of this func :)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Np, will do

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change two: do not complete the validation as the returned epoch or offset is invalid.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the function signature to be able to pass in response version.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added test coverage L203 and L214, other changes are just side cleanups and signature refactoring in this file.

@guozhangwang guozhangwang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a pass over non-testing part. I'd suggest we revert the function movement so that it helps with more clear reviews.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you not move the function while changing it so that the diff is easier to check -- e.g. here I'd have to blindly trust you that there are only changes of this func :)

offsetsResult.endOffsets().forEach((respTopicPartition, respEndOffset) -> {
if (respEndOffset.hasUndefinedEpochOrOffset()) {
// Should attempt to find the new leader in the next try.
log.debug("Requesting metadata update for partition {} due to undefined epoch or offset {}",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: ... or offset {} from OffsetsForLeaderEpoch response

}

public boolean hasUndefinedEpochOrOffset() {
return this.endOffset == UNDEFINED_EPOCH_OFFSET ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own understanding: if endOffset is UNDEFINED the epoch should always be UNDEFINED too? If that's the case we can just rely on leaderEpoch alone?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just feel this check is stronger if later we change the behavior on broker.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Older versions did not return the epoch, so it was possible to see an offset defined without an epoch. However, the version that the consumer relies on should always have both or neither. Anyway, I think it is reasonable to be a little stricter here.

@abbccdda
abbccdda force-pushed the KAFKA-9840 branch 2 times, most recently from 18d2e3a to cf73388 Compare April 23, 2020 16:46

@guozhangwang guozhangwang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @hachikuji can take another look at this.

@ijuma

ijuma commented May 17, 2020

Copy link
Copy Markdown
Member

Where are with this?

@abbccdda

Copy link
Copy Markdown
Author

@ijuma will ping Jason for another review

@hachikuji

Copy link
Copy Markdown
Contributor

I was waiting for #8376 to get merged. I will review this shortly.

@hachikuji hachikuji left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abbccdda Sorry for the delay. I think what we decided is that if the current leader epoch is not known, then we would not attempt to send OffsetsForLeaderEpoch because we cannot trust the result. What I had in mind was a little bit simpler, but I could be missing something. Inside SubscriptionState.TopicPartitionState.maybeValidatePosition, we have the following check:

            if (!currentLeaderAndEpoch.leader.isPresent() && !currentLeaderAndEpoch.epoch.isPresent()) {
                return false;
            }

What I was thinking is that we could do something like the following:

            if (!currentLeaderAndEpoch.leader.isPresent()) {
                return false;
            }

            if (!currentLeaderAndEpoch.epoch.isPresent()) {
                completeValidation();
                return false;
            }

That is, if we know the leader, but we don't have an epoch, then we must either have an older api or we must have discarded the value. Either way, we skip validation and begin fetching. Would that not work?

@abbccdda
abbccdda force-pushed the KAFKA-9840 branch 3 times, most recently from 8b5d33a to 1d08929 Compare June 1, 2020 21:58
Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
}

public boolean hasUndefinedEpochOrOffset() {
return this.endOffset == UNDEFINED_EPOCH_OFFSET ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Older versions did not return the epoch, so it was possible to see an offset defined without an epoch. However, the version that the consumer relies on should always have both or neither. Anyway, I think it is reasonable to be a little stricter here.

Comment thread clients/src/test/java/org/apache/kafka/common/requests/EpochEndOffsetTest.java Outdated
@hachikuji

Copy link
Copy Markdown
Contributor

retest this please

2 similar comments
@hachikuji

Copy link
Copy Markdown
Contributor

retest this please

@hachikuji

Copy link
Copy Markdown
Contributor

retest this please

Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
@hachikuji

Copy link
Copy Markdown
Contributor

retest this please

1 similar comment
@hachikuji

Copy link
Copy Markdown
Contributor

retest this please

Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated

if (respEndOffset.hasUndefinedEpochOrOffset()) {
handleOffsetOutOfRange(requestPosition.offset, respTopicPartition,
handleOffsetOutOfRange(requestPosition, respTopicPartition,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't notice this before, but this is handled inside a loop. If one partition hits an error, then the raised exception will prevent us from completing the validation for other partitions.

Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
"Failed leader offset epoch validation for " + requestPosition
+ " since no end offset larger than current fetch epoch was reported");
} catch (OffsetOutOfRangeException e) {
// Swallow the OffsetOutOfRangeException to finish all partitions validation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel great about having this in the code, even if it's supposed to be temporary. I think we should just fix the exception propagation bug in this patch. It seems like it would be straightforward to do something similar to what is done in the onFailure path.

                    if (!(e instanceof RetriableException) && !cachedOffsetForLeaderException.compareAndSet(null, e)) {
                        log.error("Discarding error in OffsetsForLeaderEpoch because another error is pending", e);
                    }

Above may not be ideal, but at least it provides a way to propagate individual errors.

Comment thread clients/src/main/java/org/apache/kafka/clients/consumer/internals/Fetcher.java Outdated
@hachikuji

Copy link
Copy Markdown
Contributor

ok to test

@hachikuji hachikuji left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the patch!


private void initMetadata(MockClient mockClient, Map<String, Integer> partitionCounts) {
MetadataResponse initialMetadata = TestUtils.metadataUpdateWith(1, partitionCounts);
int leaderEpoch = 1;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual fix, the other parts are mostly cleanup

@hachikuji

Copy link
Copy Markdown
Contributor

ok to test

@hachikuji

Copy link
Copy Markdown
Contributor

ok to test

@abbccdda

abbccdda commented Jun 5, 2020

Copy link
Copy Markdown
Author

retest please

@hachikuji

Copy link
Copy Markdown
Contributor

retest this please

2 similar comments
@hachikuji

Copy link
Copy Markdown
Contributor

retest this please

@hachikuji

Copy link
Copy Markdown
Contributor

retest this please

@hachikuji
hachikuji merged commit 910f317 into apache:trunk Jun 5, 2020
hachikuji pushed a commit that referenced this pull request Jun 5, 2020
…eliable (#8486)

This PR provides two fixes:
1. Skip offset validation if the current leader epoch cannot be reliably determined.
2. Raise an out of range error if the leader returns an undefined offset in response to the OffsetsForLeaderEpoch request.

Reviewers: Guozhang Wang <wangguoz@gmail.com>, Jason Gustafson <jason@confluent.io>
Kvicii pushed a commit to Kvicii/kafka that referenced this pull request Jun 6, 2020
* 'trunk' of github.com:apache/kafka: (46 commits)
  MINOR: improve code encapsulation between StreamThread and TaskManager (apache#8819)
  Fixing KAFKA-10094 (apache#8797)
  KAFKA-9851: Revoking Connect tasks due to connectivity issues should also clear the running assignment (apache#8804)
  KAFKA-9840; Skip End Offset validation when the leader epoch is not reliable (apache#8486)
  HOT_FIX: Update javadoc since imports added (apache#8817)
  KAFKA-8011: Fix flaky RegexSourceIntegrationTest (apache#8799)
  KAFKA-9570: Define SSL configs in all worker config classes, not just distributed (apache#8135)
  KAFKA-10111: Make SinkTaskContext.errantRecordReporter() a default method (apache#8814)
  KAFKA-10110: Corrected potential NPE when null label value added to KafkaMetricsContext (apache#8811)
  MINOR: Change the order that Connect calls `config()` and `validate()` to avoid validating if the required ConfigDef is null (apache#8810)
  MINOR: fix backwards incompatibility in JmxReporter introduced by KIP-606
  MINOR: Fix javadoc warnings (apache#8809)
  KAFKA-9441: Improve Kafka Streams task management (apache#8776)
  fix the broken links of streams javadoc (apache#8789)
  KAFKA-10040; Make computing the PreferredReplicaImbalanceCount metric more efficient (apache#8724)
  KAFKA-10066: TestOutputTopic should pass record headers into deserializers (apache#8759)
  MINOR: Add explanation for disabling forwarding from value transformers (apache#8771)
  KAFKA-10033: Throw UnknownTopicOrPartitionException if altering configs of non-existing topic
  KAFKA-9434: automated protocol for alterReplicaLogDirs (apache#8311)
  KAFKA-9313: Set `use_all_dns_ips` as the new default for `client.dns.lookup` (KIP-602) (apache#8644)
  ...
ijuma added a commit to confluentinc/kafka that referenced this pull request Jun 8, 2020
Conflicts:
* build.gradle: take upstream changes regarding heap memory
configuration for the build.

* apache-github/trunk: (33 commits)
  MINOR: fix HTML markup (apache#8823)
  KAFKA-10012; Reduce overhead of strings in SelectorMetrics (apache#8684)
KAFKA-9216: Enforce internal config topic settings for Connect workers
during startup (apache#8270)
  KAFKA-10097: Internalize checkpoint data (apache#8820)
KAFKA-10033: Throw UnknownTopicOrPartitionException when modifying a
non-existent topic's config
MINOR: improve code encapsulation between StreamThread and TaskManager
(apache#8819)
  Fixing KAFKA-10094 (apache#8797)
KAFKA-9851: Revoking Connect tasks due to connectivity issues should
also clear the running assignment (apache#8804)
KAFKA-9840; Skip End Offset validation when the leader epoch is not
reliable (apache#8486)
  HOT_FIX: Update javadoc since imports added (apache#8817)
  KAFKA-8011: Fix flaky RegexSourceIntegrationTest (apache#8799)
KAFKA-9570: Define SSL configs in all worker config classes, not just
distributed (apache#8135)
KAFKA-10111: Make SinkTaskContext.errantRecordReporter() a default
method (apache#8814)
KAFKA-10110: Corrected potential NPE when null label value added to
KafkaMetricsContext (apache#8811)
MINOR: Change the order that Connect calls `config()` and `validate()`
to avoid validating if the required ConfigDef is null (apache#8810)
MINOR: fix backwards incompatibility in JmxReporter introduced by
KIP-606
  MINOR: Fix javadoc warnings (apache#8809)
  KAFKA-9441: Improve Kafka Streams task management (apache#8776)
  fix the broken links of streams javadoc (apache#8789)
KAFKA-10040; Make computing the PreferredReplicaImbalanceCount metric
more efficient (apache#8724)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants