Skip to content

KAFKA-9434: automated protocol for alterReplicaLogDirs#8311

Merged
mimaison merged 23 commits into
apache:trunkfrom
tombentley:KAFKA-9434-AlterReplicaLogDirs-automatedprotocol
Jun 4, 2020
Merged

KAFKA-9434: automated protocol for alterReplicaLogDirs#8311
mimaison merged 23 commits into
apache:trunkfrom
tombentley:KAFKA-9434-AlterReplicaLogDirs-automatedprotocol

Conversation

@tombentley

Copy link
Copy Markdown
Member

Change the alterReplicaLogDirs request and response to use the generated protocol messages.

Committer Checklist (excluded from commit message)

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

@tombentley

Copy link
Copy Markdown
Member Author

@mimaison would you be able to review this one?

@tombentley
tombentley force-pushed the KAFKA-9434-AlterReplicaLogDirs-automatedprotocol branch from e154815 to 7658599 Compare March 20, 2020 16:07
@tombentley
tombentley force-pushed the KAFKA-9434-AlterReplicaLogDirs-automatedprotocol branch from 7658599 to 3c32ac4 Compare April 9, 2020 12:36
@tombentley

Copy link
Copy Markdown
Member Author

Rebased for conflict.

@tombentley
tombentley force-pushed the KAFKA-9434-AlterReplicaLogDirs-automatedprotocol branch from 3c32ac4 to b1584aa Compare April 9, 2020 14:54
@tombentley

Copy link
Copy Markdown
Member Author

Rebased for conflict.

@dajac dajac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@tombentley Thanks for the PR. I have made a pass over it and I have left couple of comments.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are refactoring this, could we add a unit test in KafkaApisTest?

Comment thread core/src/test/scala/unit/kafka/server/AlterReplicaLogDirsRequestTest.scala Outdated
Comment thread clients/src/main/java/org/apache/kafka/clients/admin/KafkaAdminClient.java Outdated
Comment thread clients/src/main/java/org/apache/kafka/common/protocol/ApiKeys.java Outdated
@tombentley
tombentley force-pushed the KAFKA-9434-AlterReplicaLogDirs-automatedprotocol branch from a6d1e17 to 4cb5e42 Compare April 21, 2020 09:35
@tombentley

Copy link
Copy Markdown
Member Author

@dajac thanks for the review, I've addressed all your comments.

@dajac dajac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@tombentley Thanks for the updated PR. The overall logic looks good to me. I have left a bunch of small comments.

Comment thread clients/src/main/java/org/apache/kafka/clients/admin/KafkaAdminClient.java Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: Could we rename value to something like alterReplicaLogDirs?

Comment thread clients/src/main/java/org/apache/kafka/clients/admin/KafkaAdminClient.java Outdated
Comment thread clients/src/test/java/org/apache/kafka/clients/admin/KafkaAdminClientTest.java Outdated
Comment thread core/src/test/scala/unit/kafka/server/KafkaApisTest.scala Outdated
Comment thread core/src/test/scala/unit/kafka/server/AlterReplicaLogDirsRequestTest.scala Outdated
Comment thread core/src/test/scala/unit/kafka/server/KafkaApisTest.scala Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we add a unit test for this one and the one bellow?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we add a unit test for this one?

@tombentley

Copy link
Copy Markdown
Member Author

Thanks @dajac, there were some useful comments and good spots there. I've pushed some fixes.

@dajac dajac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@tombentley Thanks for the updated PR. It looks good overall. I have left few more small comments and one question.

Comment on lines 3401 to 3402

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

assertNulls shouldn't be here but few lines bellow.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch! Sorry about that.

Comment on lines 2256 to 2258

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@tombentley This one bugs me. I know that it was already like this in the previous implementation but I wonder if we could improve this while we are here. I am also fine keeping it as it is and improving this latter on.

I find odd that we fail all the non-realised yet futures when we discover an unknown replica in the response. From a client perspective, the behaviour is unpredictable and we leave it with potentially a partial view of the results event though the brokers may have sent back all the requested ones. Wouldn't it be better to just ignore unexpected replica and log a warning message? If a broker would ever do this, the client would continue to work as expected which is probably better. I think that it is a better behavior.

