-
Notifications
You must be signed in to change notification settings - Fork 14.2k
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-18629: ShareGroupDeleteState admin client impl. #18928
Changes from 7 commits
56e2aa8
0a6eb65
bb0893c
24207cc
4fd9afd
ba96a6f
3d2c4b7
2da770d
81056ec
0afbffa
19d0439
a54a23b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.apache.kafka.clients.admin; | ||
|
||
import org.apache.kafka.common.annotation.InterfaceStability; | ||
|
||
import java.util.Collection; | ||
|
||
/** | ||
* Options for the {@link Admin#deleteShareGroups(Collection <String>, DeleteShareGroupsOptions)} call. | ||
* <p> | ||
* The API of this class is evolving, see {@link Admin} for details. | ||
*/ | ||
@InterfaceStability.Evolving | ||
public class DeleteShareGroupsOptions extends AbstractOptions<DeleteShareGroupsOptions> { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.apache.kafka.clients.admin; | ||
|
||
import org.apache.kafka.common.KafkaFuture; | ||
import org.apache.kafka.common.annotation.InterfaceStability; | ||
|
||
import java.util.Collection; | ||
import java.util.Map; | ||
|
||
/** | ||
* The result of the {@link Admin#deleteShareGroups(Collection <String>, DeleteShareGroupsOptions)} call. | ||
* <p> | ||
* The API of this class is evolving, see {@link Admin} for details. | ||
*/ | ||
@InterfaceStability.Evolving | ||
public class DeleteShareGroupsResult extends DeleteConsumerGroupsResult { | ||
DeleteShareGroupsResult(final Map<String, KafkaFuture<Void>> futures) { | ||
super(futures); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The implementation for the public methods here could be added. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As of now, it is exactly the same as the parent class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, OK. I suggest making a common superclass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.apache.kafka.clients.admin.internals; | ||
|
||
import org.apache.kafka.common.utils.LogContext; | ||
|
||
public class DeleteShareGroupsHandler extends DeleteConsumerGroupsHandler { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, in a similar vein, maybe having a common |
||
public DeleteShareGroupsHandler( | ||
LogContext logContext | ||
) { | ||
super(logContext, DeleteShareGroupsHandler.class); | ||
} | ||
|
||
@Override | ||
public String apiName() { | ||
return "deleteShareGroups"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.apache.kafka.clients.admin.internals; | ||
|
||
import org.apache.kafka.common.utils.LogContext; | ||
|
||
public class DeleteShareGroupsHandlerTest extends DeleteConsumerGroupsHandlerTest { | ||
private final LogContext logContext = new LogContext(); | ||
|
||
@Override | ||
protected DeleteConsumerGroupsHandler getHandler() { | ||
return new DeleteShareGroupsHandler(logContext); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,9 @@ | |
package org.apache.kafka.tools.consumer.group; | ||
|
||
import org.apache.kafka.clients.CommonClientConfigs; | ||
import org.apache.kafka.clients.admin.AbstractOptions; | ||
import org.apache.kafka.clients.admin.Admin; | ||
import org.apache.kafka.clients.admin.DeleteShareGroupsOptions; | ||
import org.apache.kafka.clients.admin.DescribeShareGroupsOptions; | ||
import org.apache.kafka.clients.admin.GroupListing; | ||
import org.apache.kafka.clients.admin.ListGroupsOptions; | ||
|
@@ -81,7 +83,7 @@ public static void run(ShareGroupCommandOptions opts) { | |
} else if (opts.options.has(opts.describeOpt)) { | ||
shareGroupService.describeGroups(); | ||
} else if (opts.options.has(opts.deleteOpt)) { | ||
throw new UnsupportedOperationException("--delete option is not yet implemented"); | ||
shareGroupService.deleteShareGroups(); | ||
} else if (opts.options.has(opts.resetOffsetsOpt)) { | ||
throw new UnsupportedOperationException("--reset-offsets option is not yet implemented"); | ||
} else if (opts.options.has(opts.deleteOffsetsOpt)) { | ||
|
@@ -154,6 +156,18 @@ List<String> listShareGroups() { | |
} | ||
} | ||
|
||
List<GroupListing> listDetailedShareGroups() { | ||
try { | ||
ListGroupsResult result = adminClient.listGroups(new ListGroupsOptions() | ||
.timeoutMs(opts.options.valueOf(opts.timeoutMsOpt).intValue()) | ||
.withTypes(Set.of(GroupType.SHARE))); | ||
Collection<GroupListing> listings = result.all().get(); | ||
return listings.stream().toList(); | ||
} catch (InterruptedException | ExecutionException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
List<GroupListing> listShareGroupsInStates(Set<GroupState> states) throws ExecutionException, InterruptedException { | ||
ListGroupsResult result = adminClient.listGroups(new ListGroupsOptions() | ||
.timeoutMs(opts.options.valueOf(opts.timeoutMsOpt).intValue()) | ||
|
@@ -208,6 +222,67 @@ public void describeGroups() throws ExecutionException, InterruptedException { | |
} | ||
} | ||
|
||
Map<String, Throwable> deleteShareGroups() { | ||
List<GroupListing> shareGroupIds = listDetailedShareGroups(); | ||
List<String> groupIds = opts.options.has(opts.allGroupsOpt) | ||
? shareGroupIds.stream().map(GroupListing::groupId).toList() | ||
: opts.options.valuesOf(opts.groupOpt); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because In the |
||
if (groupIds.isEmpty()) { | ||
throw new IllegalArgumentException("--groups or --all-groups argument is mandatory"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have said that the checking that there is either As it stands, if the user specified There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AndrewJSchofield |
||
} | ||
|
||
for (String groupId : groupIds) { | ||
Optional<GroupListing> listing = shareGroupIds.stream().filter(item -> item.groupId().equals(groupId)).findAny(); | ||
if (listing.isEmpty()) { | ||
throw new IllegalArgumentException("Group '" + groupId + "' is not a share group."); | ||
} else { | ||
Optional<GroupState> state = listing.get().groupState(); | ||
if (state.isPresent() && !state.get().equals(GroupState.EMPTY)) { | ||
throw new IllegalStateException("Share group '" + groupId + "' is not EMPTY."); | ||
} | ||
} | ||
} | ||
|
||
Map<String, KafkaFuture<Void>> groupsToDelete = adminClient.deleteShareGroups( | ||
groupIds, | ||
withTimeoutMs(new DeleteShareGroupsOptions()) | ||
).deletedGroups(); | ||
|
||
Map<String, Throwable> success = new HashMap<>(); | ||
Map<String, Throwable> failed = new HashMap<>(); | ||
|
||
groupsToDelete.forEach((g, f) -> { | ||
try { | ||
f.get(); | ||
success.put(g, null); | ||
} catch (InterruptedException ie) { | ||
failed.put(g, ie); | ||
} catch (ExecutionException e) { | ||
failed.put(g, e.getCause()); | ||
} | ||
}); | ||
|
||
if (failed.isEmpty()) | ||
System.out.println("Deletion of requested share groups (" + "'" + success.keySet().stream().map(Object::toString).collect(Collectors.joining(", ")) + "'" + ") was successful."); | ||
else { | ||
printError("Deletion of some share groups failed:", Optional.empty()); | ||
failed.forEach((group, error) -> System.out.println("* Share Group '" + group + "' could not be deleted due to: " + error)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
|
||
if (!success.isEmpty()) | ||
System.out.println("\nThese share groups were deleted successfully: " + "'" + success.keySet().stream().map(Object::toString).collect(Collectors.joining("'")) + "', '"); | ||
} | ||
|
||
failed.putAll(success); | ||
|
||
return failed; | ||
} | ||
|
||
private <T extends AbstractOptions<T>> T withTimeoutMs(T options) { | ||
int t = opts.options.valueOf(opts.timeoutMsOpt).intValue(); | ||
return options.timeoutMs(t); | ||
} | ||
|
||
Map<String, ShareGroupDescription> describeShareGroups(Collection<String> groupIds) throws ExecutionException, InterruptedException { | ||
Map<String, ShareGroupDescription> res = new HashMap<>(); | ||
Map<String, KafkaFuture<ShareGroupDescription>> stringKafkaFutureMap = adminClient.describeShareGroups( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I'd prefer to see these two methods declared with the other share group methods, not mixed in with the consumer group methods.