Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KAFKA-6391 ensure topics are created with correct partitions BEFORE building the… #4347

Merged
merged 7 commits into from
Jan 2, 2018

Conversation

cvaliente
Copy link
Contributor

ensure topics are created with correct partitions BEFORE building the metadata for our stream tasks

First ensureCoPartitioning() on repartitionTopicMetadata before creating allRepartitionTopicPartitions

Committer Checklist (excluded from commit message)

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

@cvaliente
Copy link
Contributor Author

@guozhangwang Could you have a look on this as well since you seem to have contributed a lot to this class before?

@mjsax
Copy link
Member

mjsax commented Dec 26, 2017

@cvaliente Thx for the PR. Can you elaborate on this change? The return value of copartitionGroups() as well as repartitionTopicMetadata and metadata should be the same before or after the for loop. What is the issue you try to address and why does this PR fix it?

@cvaliente
Copy link
Contributor Author

cvaliente commented Dec 27, 2017

@mjsax ensureCopartitioning doesn't have a return value (a pattern I'm not particular fond of). It modifies the input parameter repartitionTopicMetadata - increasing the number of partitions for copartitioned topics when necessary.

allRepartitionTopicPartitions is built from the information in repartitionTopicMetadata and used to generate all the streams task.

previously, allRepartitionTopicPartitions was built before running ensureCopartitioning on repartitionTopicMetadata, so any extra partitions added by ensureCopartitioning did not have a streams task generated from them - those partitions were being written to, but no data read from.

@cvaliente
Copy link
Contributor Author

With this PR, we first validate our repartitionTopicMetadata to make sure number of partitions is correct, then we create the topics & partitions if needed, and then we create the tasks for those topics & partitions.

Copy link
Member

@mjsax mjsax left a comment

Choose a reason for hiding this comment

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

Thanks for the explanation. I missed the fact that we indeed modify repartitionTopicMetadata. Good catch and thanks for the patch!

LGMT.

Call for second review @bbejeck @dguy @guozhangwang

@bbejeck
Copy link
Contributor

bbejeck commented Dec 27, 2017

Thanks for the patch @cvaliente. LGTM, but I think we should have an integration test for this.

Clemens Valiente added 3 commits December 28, 2017 10:11
The test should fail with the old logic, because:
While stream-partition-assignor-test-KSTREAM-MAP-0000000001-repartition is created correctly with four partitions,
the StreamPartitionAssignor will only assign three tasks to the topic. Test passes with the new logic.
new TopicPartition("topic1", 0),
new TopicPartition("topic1", 1),
new TopicPartition("topic1", 2),
new TopicPartition("topic3", 0),
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider naming the topic "topic2" since there are only two topics in the test

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am using the source topics defined in the test class for this. Since topic2 has the same number of partitions as topic1, it is not suitable for this test case.

@cvaliente
Copy link
Contributor Author

@bbejeck @mjsax I added a test case covering this.
The old logic fails when checking the assigned tasks, the new logic passes the test.

Copy link
Member

@mjsax mjsax left a comment

Choose a reason for hiding this comment

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

Thanks for adding a test!

// joining the stream and the table
// this triggers the enforceCopartitioning() routine in the StreamPartitionAssignor,
// forcing the stream.map to get repartitioned to a topic with four partitions.
stream1.join(table1,
Copy link
Member

Choose a reason for hiding this comment

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

nit: move table1 to next line, or fix indention below to align with table1

final UUID uuid = UUID.randomUUID();
final String client = "client1";

mockTaskManager(Collections.<TaskId>emptySet(),
Copy link
Member

Choose a reason for hiding this comment

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

nit: as above

subscriptions.put(
client,
new PartitionAssignor.Subscription(
Collections.singletonList("unknownTopic"),
Copy link
Member

Choose a reason for hiding this comment

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

why do we put "unknownTopic" here? Should we subscribe to "topic1" and "topic3"? Or we can actually pass in an empty list?

I guess this is copied from shouldNotLoopInfinitelyOnMissingMetadataAndShouldNotCreateRelatedTasks -- there we need to pass in unknownTopic as this topic does not exist in cluster metadata.

expectedCreatedInternalTopics.put(applicationId + "-topic3-STATE-STORE-0000000002-changelog", 4);

// check if all internal topics were created as expected
assertThat(mockInternalTopicManager.readyTopics, equalTo(expectedCreatedInternalTopics));
Copy link
Member

Choose a reason for hiding this comment

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

Nit: assertThat take expected result as first parameter IIRC (otherwise error message on failing test is "reversed")

);

// check if we created a task for all expected topicpartitions.
assertThat(new HashSet<>(assignment.get(client).partitions()), equalTo(new HashSet<>(expectedAssignment)));
Copy link
Member

Choose a reason for hiding this comment

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

as above. (please fix in shouldNotLoopInfinitelyOnMissingMetadataAndShouldNotCreateRelatedTasks, too)

@cvaliente
Copy link
Contributor Author

@mjsax thanks for the feedback, done!

@bbejeck
Copy link
Contributor

bbejeck commented Dec 29, 2017

@cvaliente thanks for adding the test, LGTM.

Copy link
Member

@mjsax mjsax left a comment

Choose a reason for hiding this comment

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

Sorry for the forth and back :(

return new KeyValue<>(key, value);
}
})
.count(Materialized.<Object, Long, KeyValueStore<Bytes, byte[]>>as("count"));
Copy link
Member

Choose a reason for hiding this comment

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

We actually don't need to name the store. This could be .count() plus updating the name for the repartitioning and changelog topic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

know the naming was not required. My thinking is it would be preferable to explicitly choose the name so I don't have to rely on internal logic for naming the repartition topic that I later refer to. Unfortunately there's no explicit naming for the streams.map() repartition topic so it is all moot anyway.

Copy link
Member

Choose a reason for hiding this comment

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

I understand your point -- however, naming must be deterministic and should not change from release to release (otherwise, upgrading hard harder and we need to mention it in the upgrade docs). Thus, if we use internal names in tests, we have some implicit testing that naming does not change. That's why I prefer to not name the operator, too, if not required.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I guess that's ok. I agree that an upgrade should not require a reset of the streams application's topology unless absolutely necessary. However I think in that case we should also have some explicit testing for the naming of itnernal topics (not sure if those exist yet?)

Either way I will fix this PR up once I am back in the office.

expectedCreatedInternalTopics.put(applicationId + "-topic3-STATE-STORE-0000000002-changelog", 4);

// check if all internal topics were created as expected
assertThat(expectedCreatedInternalTopics, equalTo(mockInternalTopicManager.readyTopics));
Copy link
Member

Choose a reason for hiding this comment

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

Sorry for the forth and back -- for assertThat you original code was correct and expected argument is second one... (it different for assertEquals -- my bad(!)).

);

// check if we created a task for all expected topicpartitions.
assertThat(new HashSet<>(expectedAssignment), equalTo(new HashSet<>(assignment.get(client).partitions())));
Copy link
Member

Choose a reason for hiding this comment

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

as above: flip arguments

Collections.<TaskId>emptySet(),
uuid1,
builder);
mockTaskManager(
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the cleanup! Really appreciated!

@cvaliente
Copy link
Contributor Author

@mjsax @bbejeck Cleaned up all the things that were commented on.

@guozhangwang guozhangwang merged commit 5ca1226 into apache:trunk Jan 2, 2018
@guozhangwang
Copy link
Contributor

LGTM! Merged to trunk.

@cvaliente cvaliente deleted the KAFKA-6391 branch January 2, 2018 22:44
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.

5 participants