Similarly, we may want to ensure that all futures are done in case a replica would be missing in the response from the broker.

What do you think?

Comment thread core/src/test/scala/unit/kafka/server/KafkaApisTest.scala Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do you mind adding a unit test for this one in AlterReplicaLogDirsRequestTest for the sake of completeness?

@tombentley
tombentley force-pushed the KAFKA-9434-AlterReplicaLogDirs-automatedprotocol branch from e323868 to 1dffaa9 Compare April 27, 2020 13:09
@tombentley

Copy link
Copy Markdown
Member Author

Rebased for conflict.

@dajac thanks for the review. I agree about the case of the unexpected partition. The only way it could happen would be a bug in either the client or the broker, but there's no need to force that onto the user to handle. A warning log should be enough for the bug to be reported eventually. So I've made this change.

About the case of the client not getting a response for a partition: I agree that could happen (again, as a bug in the broker or client). It would be a bit harder to detect though; we'd be spending CPU cycles to detect and log a case which we don't think will ever happen in practice. Maybe that's OK since altering log dirs not something done frequently. But it strikes me that there are plenty of other RPCs in the admin client that don't do this kind of second-guessing of the results. If we're prepared to go to that effort here, why not elsewhere? So personally, I think it would OK to omit this check, but I can do it if you really think it should be done.

@dajac

dajac commented May 4, 2020

Copy link
Copy Markdown
Member

@tombentley Did you consider adding a loop in at the end of the handleResponse that would complete any non-completed future related to the current brokerId with an exception?

// The server should send back a response for every topic. But do a sanity check anyway.
for (Map.Entry<TopicPartitionReplica, KafkaFutureImpl<Void>> entry : futures.entrySet()) {
    TopicPartitionReplica replica = entry.getKey();
    KafkaFutureImpl<Void> future = entry.getValue();
    if (!future.isDone && replica.brokerId() == brokerId)) {
        future.completeExceptionally(new ApiException("The server response did not " +
            "contain a reference to node " + entry.getKey()));
    }
}

You're right. We have few other cases in the Admin client that suffer from the same issue. If we can find an easy way to do it, I would rather prefer doing it in this PR so we can forget about it. I will try to fix some of the others myself.

@tombentley

Copy link
Copy Markdown
Member Author

