{"action":"created","comment":{"url":"https://api.github.com/repos/apache/kafka/pulls/comments/139964943","pull_request_review_id":63965505,"id":139964943,"diff_hunk":"@@ -0,0 +1,215 @@\n+/*\n+ * Licensed to the Apache Software Foundation (ASF) under one or more\n+ * contributor license agreements. See the NOTICE file distributed with\n+ * this work for additional information regarding copyright ownership.\n+ * The ASF licenses this file to You under the Apache License, Version 2.0\n+ * (the \"License\"); you may not use this file except in compliance with\n+ * the License. You may obtain a copy of the License at\n+ *\n+ *    http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+package org.apache.kafka.common.requests;\n+\n+import org.apache.kafka.clients.admin.NewPartitions;\n+import org.apache.kafka.common.protocol.ApiKeys;\n+import org.apache.kafka.common.protocol.types.ArrayOf;\n+import org.apache.kafka.common.protocol.types.Field;\n+import org.apache.kafka.common.protocol.types.Schema;\n+import org.apache.kafka.common.protocol.types.Struct;\n+\n+import java.nio.ByteBuffer;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.HashMap;\n+import java.util.HashSet;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Set;\n+\n+import static org.apache.kafka.common.protocol.CommonFields.TOPIC_NAME;\n+import static org.apache.kafka.common.protocol.types.Type.BOOLEAN;\n+import static org.apache.kafka.common.protocol.types.Type.INT32;\n+\n+public class CreatePartitionsRequest extends AbstractRequest {\n+\n+    private static final String TOPIC_PARTITION_COUNT_KEY_NAME = \"topic_partition_count\";\n+    private static final String NEW_PARTITIONS_KEY_NAME = \"new_partitions\";\n+    private static final String COUNT_KEY_NAME = \"count\";\n+    private static final String ASSIGNMENT_KEY_NAME = \"assignment\";\n+    private static final String TIMEOUT_KEY_NAME = \"timeout\";\n+    private static final String VALIDATE_ONLY_KEY_NAME = \"validate_only\";\n+\n+    private static final Schema CREATE_PARTITIONS_REQUEST_V0 = new Schema(\n+            new Field(TOPIC_PARTITION_COUNT_KEY_NAME, new ArrayOf(\n+                    new Schema(\n+                            TOPIC_NAME,\n+                            new Field(NEW_PARTITIONS_KEY_NAME, new Schema(\n+                                    new Field(COUNT_KEY_NAME, INT32, \"The new partition count.\"),\n+                                    new Field(ASSIGNMENT_KEY_NAME, ArrayOf.nullable(new ArrayOf(INT32)), \"The assigned brokers\")\n+                            )))),\n+                    \"List of topic and the corresponding partition count\"),\n+            new Field(TIMEOUT_KEY_NAME, INT32, \"The time in ms to wait for a topic to be altered.\"),\n+            new Field(VALIDATE_ONLY_KEY_NAME, BOOLEAN, \"If true then validate the request, but don't actually increase the partition count.\"));\n+\n+    public static Schema[] schemaVersions() {\n+        return new Schema[]{CREATE_PARTITIONS_REQUEST_V0};\n+    }\n+\n+    private final Set<String> duplicates;\n+    private final Map<String, NewPartitions> newPartitions;\n+    private final int timeout;\n+    private final boolean validateOnly;\n+\n+    public static class Builder extends AbstractRequest.Builder<CreatePartitionsRequest> {\n+\n+        private final Map<String, NewPartitions> newPartitions;\n+        private final int timeout;\n+        private final boolean validateOnly;\n+\n+        public Builder(Map<String, NewPartitions> newPartitions, int timeout, boolean validateOnly) {\n+            super(ApiKeys.CREATE_PARTITIONS);\n+            this.newPartitions = newPartitions;\n+            this.timeout = timeout;\n+            this.validateOnly = validateOnly;\n+        }\n+\n+        @Override\n+        public CreatePartitionsRequest build(short version) {\n+            return new CreatePartitionsRequest(newPartitions, timeout, validateOnly, version);\n+        }\n+\n+        @Override\n+        public String toString() {\n+            StringBuilder bld = new StringBuilder();\n+            bld.append(\"(type=CreatePartitionsRequest\").\n+                    append(\", newPartitions=\").append(newPartitions).\n+                    append(\", timeout=\").append(timeout).\n+                    append(\", validateOnly=\").append(validateOnly).\n+                    append(\")\");\n+            return bld.toString();\n+        }\n+    }\n+\n+    CreatePartitionsRequest(Map<String, NewPartitions> newPartitions, int timeout, boolean validateOnly, short apiVersion) {\n+        super(apiVersion);\n+        this.newPartitions = newPartitions;\n+        this.duplicates = Collections.emptySet();\n+        this.timeout = timeout;\n+        this.validateOnly = validateOnly;\n+    }\n+\n+    public CreatePartitionsRequest(Struct struct, short apiVersion) {\n+        super(apiVersion);\n+        Object[] topicCountArray = struct.getArray(TOPIC_PARTITION_COUNT_KEY_NAME);\n+        Map<String, NewPartitions> counts = new HashMap<>(topicCountArray.length);\n+        Set<String> dupes = Collections.emptySet();\n+        for (Object topicPartitionCountObj : topicCountArray) {\n+            Struct topicPartitionCountStruct = (Struct) topicPartitionCountObj;\n+            String topic = topicPartitionCountStruct.get(TOPIC_NAME);\n+            Struct partitionCountStruct = topicPartitionCountStruct.getStruct(NEW_PARTITIONS_KEY_NAME);\n+            int count = partitionCountStruct.getInt(COUNT_KEY_NAME);\n+            Object[] outerArray = partitionCountStruct.getArray(ASSIGNMENT_KEY_NAME);\n+            NewPartitions newPartition;\n+            if (outerArray != null) {\n+                List<List<Integer>> assignments = new ArrayList(outerArray.length);\n+                for (Object inner : outerArray) {\n+                    Object[] innerArray = (Object[]) inner;\n+                    List<Integer> innerList = new ArrayList<>(innerArray.length);\n+                    assignments.add(innerList);\n+                    for (Object broker : innerArray) {\n+                        innerList.add((Integer) broker);\n+                    }\n+                }\n+                newPartition = NewPartitions.increaseTo(count, assignments);\n+            } else {\n+                newPartition = NewPartitions.increaseTo(count);\n+            }\n+            NewPartitions dupe = counts.put(topic, newPartition);\n+            if (dupe != null) {\n+                if (dupes.isEmpty()) {\n+                    dupes = new HashSet<>();","path":"clients/src/main/java/org/apache/kafka/common/requests/CreatePartitionsRequest.java","position":137,"original_position":137,"commit_id":"d09b3aa5d2d6bb737845a4e671d903ab82b201e0","original_commit_id":"d09b3aa5d2d6bb737845a4e671d903ab82b201e0","user":{"login":"ijuma","id":24747,"avatar_url":"https://avatars2.githubusercontent.com/u/24747?v=4","gravatar_id":"","url":"https://api.github.com/users/ijuma","html_url":"https://github.com/ijuma","followers_url":"https://api.github.com/users/ijuma/followers","following_url":"https://api.github.com/users/ijuma/following{/other_user}","gists_url":"https://api.github.com/users/ijuma/gists{/gist_id}","starred_url":"https://api.github.com/users/ijuma/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ijuma/subscriptions","organizations_url":"https://api.github.com/users/ijuma/orgs","repos_url":"https://api.github.com/users/ijuma/repos","events_url":"https://api.github.com/users/ijuma/events{/privacy}","received_events_url":"https://api.github.com/users/ijuma/received_events","type":"User","site_admin":false},"body":"This isn't worth it, I don't think. Assigning a `new HashSet<>(0)` instead of `emptySet` is probably fine considering all the other work happening in this method.","created_at":"2017-09-20T13:15:37Z","updated_at":"2017-09-20T13:23:19Z","html_url":"https://github.com/apache/kafka/pull/3870#discussion_r139964943","pull_request_url":"https://api.github.com/repos/apache/kafka/pulls/3870","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/apache/kafka/pulls/comments/139964943"},"html":{"href":"https://github.com/apache/kafka/pull/3870#discussion_r139964943"},"pull_request":{"href":"https://api.github.com/repos/apache/kafka/pulls/3870"}}},"pull_request":{"url":"https://api.github.com/repos/apache/kafka/pulls/3870","id":141285504,"html_url":"https://github.com/apache/kafka/pull/3870","diff_url":"https://github.com/apache/kafka/pull/3870.diff","patch_url":"https://github.com/apache/kafka/pull/3870.patch","issue_url":"https://api.github.com/repos/apache/kafka/issues/3870","number":3870,"state":"open","locked":false,"title":"KAFKA-5856: Add AdminClient.createPartitions()","user":{"login":"tombentley","id":879487,"avatar_url":"https://avatars3.githubusercontent.com/u/879487?v=4","gravatar_id":"","url":"https://api.github.com/users/tombentley","html_url":"https://github.com/tombentley","followers_url":"https://api.github.com/users/tombentley/followers","following_url":"https://api.github.com/users/tombentley/following{/other_user}","gists_url":"https://api.github.com/users/tombentley/gists{/gist_id}","starred_url":"https://api.github.com/users/tombentley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tombentley/subscriptions","organizations_url":"https://api.github.com/users/tombentley/orgs","repos_url":"https://api.github.com/users/tombentley/repos","events_url":"https://api.github.com/users/tombentley/events{/privacy}","received_events_url":"https://api.github.com/users/tombentley/received_events","type":"User","site_admin":false},"body":"See KIP-195.\r\n\r\nThe contribution is my original work and I license the work to the project under the project's open source license.\r\n\r\nThis patch adds AdminClient.createPartitions() and the network protocol is\r\nuses. The broker-side algorithm is as follows:\r\n\r\n1. KafkaApis makes some initial checks on the request, then delegates to the\r\n   new AdminManager.createPartitions() method.\r\n2. AdminManager.createPartitions() performs some validation then delegates to\r\n   AdminUtils.addPartitions().\r\n\r\nAside: I felt it was safer to add the extra validation in\r\nAdminManager.createPartitions() than in AdminUtils.addPartitions() since the\r\nlatter is used on other code paths which might fail differently with the\r\nintroduction of extra checks.\r\n\r\n3. AdminUtils.addPartitions() does its own checks and adds the partitions.\r\n4. AdminManager then uses the existing topic purgatory to wait for the\r\n   PartitionInfo available from the metadata cache to become consistent with\r\n   the new total number of partitions.\r\n\r\nThe messages of exceptions thrown in AdminUtils affecting this new API have\r\nbeen made consistent with initial capital letter and terminating period.\r\nA few have been reworded for clarity. I've also standardized on using\r\nString.format().\r\n\r\ncc @ijuma","created_at":"2017-09-15T11:05:57Z","updated_at":"2017-09-20T13:23:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b7a7c0301a88115a17dec2e263a1999c2b798613","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/apache/kafka/pulls/3870/commits","review_comments_url":"https://api.github.com/repos/apache/kafka/pulls/3870/comments","review_comment_url":"https://api.github.com/repos/apache/kafka/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/kafka/issues/3870/comments","statuses_url":"https://api.github.com/repos/apache/kafka/statuses/d09b3aa5d2d6bb737845a4e671d903ab82b201e0","head":{"label":"tombentley:KAFKA-5856-AdminClient.createPartitions","ref":"KAFKA-5856-AdminClient.createPartitions","sha":"d09b3aa5d2d6bb737845a4e671d903ab82b201e0","user":{"login":"tombentley","id":879487,"avatar_url":"https://avatars3.githubusercontent.com/u/879487?v=4","gravatar_id":"","url":"https://api.github.com/users/tombentley","html_url":"https://github.com/tombentley","followers_url":"https://api.github.com/users/tombentley/followers","following_url":"https://api.github.com/users/tombentley/following{/other_user}","gists_url":"https://api.github.com/users/tombentley/gists{/gist_id}","starred_url":"https://api.github.com/users/tombentley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tombentley/subscriptions","organizations_url":"https://api.github.com/users/tombentley/orgs","repos_url":"https://api.github.com/users/tombentley/repos","events_url":"https://api.github.com/users/tombentley/events{/privacy}","received_events_url":"https://api.github.com/users/tombentley/received_events","type":"User","site_admin":false},"repo":{"id":93850932,"name":"kafka","full_name":"tombentley/kafka","owner":{"login":"tombentley","id":879487,"avatar_url":"https://avatars3.githubusercontent.com/u/879487?v=4","gravatar_id":"","url":"https://api.github.com/users/tombentley","html_url":"https://github.com/tombentley","followers_url":"https://api.github.com/users/tombentley/followers","following_url":"https://api.github.com/users/tombentley/following{/other_user}","gists_url":"https://api.github.com/users/tombentley/gists{/gist_id}","starred_url":"https://api.github.com/users/tombentley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tombentley/subscriptions","organizations_url":"https://api.github.com/users/tombentley/orgs","repos_url":"https://api.github.com/users/tombentley/repos","events_url":"https://api.github.com/users/tombentley/events{/privacy}","received_events_url":"https://api.github.com/users/tombentley/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/tombentley/kafka","description":"Mirror of Apache Kafka","fork":true,"url":"https://api.github.com/repos/tombentley/kafka","forks_url":"https://api.github.com/repos/tombentley/kafka/forks","keys_url":"https://api.github.com/repos/tombentley/kafka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tombentley/kafka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tombentley/kafka/teams","hooks_url":"https://api.github.com/repos/tombentley/kafka/hooks","issue_events_url":"https://api.github.com/repos/tombentley/kafka/issues/events{/number}","events_url":"https://api.github.com/repos/tombentley/kafka/events","assignees_url":"https://api.github.com/repos/tombentley/kafka/assignees{/user}","branches_url":"https://api.github.com/repos/tombentley/kafka/branches{/branch}","tags_url":"https://api.github.com/repos/tombentley/kafka/tags","blobs_url":"https://api.github.com/repos/tombentley/kafka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tombentley/kafka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tombentley/kafka/git/refs{/sha}","trees_url":"https://api.github.com/repos/tombentley/kafka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tombentley/kafka/statuses/{sha}","languages_url":"https://api.github.com/repos/tombentley/kafka/languages","stargazers_url":"https://api.github.com/repos/tombentley/kafka/stargazers","contributors_url":"https://api.github.com/repos/tombentley/kafka/contributors","subscribers_url":"https://api.github.com/repos/tombentley/kafka/subscribers","subscription_url":"https://api.github.com/repos/tombentley/kafka/subscription","commits_url":"https://api.github.com/repos/tombentley/kafka/commits{/sha}","git_commits_url":"https://api.github.com/repos/tombentley/kafka/git/commits{/sha}","comments_url":"https://api.github.com/repos/tombentley/kafka/comments{/number}","issue_comment_url":"https://api.github.com/repos/tombentley/kafka/issues/comments{/number}","contents_url":"https://api.github.com/repos/tombentley/kafka/contents/{+path}","compare_url":"https://api.github.com/repos/tombentley/kafka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tombentley/kafka/merges","archive_url":"https://api.github.com/repos/tombentley/kafka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tombentley/kafka/downloads","issues_url":"https://api.github.com/repos/tombentley/kafka/issues{/number}","pulls_url":"https://api.github.com/repos/tombentley/kafka/pulls{/number}","milestones_url":"https://api.github.com/repos/tombentley/kafka/milestones{/number}","notifications_url":"https://api.github.com/repos/tombentley/kafka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tombentley/kafka/labels{/name}","releases_url":"https://api.github.com/repos/tombentley/kafka/releases{/id}","deployments_url":"https://api.github.com/repos/tombentley/kafka/deployments","created_at":"2017-06-09T11:08:28Z","updated_at":"2017-06-09T11:08:37Z","pushed_at":"2017-09-19T20:45:14Z","git_url":"git://github.com/tombentley/kafka.git","ssh_url":"git@github.com:tombentley/kafka.git","clone_url":"https://github.com/tombentley/kafka.git","svn_url":"https://github.com/tombentley/kafka","homepage":null,"size":55971,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"trunk"}},"base":{"label":"apache:trunk","ref":"trunk","sha":"5e0bb3df9679797f0036d4718e2c1fd22dbee8f2","user":{"login":"apache","id":47359,"avatar_url":"https://avatars0.githubusercontent.com/u/47359?v=4","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":2211243,"name":"kafka","full_name":"apache/kafka","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars0.githubusercontent.com/u/47359?v=4","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/kafka","description":"Mirror of Apache Kafka","fork":false,"url":"https://api.github.com/repos/apache/kafka","forks_url":"https://api.github.com/repos/apache/kafka/forks","keys_url":"https://api.github.com/repos/apache/kafka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/kafka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/kafka/teams","hooks_url":"https://api.github.com/repos/apache/kafka/hooks","issue_events_url":"https://api.github.com/repos/apache/kafka/issues/events{/number}","events_url":"https://api.github.com/repos/apache/kafka/events","assignees_url":"https://api.github.com/repos/apache/kafka/assignees{/user}","branches_url":"https://api.github.com/repos/apache/kafka/branches{/branch}","tags_url":"https://api.github.com/repos/apache/kafka/tags","blobs_url":"https://api.github.com/repos/apache/kafka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/kafka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/kafka/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/kafka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/kafka/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/kafka/languages","stargazers_url":"https://api.github.com/repos/apache/kafka/stargazers","contributors_url":"https://api.github.com/repos/apache/kafka/contributors","subscribers_url":"https://api.github.com/repos/apache/kafka/subscribers","subscription_url":"https://api.github.com/repos/apache/kafka/subscription","commits_url":"https://api.github.com/repos/apache/kafka/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/kafka/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/kafka/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/kafka/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/kafka/contents/{+path}","compare_url":"https://api.github.com/repos/apache/kafka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/kafka/merges","archive_url":"https://api.github.com/repos/apache/kafka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/kafka/downloads","issues_url":"https://api.github.com/repos/apache/kafka/issues{/number}","pulls_url":"https://api.github.com/repos/apache/kafka/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/kafka/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/kafka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/kafka/labels{/name}","releases_url":"https://api.github.com/repos/apache/kafka/releases{/id}","deployments_url":"https://api.github.com/repos/apache/kafka/deployments","created_at":"2011-08-15T18:06:16Z","updated_at":"2017-09-20T12:50:39Z","pushed_at":"2017-09-20T12:57:53Z","git_url":"git://github.com/apache/kafka.git","ssh_url":"git@github.com:apache/kafka.git","clone_url":"https://github.com/apache/kafka.git","svn_url":"https://github.com/apache/kafka","homepage":null,"size":56583,"stargazers_count":6170,"watchers_count":6170,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3783,"mirror_url":"git://git.apache.org/kafka.git","open_issues_count":461,"forks":3783,"open_issues":461,"watchers":6170,"default_branch":"trunk"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/kafka/pulls/3870"},"html":{"href":"https://github.com/apache/kafka/pull/3870"},"issue":{"href":"https://api.github.com/repos/apache/kafka/issues/3870"},"comments":{"href":"https://api.github.com/repos/apache/kafka/issues/3870/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/kafka/pulls/3870/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/kafka/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/kafka/pulls/3870/commits"},"statuses":{"href":"https://api.github.com/repos/apache/kafka/statuses/d09b3aa5d2d6bb737845a4e671d903ab82b201e0"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"ijuma"}}