KAFKA-17331: Throw UnsupportedVersionException if the data in ListOffsetRequest does NOT fit EarliestLocalSpec and LatestTieredSpec.#16876
Conversation
chia7712
left a comment
There was a problem hiding this comment.
@frankvicky thanks for this patch
|
Hi @chia7712 |
FrankYang0529
left a comment
There was a problem hiding this comment.
Thanks for the PR. Leave one comment.
| public ListOffsetsRequest build(short version) { | ||
| for (ListOffsetsTopic topic : data.topics()) { | ||
| for (ListOffsetsPartition partition : topic.partitions()) { | ||
| checkVersion(version, topic.name(), partition); |
There was a problem hiding this comment.
The failed case ListOffsetsRequestTest#testResponseDefaultOffsetAndLeaderEpochForAllVersions can be reproduced on my laptop. Could you take a look? Thank you.
There was a problem hiding this comment.
Thank you, I will take a look!
There was a problem hiding this comment.
@frankvicky Could you please add a helper to build invalid request in order to run test?
There was a problem hiding this comment.
Hi @FrankYang0529 and @chia7712
I have reviewed the code. Since this error is due to a version check in the build method, I will change assertEqual to assertThrows when the test tries to build an invalid request to align with the current behavior.
There was a problem hiding this comment.
@frankvicky What we want to protect is the request from clients (admin and consumer). So maybe we should revise fromConsumer[0] rather than build.
WDYT?
[0]
There was a problem hiding this comment.
The benefit is that we can leverage the API_VERSION check of network layer. The disadvantage is that it requires the API_VERSION, and fortunately that is existent in consumer and admin.
There was a problem hiding this comment.
Hi @chia7712
I have reviewed the code. Since forConsumer is a static method, if we aim to perform a version check in this method, we will need to add a new argument. I will try to implement it.
| boolean requireMaxTimestamp, | ||
| boolean requireTieredStorageTimestamp) { | ||
| boolean requireTieredStorageTimestamp, | ||
| List<ListOffsetsTopic> topics) { |
There was a problem hiding this comment.
Could you please follow the pattern? You can add a flag to pick up the min version. Current implementation does not consider the version=8 and I feel that is a kind of bug. @FrankYang0529 WDYT?
There was a problem hiding this comment.
Hi @chia7712
I have add a requireEarliestLocalTimestamp parameter to forConsumer method, PTAL
There was a problem hiding this comment.
Yes, we should update this. Thanks @chia7712 and @frankvicky
chia7712
left a comment
There was a problem hiding this comment.
@frankvicky thanks for this patch
| return new Builder(minVersion, ApiKeys.LIST_OFFSETS.latestVersion(), CONSUMER_REPLICA_ID, isolationLevel); | ||
|
|
||
| short version = ApiKeys.LIST_OFFSETS.latestVersion(); | ||
| topics.forEach(topic -> topic.partitions().forEach(partition -> checkVersion(version, topic.name(), partition))); |
There was a problem hiding this comment.
After setting the min version, the version checkcan handled by NetworkClient [0]. Hence, we don't need to add extra check here.
|
@frankvicky I feel the refactor is a bit overkill. Maybe we can add following two methods: public static Builder forConsumer(boolean requireTimestamp,
IsolationLevel isolationLevel) {
return forConsumer(requireTimestamp, isolationLevel, false, false, false);
}
public static Builder forConsumer(boolean requireTimestamp,
IsolationLevel isolationLevel,
boolean requireMaxTimestamp,
boolean requireEarliestTimestamp,
boolean requireTieredStorageTimestamp) {
short minVersion = 0;
if (requireTieredStorageTimestamp)
minVersion = 9;
else if (requireEarliestTimestamp)
minVersion = 8;
else if (requireMaxTimestamp)
minVersion = 7;
else if (isolationLevel == IsolationLevel.READ_COMMITTED)
minVersion = 2;
else if (requireTimestamp)
minVersion = 1;
return new Builder(minVersion, ApiKeys.LIST_OFFSETS.latestVersion(), CONSUMER_REPLICA_ID, isolationLevel);
}Those two methods should cover all use cases for now. |
|
Hi @chia7712 |
chia7712
left a comment
There was a problem hiding this comment.
@frankvicky thanks for this patch. a couple of comments are left.
| ListOffsetsRequest.Builder builder = ListOffsetsRequest.Builder | ||
| .forConsumer(requireTimestamps, isolationLevel, false, false) | ||
| .setTargetTimes(ListOffsetsRequest.toListOffsetsTopics(targetTimes)); | ||
| List<ListOffsetsRequestData.ListOffsetsTopic> topics = ListOffsetsRequest.toListOffsetsTopics(targetTimes); |
There was a problem hiding this comment.
If there is no strong reason, could we keep fluent pattern?
ListOffsetsRequest.Builder builder = ListOffsetsRequest.Builder
.forConsumer(requireTimestamps, isolationLevel)
.setTargetTimes(ListOffsetsRequest.toListOffsetsTopics(targetTimes));| .setTargetTimes(topics) | ||
| .build(version); | ||
| ListOffsetsResponse response = (ListOffsetsResponse) request.getErrorResponse(0, Errors.NOT_LEADER_OR_FOLLOWER.exception()); | ||
There was a problem hiding this comment.
please revert this unrelated change
| .forConsumer(false, IsolationLevel.READ_UNCOMMITTED, false, false) | ||
| .setTargetTimes(singletonList(topic)) | ||
| .forConsumer(false, IsolationLevel.READ_UNCOMMITTED) | ||
| .setTargetTimes(topics) |
| .forConsumer(true, IsolationLevel.READ_UNCOMMITTED, false, false) | ||
| .setTargetTimes(singletonList(topic)) | ||
| .forConsumer(true, IsolationLevel.READ_UNCOMMITTED) | ||
| .setTargetTimes(topics) |
| .forConsumer(true, IsolationLevel.READ_COMMITTED, false, false) | ||
| .setTargetTimes(singletonList(topic)) | ||
| .forConsumer(true, IsolationLevel.READ_COMMITTED) | ||
| .setTargetTimes(topics) |
| .setCurrentLeaderEpoch(27)).asJava)).asJava | ||
| ). | ||
| build() | ||
| val topics = List(new ListOffsetsTopic() |
| val topicPartition = new TopicPartition("foo", 0) | ||
| val request = ListOffsetsRequest.Builder.forConsumer(false, IsolationLevel.READ_UNCOMMITTED, false, false) | ||
| .setTargetTimes(buildTargetTimes(topicPartition, ListOffsetsRequest.LATEST_TIMESTAMP, 10).asJava).build(0) | ||
| val targetTimes = buildTargetTimes(topicPartition, ListOffsetsRequest.LATEST_TIMESTAMP, 10).asJava |
| .setCurrentLeaderEpoch(15)).asJava) | ||
| ListOffsetsRequest.Builder.forConsumer(false, IsolationLevel.READ_UNCOMMITTED, false, false) | ||
| .setTargetTimes(List(topic).asJava) | ||
| val targetTimes = List(topic).asJava |
| public String toString() { | ||
| return data.toString(); | ||
| } | ||
|
|
| short minVersion = 0; | ||
| if (requireTieredStorageTimestamp) | ||
| minVersion = 9; | ||
| else if (requireEarliestTimestamp) |
There was a problem hiding this comment.
Hi @chia7712 and @frankvicky, What does requireEarliestTimestamp mean? EARLIEST_TIMESTAMP or EARLIEST_LOCAL_TIMESTAMP? If it's EARLIEST_LOCAL_TIMESTAMP, probably we should change the parameter name as requireEarliestLocalTimestamp, so users don't misunderstand it.
There was a problem hiding this comment.
Sure, I will update it
|
@FrankYang0529 Could you please take a look? |
FrankYang0529
left a comment
There was a problem hiding this comment.
LGTM. Thanks for the fix.
…dSpec (#16876) Add the version check to client side when building ListOffsetRequest for the specific timestamp: 1) the version must be >=8 if timestamp=-4L (EARLIEST_LOCAL_TIMESTAMP) 2) the version must be >=9 if timestamp=-5L (LATEST_TIERED_TIMESTAMP) Reviewers: PoAn Yang <payang@apache.org>, Chia-Ping Tsai <chia7712@gmail.com>
…dSpec (apache#16876) Add the version check to client side when building ListOffsetRequest for the specific timestamp: 1) the version must be >=8 if timestamp=-4L (EARLIEST_LOCAL_TIMESTAMP) 2) the version must be >=9 if timestamp=-5L (LATEST_TIERED_TIMESTAMP) Reviewers: PoAn Yang <payang@apache.org>, Chia-Ping Tsai <chia7712@gmail.com>
…dSpec (apache#16876) Add the version check to client side when building ListOffsetRequest for the specific timestamp: 1) the version must be >=8 if timestamp=-4L (EARLIEST_LOCAL_TIMESTAMP) 2) the version must be >=9 if timestamp=-5L (LATEST_TIERED_TIMESTAMP) Reviewers: PoAn Yang <payang@apache.org>, Chia-Ping Tsai <chia7712@gmail.com>
JIRA: KAFKA-17331
Add the version check to client side when building
ListOffsetRequestfor the specific timestamp:>=8iftimestamp=-4L(EARLIEST_LOCAL_TIMESTAMP)>=9iftimestamp=-5L(LATEST_TIERED_TIMESTAMP)Committer Checklist (excluded from commit message)