@dajac yes, fair point. And I notice now that create and delete topics response handlers are already doing this (though the error messages in those cases don't really make much sense). So I've added this with a slightly reworded message.

@dajac

dajac commented May 5, 2020

Copy link
Copy Markdown
Member

@tombentley thanks for the addition. we could perhaps add a small unit test for the sake of completeness. we already cover all the other cases but this one.

@tombentley

Copy link
Copy Markdown
Member Author

@dajac sure. I added tests for the create and delete topics cases too.

@dajac dajac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@tombentley Thanks for the update! LGTM. I just left small cosmetic comments.

Comment thread clients/src/test/java/org/apache/kafka/clients/admin/KafkaAdminClientTest.java Outdated
Comment on lines +2280 to +2281
if (!future.isDone()
&& replica.brokerId() == brokerId) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: i would bring the second part on the first line.

/**
* Fail the given future when a response handler expected a result for an entity but no result was present.
* @param future The future to fail.
* @param message The message to fail the future with

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: . at the end to stay consistent with previous line.

* @param future The future to fail.
* @param message The message to fail the future with
*/
private void partialResponse(KafkaFutureImpl<?> future, String message) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am not entirely convinced by the name. At the end, the method complete a future with the given message. There is nothing specific to handling partial responses. I would rename it to something more generic or simply remove it as we don't gain much with it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I agree it's not doing much. What value it has is in trying to handle these cases in a consistent way, and being able to more easily discover/reason about the call sites. Maybe something like invalidBrokerResponse() would be a better name? But if you don't like that I'm happy to remove it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see your point. I am definitely for reusing logic as much as possible for those cases. What about going an extra step and do something like this then (code not tested)? Just an idea...

public static <K, V> void completeUnrealizedFutures(
            Map<K, KafkaFutureImpl<V>> futures,
            Function<K, Boolean> filter,
            Function<K, String> messageFormatter) {
        for (Map.Entry<K, KafkaFutureImpl<V>> entry : futures.entrySet()) {
            K key = entry.getKey();
            KafkaFutureImpl<V> future = entry.getValue();
            if (!future.isDone() && filter.apply(key)) {
                future.completeExceptionally(new ApiException(messageFormatter.apply(key)));
            }
        }
    }

    public static  <K, V> void completeUnrealizedFutures(
            Map<K, KafkaFutureImpl<V>> futures,
            unction<K, String> messageFormatter) {
        completeUnrealizedFutures(futures, (key) -> true, messageFormatter);
    }

It would allow the following:

completeUnrealizedFutures(topicFutures,
     replica -> replica.brokerId() == brokerId,
     topic -> "The response from broker " + brokerId + " did not contain a result for replica " + replica);

completeUnrealizedFutures(topicFutures,
     topic -> "The server response did not contain a reference to node " + topic);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That works for me, especially if this is something you're going to make more use of for the other response handlers in the admin client.

@dajac dajac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@tombentley Thanks for the update. The PR LGTM! I just left few cosmetic comments.

}
completeUnrealizedFutures(topicFutures,
topic -> "The controller response " +
"did not contain a result for topic " + topic);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: we can bring this back on the first line.

}
completeUnrealizedFutures(topicFutures,
topic -> "The controller response " +
"did not contain a result for topic " + topic);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: we can bring this back on the first line.

future.completeExceptionally(error.exception());
for (AlterReplicaLogDirTopicResult topicResult: response.data().results()) {
for (AlterReplicaLogDirPartitionResult partitionResult: topicResult.partitions()) {
TopicPartitionReplica replica = new TopicPartitionReplica(topicResult.topicName(), partitionResult.partitionIndex(), brokerId);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: We could break this long line.

@tombentley

Copy link
Copy Markdown
Member Author

@dajac I fixed those, and also changed the completeUnrealizedFutures() method to take a Stream, which is a little nicer since it allows the caller to do the filtering, thus removing the need for the overloaded version.

@dajac

dajac commented May 11, 2020

Copy link
Copy Markdown
Member

@tombentley thanks!

@hachikuji

Copy link
Copy Markdown
Contributor

ok to test

@mimaison

Copy link
Copy Markdown
Member

retest this please

try {
topicsResult.values().get("myTopic2").get();
fail("Expected an exception.");
} catch (ExecutionException e) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can use assertThrows() here. Same below

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't think we can, at least not if we want to check that topicsResult.values().get("myTopic2").get() throws an ExecutionException wrapping an ApiException. assertThrows() would only let us assert the outer exception type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry, I meant TestUtils.assertFutureThrows()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, thanks! Now fixed.

topicResult.topicName(), partitionResult.partitionIndex(), brokerId);
KafkaFutureImpl<Void> future = futures.get(replica);
if (future == null) {
log.warn("The partition {} in the response from broker {}} is not in the request",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's an extra }

@mimaison

mimaison commented Jun 4, 2020

Copy link
Copy Markdown
Member

Thanks @tombentley for the PR. It looks good. Happy to merge once the last 2 comments are addressed

@mimaison

mimaison commented Jun 4, 2020

Copy link
Copy Markdown
Member

retest this please

Comment thread clients/src/test/java/org/apache/kafka/clients/admin/KafkaAdminClientTest.java Outdated
Comment thread clients/src/test/java/org/apache/kafka/clients/admin/KafkaAdminClientTest.java Outdated
@tombentley

Copy link
Copy Markdown
Member Author

@mimaison done.

@mimaison

mimaison commented Jun 4, 2020

Copy link
Copy Markdown
Member

retest this please

@mimaison
mimaison merged commit 78e8a49 into apache:trunk Jun 4, 2020
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)
  ...
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