KAFKA-8670: Fix exception for kafka-topics.sh --describe without --topic mentioned#7094
Conversation
…opic mentioned if there are no topics in cluster, improve error message. If there are no topics in a cluster, kafka-topics.sh --describe without a --topic option should return empty list, do not throw an exception. If there are no matching topics, throw IllegalArgumentException but with better error message.
|
retest this please |
|
Intermittent failures (not caused by this change) for two builds. |
hachikuji
left a comment
There was a problem hiding this comment.
Thanks for the fix. Left a few small comments.
| if (desiredTopicName.isDefined && topics.isEmpty && !topicOptWithExists) { | ||
| // If given topic doesn't exist then throw exception | ||
| throw new IllegalArgumentException(s"Topics in [${topics.mkString(",")}] does not exist") | ||
| if (desiredTopicName.isDefined) { |
There was a problem hiding this comment.
Didn't we already check this?
| */ | ||
| private def ensureTopicExists(topics: Seq[String], topicOptWithExists: Boolean = false) = { | ||
| if (topics.isEmpty && !topicOptWithExists) { | ||
| private def ensureTopicExists(topics: Seq[String], desiredTopicName: Option[String], topicOptWithExists: Boolean = false) = { |
There was a problem hiding this comment.
Might just be me, but I find this method a bit hard to understand. Maybe it is just a matter of choosing better names. Maybe something like this:
topics -> foundTopics
desiredTopicName -> requestedTopic
topicOptWithExists -> requireTopicExists
Then the check becomes:
requestedTopic.isDefined && requireTopicExists && foundTopics.isEmptyThat seems a little easier to understand. Maybe we can add a simple description for each parameter in the doc above.
There was a problem hiding this comment.
That seems great. I will make these changes.
There was a problem hiding this comment.
Changed variable names & added docs.
| val topics = getTopics(opts.topic, opts.excludeInternalTopics) | ||
| val topicOptWithExits = opts.topic.isDefined && opts.ifExists | ||
| ensureTopicExists(topics, topicOptWithExits) | ||
| ensureTopicExists(topics, opts.topic, topicOptWithExits) |
There was a problem hiding this comment.
typo: should be topicOptWithExists
There was a problem hiding this comment.
Yeah you are right. I did not rename the variable since it was already there. Let me do that.
There was a problem hiding this comment.
Used requireTopicExists rather than topicOptWithExists. Felt like the previous one was not a very descriptive name.
|
@hachikuji I addressed all the comments. |
| if (requestedTopic.isDefined && requireTopicExists && foundTopics.isEmpty) { | ||
| // If given topic doesn't exist then throw exception | ||
| throw new IllegalArgumentException(s"Topics in [${topics.mkString(",")}] does not exist") | ||
| throw new IllegalArgumentException(s"Topics in [${foundTopics.mkString(",")}] does not exist") |
There was a problem hiding this comment.
This message seems not very clear. We know foundTopics must be empty, so the message would look like this:
Topics in [] does not exist
I think it makes more sense for the message to indicate that the requested topic was not found. For example:
Topic 'foo' does not exist as expected
There was a problem hiding this comment.
@hachikuji I know about this issue and had initially fixed this in this pull request. But I later realized that there is a separate JIRA https://issues.apache.org/jira/browse/KAFKA-8053 for it, and I can push a different commit for it. That is why I pushed a commit in this branch to revert the string change (225d6fd)
I am not aware of the general practice used for Apache Kafka, but assumed that we try to address separate bugs as separate commits, to improve traceability.
I guess in this case it makes sense to club them together, so I will make the string change again, and resolve the other issue when this is merged.
There was a problem hiding this comment.
@hachikuji I don't have a strong preference. If you think its easier to fix it here in this PR and close mine I'm fine with it.
There was a problem hiding this comment.
Okay, @hachikuji I have made the string change here. We can resolve both JIRAs once this is merged.
hachikuji
left a comment
There was a problem hiding this comment.
Thanks, just one more comment.
| val topics = getTopics(opts.topic, opts.excludeInternalTopics) | ||
| val topicOptWithExits = opts.topic.isDefined && opts.ifExists | ||
| ensureTopicExists(topics, topicOptWithExits) | ||
| val requireTopicExists = !(opts.topic.isDefined && opts.ifExists) |
There was a problem hiding this comment.
Initially I was going to post a comment on the redundant opts.topic.isDefined check here. As I was looking at this, however, I noticed that the second check seemed backwards. I think this has always been broken. We have the following documentation for the --if-exists argument:
if set when altering or deleting or describing topics, the action will only execute if the topic exists.
But we are actually raising the exception only when --if-exists is not defined (i.e. !topicOptWithExists in the original source). I think that is actually the root of the problem. So it seems we just do the following:
ensureTopicExists(topics, opts.topic, opts.ifExists)
Does that seem right?
There was a problem hiding this comment.
@hachikuji No, I don't think that's right. The --if-exists essentially means "check if the topic exists, and if it does go do this". Which means, we should not be throwing an exception if the topic does not exist. It is like performing a map() operation on an Optional in Java.
If this option is not passed, AND the topic does not exist, we should be throwing an exception.
We can actually simplify line 358 above to
ensureTopicExists(topics, opts.topic, !opts.ifExists)
Let me know your thoughts.
There was a problem hiding this comment.
Ok, that makes more sense. So the expected semantic is "do nothing if the topic doesn't exist." Probably the description could be improved. Anyway, the simplification sounds good.
There was a problem hiding this comment.
@hachikuji Thanks. Updated with the simplification. The test servers seem to be having some issue connecting to github.com.
There was a problem hiding this comment.
Yeah, been seeing that lately. Not sure why.
By the way, I think I get why this logic seemed weird to me. If the topic doesn't exist, we still continue with the operation. But that doesn't make sense, right? Why go on deleting or altering the topic if it doesn't exist? Anyway, this can be improved separately.
hachikuji
left a comment
There was a problem hiding this comment.
Thanks for the patch! LGTM
|
@hachikuji What is the process for getting these merged into trunk (for 2.4.0) and for 2.3.1? |
…pic mentioned (#7094) If there are **no topics** in a cluster, kafka-topics.sh --describe without a --topic option should return empty list, not throw an exception. Reviewers: Jason Gustafson <jason@confluent.io>
* apache-github/2.3: MINOR: Update documentation for enabling optimizations (apache#7099) MINOR: Remove stale streams producer retry default docs. (apache#6844) KAFKA-8635; Skip client poll in Sender loop when no request is sent (apache#7085) KAFKA-8615: Change to track partition time breaks TimestampExtractor (apache#7054) KAFKA-8670; Fix exception for kafka-topics.sh --describe without --topic mentioned (apache#7094) KAFKA-8602: Separate PR for 2.3 branch (apache#7092) KAFKA-8530; Check for topic authorization errors in OffsetFetch response (apache#6928) KAFKA-8662; Fix producer metadata error handling and consumer manual assignment (apache#7086) KAFKA-8637: WriteBatch objects leak off-heap memory (apache#7050) KAFKA-8620: fix NPE due to race condition during shutdown while rebalancing (apache#7021) HOT FIX: close RocksDB objects in correct order (apache#7076) KAFKA-7157: Fix handling of nulls in TimestampConverter (apache#7070) KAFKA-6605: Fix NPE in Flatten when optional Struct is null (apache#5705) Fixes apache#8198 KStreams testing docs use non-existent method pipe (apache#6678) KAFKA-5998: fix checkpointableOffsets handling (apache#7030) KAFKA-8653; Default rebalance timeout to session timeout for JoinGroup v0 (apache#7072) KAFKA-8591; WorkerConfigTransformer NPE on connector configuration reloading (apache#6991) MINOR: add upgrade text (apache#7013) Bump version to 2.3.1-SNAPSHOT
….sh --describe without --topic mentioned (apache#7094) TICKET = KAFKA-8670 LI_DESCRIPTION = EXIT_CRITERIA = HASH [6f189d9] ORIGINAL_DESCRIPTION = If there are **no topics** in a cluster, kafka-topics.sh --describe without a --topic option should return empty list, not throw an exception. Reviewers: Jason Gustafson <jason@confluent.io> (cherry picked from commit 6f189d9)
If there are no topics in a cluster, kafka-topics.sh --describe without a --topic option should return empty list, not throw an exception.
We pass a boolean flag to
ensureTopicExistsmethod indicating whether to throw an exception if there are no topics in the cluster. In case ofkafka-topics.sh --describe, the exception should NOT be thrown if either of these are true ---topicoption was not passed to the CLI. In that case, the output should be empty.--if-existsoption was passed to the CLI.Earlier, the first condition was not part of the check. This bugfix adds the first condition mentioned above to the check.
NOTE: I have added the
desiredTopicNameargument to the ensureTopicExists to check if it is defined. I could have simply passed!desiredTopicName.isDefined || topicOptWithExistsin case of describe and it would still fix this bug. However, to fix bug KAFKA-8053, we need to improve the error message of the exception. ThedesiredTopicNamewill be required to build this exception message. I have not combined that messaging fix with this one to keep the commits for two bugs separate.Summary of testing strategy (including rationale)
for the feature or bug fix. Unit and/or integration
tests are expected for any behaviour change and
system tests should be considered for larger changes.
I added the necessary unit test to check for this case.
Also, I ran these commands. The output before and after my change are mentioned inline
Describe without topic name - Before
Describe without topic name - After (no output)
Committer Checklist (excluded from commit message)