mirror of https://github.com/apache/kafka.git
KAFKA-17949: Introduce GroupState and replace ShareGroupState (#17763)
This PR introduces the unified GroupState enum for all group types from KIP-1043. This PR also removes ShareGroupState and begins the work to replace Admin.listShareGroups with Admin.listGroups. That will complete in a future PR. Reviewers: Manikumar Reddy <manikumar.reddy@gmail.com>
This commit is contained in:
parent
a211ee99b5
commit
32c887b05e
|
@ -18,6 +18,7 @@
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.ConsumerGroupState;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.acl.AclOperation;
|
import org.apache.kafka.common.acl.AclOperation;
|
||||||
|
@ -38,10 +39,14 @@ public class ConsumerGroupDescription {
|
||||||
private final Collection<MemberDescription> members;
|
private final Collection<MemberDescription> members;
|
||||||
private final String partitionAssignor;
|
private final String partitionAssignor;
|
||||||
private final GroupType type;
|
private final GroupType type;
|
||||||
private final ConsumerGroupState state;
|
private final GroupState groupState;
|
||||||
private final Node coordinator;
|
private final Node coordinator;
|
||||||
private final Set<AclOperation> authorizedOperations;
|
private final Set<AclOperation> authorizedOperations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Since 4.0. Use {@link #ConsumerGroupDescription(String, boolean, Collection, String, GroupState, Node)}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public ConsumerGroupDescription(String groupId,
|
public ConsumerGroupDescription(String groupId,
|
||||||
boolean isSimpleConsumerGroup,
|
boolean isSimpleConsumerGroup,
|
||||||
Collection<MemberDescription> members,
|
Collection<MemberDescription> members,
|
||||||
|
@ -51,6 +56,10 @@ public class ConsumerGroupDescription {
|
||||||
this(groupId, isSimpleConsumerGroup, members, partitionAssignor, state, coordinator, Collections.emptySet());
|
this(groupId, isSimpleConsumerGroup, members, partitionAssignor, state, coordinator, Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Since 4.0. Use {@link #ConsumerGroupDescription(String, boolean, Collection, String, GroupState, Node, Set)}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public ConsumerGroupDescription(String groupId,
|
public ConsumerGroupDescription(String groupId,
|
||||||
boolean isSimpleConsumerGroup,
|
boolean isSimpleConsumerGroup,
|
||||||
Collection<MemberDescription> members,
|
Collection<MemberDescription> members,
|
||||||
|
@ -61,6 +70,10 @@ public class ConsumerGroupDescription {
|
||||||
this(groupId, isSimpleConsumerGroup, members, partitionAssignor, GroupType.CLASSIC, state, coordinator, authorizedOperations);
|
this(groupId, isSimpleConsumerGroup, members, partitionAssignor, GroupType.CLASSIC, state, coordinator, authorizedOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Since 4.0. Use {@link #ConsumerGroupDescription(String, boolean, Collection, String, GroupType, GroupState, Node, Set)}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public ConsumerGroupDescription(String groupId,
|
public ConsumerGroupDescription(String groupId,
|
||||||
boolean isSimpleConsumerGroup,
|
boolean isSimpleConsumerGroup,
|
||||||
Collection<MemberDescription> members,
|
Collection<MemberDescription> members,
|
||||||
|
@ -75,7 +88,45 @@ public class ConsumerGroupDescription {
|
||||||
Collections.unmodifiableList(new ArrayList<>(members));
|
Collections.unmodifiableList(new ArrayList<>(members));
|
||||||
this.partitionAssignor = partitionAssignor == null ? "" : partitionAssignor;
|
this.partitionAssignor = partitionAssignor == null ? "" : partitionAssignor;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.state = state;
|
this.groupState = GroupState.parse(state.name());
|
||||||
|
this.coordinator = coordinator;
|
||||||
|
this.authorizedOperations = authorizedOperations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConsumerGroupDescription(String groupId,
|
||||||
|
boolean isSimpleConsumerGroup,
|
||||||
|
Collection<MemberDescription> members,
|
||||||
|
String partitionAssignor,
|
||||||
|
GroupState groupState,
|
||||||
|
Node coordinator) {
|
||||||
|
this(groupId, isSimpleConsumerGroup, members, partitionAssignor, groupState, coordinator, Collections.emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConsumerGroupDescription(String groupId,
|
||||||
|
boolean isSimpleConsumerGroup,
|
||||||
|
Collection<MemberDescription> members,
|
||||||
|
String partitionAssignor,
|
||||||
|
GroupState groupState,
|
||||||
|
Node coordinator,
|
||||||
|
Set<AclOperation> authorizedOperations) {
|
||||||
|
this(groupId, isSimpleConsumerGroup, members, partitionAssignor, GroupType.CLASSIC, groupState, coordinator, authorizedOperations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConsumerGroupDescription(String groupId,
|
||||||
|
boolean isSimpleConsumerGroup,
|
||||||
|
Collection<MemberDescription> members,
|
||||||
|
String partitionAssignor,
|
||||||
|
GroupType type,
|
||||||
|
GroupState groupState,
|
||||||
|
Node coordinator,
|
||||||
|
Set<AclOperation> authorizedOperations) {
|
||||||
|
this.groupId = groupId == null ? "" : groupId;
|
||||||
|
this.isSimpleConsumerGroup = isSimpleConsumerGroup;
|
||||||
|
this.members = members == null ? Collections.emptyList() :
|
||||||
|
Collections.unmodifiableList(new ArrayList<>(members));
|
||||||
|
this.partitionAssignor = partitionAssignor == null ? "" : partitionAssignor;
|
||||||
|
this.type = type;
|
||||||
|
this.groupState = groupState;
|
||||||
this.coordinator = coordinator;
|
this.coordinator = coordinator;
|
||||||
this.authorizedOperations = authorizedOperations;
|
this.authorizedOperations = authorizedOperations;
|
||||||
}
|
}
|
||||||
|
@ -90,14 +141,14 @@ public class ConsumerGroupDescription {
|
||||||
Objects.equals(members, that.members) &&
|
Objects.equals(members, that.members) &&
|
||||||
Objects.equals(partitionAssignor, that.partitionAssignor) &&
|
Objects.equals(partitionAssignor, that.partitionAssignor) &&
|
||||||
type == that.type &&
|
type == that.type &&
|
||||||
state == that.state &&
|
groupState == that.groupState &&
|
||||||
Objects.equals(coordinator, that.coordinator) &&
|
Objects.equals(coordinator, that.coordinator) &&
|
||||||
Objects.equals(authorizedOperations, that.authorizedOperations);
|
Objects.equals(authorizedOperations, that.authorizedOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(groupId, isSimpleConsumerGroup, members, partitionAssignor, type, state, coordinator, authorizedOperations);
|
return Objects.hash(groupId, isSimpleConsumerGroup, members, partitionAssignor, type, groupState, coordinator, authorizedOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,9 +189,18 @@ public class ConsumerGroupDescription {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The consumer group state, or UNKNOWN if the state is too new for us to parse.
|
* The consumer group state, or UNKNOWN if the state is too new for us to parse.
|
||||||
|
* @deprecated Since 4.0. Use {@link #groupState()} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public ConsumerGroupState state() {
|
public ConsumerGroupState state() {
|
||||||
return state;
|
return ConsumerGroupState.parse(groupState.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The group state, or UNKNOWN if the state is too new for us to parse.
|
||||||
|
*/
|
||||||
|
public GroupState groupState() {
|
||||||
|
return groupState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,7 +224,7 @@ public class ConsumerGroupDescription {
|
||||||
", members=" + members.stream().map(MemberDescription::toString).collect(Collectors.joining(",")) +
|
", members=" + members.stream().map(MemberDescription::toString).collect(Collectors.joining(",")) +
|
||||||
", partitionAssignor=" + partitionAssignor +
|
", partitionAssignor=" + partitionAssignor +
|
||||||
", type=" + type +
|
", type=" + type +
|
||||||
", state=" + state +
|
", groupState=" + groupState +
|
||||||
", coordinator=" + coordinator +
|
", coordinator=" + coordinator +
|
||||||
", authorizedOperations=" + authorizedOperations +
|
", authorizedOperations=" + authorizedOperations +
|
||||||
")";
|
")";
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.ConsumerGroupState;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -29,28 +30,30 @@ import java.util.Optional;
|
||||||
public class ConsumerGroupListing {
|
public class ConsumerGroupListing {
|
||||||
private final String groupId;
|
private final String groupId;
|
||||||
private final boolean isSimpleConsumerGroup;
|
private final boolean isSimpleConsumerGroup;
|
||||||
private final Optional<ConsumerGroupState> state;
|
private final Optional<GroupState> groupState;
|
||||||
private final Optional<GroupType> type;
|
private final Optional<GroupType> type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance with the specified parameters.
|
* Create an instance with the specified parameters.
|
||||||
*
|
*
|
||||||
* @param groupId Group Id
|
* @param groupId Group Id.
|
||||||
* @param isSimpleConsumerGroup If consumer group is simple or not.
|
* @param isSimpleConsumerGroup If consumer group is simple or not.
|
||||||
*/
|
*/
|
||||||
public ConsumerGroupListing(String groupId, boolean isSimpleConsumerGroup) {
|
public ConsumerGroupListing(String groupId, boolean isSimpleConsumerGroup) {
|
||||||
this(groupId, isSimpleConsumerGroup, Optional.empty(), Optional.empty());
|
this(groupId, Optional.empty(), Optional.empty(), isSimpleConsumerGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance with the specified parameters.
|
* Create an instance with the specified parameters.
|
||||||
*
|
*
|
||||||
* @param groupId Group Id
|
* @param groupId Group Id.
|
||||||
* @param isSimpleConsumerGroup If consumer group is simple or not.
|
* @param isSimpleConsumerGroup If consumer group is simple or not.
|
||||||
* @param state The state of the consumer group
|
* @param state The state of the consumer group.
|
||||||
|
* @deprecated Since 4.0. Use {@link #ConsumerGroupListing(String, Optional, boolean)}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public ConsumerGroupListing(String groupId, boolean isSimpleConsumerGroup, Optional<ConsumerGroupState> state) {
|
public ConsumerGroupListing(String groupId, boolean isSimpleConsumerGroup, Optional<ConsumerGroupState> state) {
|
||||||
this(groupId, isSimpleConsumerGroup, state, Optional.empty());
|
this(groupId, Objects.requireNonNull(state).map(state0 -> GroupState.parse(state0.name())), Optional.empty(), isSimpleConsumerGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,17 +63,51 @@ public class ConsumerGroupListing {
|
||||||
* @param isSimpleConsumerGroup If consumer group is simple or not.
|
* @param isSimpleConsumerGroup If consumer group is simple or not.
|
||||||
* @param state The state of the consumer group.
|
* @param state The state of the consumer group.
|
||||||
* @param type The type of the consumer group.
|
* @param type The type of the consumer group.
|
||||||
|
* @deprecated Since 4.0. Use {@link #ConsumerGroupListing(String, Optional, Optional, boolean)}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public ConsumerGroupListing(
|
public ConsumerGroupListing(
|
||||||
String groupId,
|
String groupId,
|
||||||
boolean isSimpleConsumerGroup,
|
boolean isSimpleConsumerGroup,
|
||||||
Optional<ConsumerGroupState> state,
|
Optional<ConsumerGroupState> state,
|
||||||
Optional<GroupType> type
|
Optional<GroupType> type
|
||||||
|
) {
|
||||||
|
this(groupId, Objects.requireNonNull(state).map(state0 -> GroupState.parse(state0.name())), type, isSimpleConsumerGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance with the specified parameters.
|
||||||
|
*
|
||||||
|
* @param groupId Group Id.
|
||||||
|
* @param groupState The state of the consumer group.
|
||||||
|
* @param isSimpleConsumerGroup If consumer group is simple or not.
|
||||||
|
*/
|
||||||
|
public ConsumerGroupListing(
|
||||||
|
String groupId,
|
||||||
|
Optional<GroupState> groupState,
|
||||||
|
boolean isSimpleConsumerGroup
|
||||||
|
) {
|
||||||
|
this(groupId, groupState, Optional.empty(), isSimpleConsumerGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance with the specified parameters.
|
||||||
|
*
|
||||||
|
* @param groupId Group Id.
|
||||||
|
* @param groupState The state of the consumer group.
|
||||||
|
* @param type The type of the consumer group.
|
||||||
|
* @param isSimpleConsumerGroup If consumer group is simple or not.
|
||||||
|
*/
|
||||||
|
public ConsumerGroupListing(
|
||||||
|
String groupId,
|
||||||
|
Optional<GroupState> groupState,
|
||||||
|
Optional<GroupType> type,
|
||||||
|
boolean isSimpleConsumerGroup
|
||||||
) {
|
) {
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
this.isSimpleConsumerGroup = isSimpleConsumerGroup;
|
this.groupState = Objects.requireNonNull(groupState);
|
||||||
this.state = Objects.requireNonNull(state);
|
|
||||||
this.type = Objects.requireNonNull(type);
|
this.type = Objects.requireNonNull(type);
|
||||||
|
this.isSimpleConsumerGroup = isSimpleConsumerGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,10 +125,19 @@ public class ConsumerGroupListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumer Group state
|
* Group state
|
||||||
*/
|
*/
|
||||||
|
public Optional<GroupState> groupState() {
|
||||||
|
return groupState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumer Group state
|
||||||
|
* @deprecated Since 4.0. Use {@link #groupState()}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public Optional<ConsumerGroupState> state() {
|
public Optional<ConsumerGroupState> state() {
|
||||||
return state;
|
return groupState.map(state0 -> ConsumerGroupState.parse(state0.name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,14 +154,14 @@ public class ConsumerGroupListing {
|
||||||
return "(" +
|
return "(" +
|
||||||
"groupId='" + groupId + '\'' +
|
"groupId='" + groupId + '\'' +
|
||||||
", isSimpleConsumerGroup=" + isSimpleConsumerGroup +
|
", isSimpleConsumerGroup=" + isSimpleConsumerGroup +
|
||||||
", state=" + state +
|
", groupState=" + groupState +
|
||||||
", type=" + type +
|
", type=" + type +
|
||||||
')';
|
')';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(groupId, isSimpleConsumerGroup(), state, type);
|
return Objects.hash(groupId, isSimpleConsumerGroup(), groupState, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -125,7 +171,7 @@ public class ConsumerGroupListing {
|
||||||
ConsumerGroupListing that = (ConsumerGroupListing) o;
|
ConsumerGroupListing that = (ConsumerGroupListing) o;
|
||||||
return isSimpleConsumerGroup() == that.isSimpleConsumerGroup() &&
|
return isSimpleConsumerGroup() == that.isSimpleConsumerGroup() &&
|
||||||
Objects.equals(groupId, that.groupId) &&
|
Objects.equals(groupId, that.groupId) &&
|
||||||
Objects.equals(state, that.state) &&
|
Objects.equals(groupState, that.groupState) &&
|
||||||
Objects.equals(type, that.type);
|
Objects.equals(type, that.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
|
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
|
import org.apache.kafka.common.annotation.InterfaceStability;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -25,22 +27,26 @@ import java.util.Optional;
|
||||||
/**
|
/**
|
||||||
* A listing of a group in the cluster.
|
* A listing of a group in the cluster.
|
||||||
*/
|
*/
|
||||||
|
@InterfaceStability.Evolving
|
||||||
public class GroupListing {
|
public class GroupListing {
|
||||||
private final String groupId;
|
private final String groupId;
|
||||||
private final Optional<GroupType> type;
|
private final Optional<GroupType> type;
|
||||||
private final String protocol;
|
private final String protocol;
|
||||||
|
private final Optional<GroupState> groupState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance with the specified parameters.
|
* Create an instance with the specified parameters.
|
||||||
*
|
*
|
||||||
* @param groupId Group Id
|
* @param groupId Group Id
|
||||||
* @param type Group type
|
* @param type Group type
|
||||||
* @param protocol Protocol
|
* @param protocol Protocol
|
||||||
|
* @param groupState Group state
|
||||||
*/
|
*/
|
||||||
public GroupListing(String groupId, Optional<GroupType> type, String protocol) {
|
public GroupListing(String groupId, Optional<GroupType> type, String protocol, Optional<GroupState> groupState) {
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
this.type = Objects.requireNonNull(type);
|
this.type = Objects.requireNonNull(type);
|
||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
|
this.groupState = groupState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,6 +81,19 @@ public class GroupListing {
|
||||||
return protocol;
|
return protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The group state.
|
||||||
|
* <p>
|
||||||
|
* If the broker returns a group state which is not recognised, as might
|
||||||
|
* happen when talking to a broker with a later version, the state will be
|
||||||
|
* <code>Optional.of(GroupState.UNKNOWN)</code>.
|
||||||
|
*
|
||||||
|
* @return An Optional containing the state, if available.
|
||||||
|
*/
|
||||||
|
public Optional<GroupState> groupState() {
|
||||||
|
return groupState;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the group is a simple consumer group or not.
|
* If the group is a simple consumer group or not.
|
||||||
*/
|
*/
|
||||||
|
@ -88,12 +107,13 @@ public class GroupListing {
|
||||||
"groupId='" + groupId + '\'' +
|
"groupId='" + groupId + '\'' +
|
||||||
", type=" + type.map(GroupType::toString).orElse("none") +
|
", type=" + type.map(GroupType::toString).orElse("none") +
|
||||||
", protocol='" + protocol + '\'' +
|
", protocol='" + protocol + '\'' +
|
||||||
|
", groupState=" + groupState.map(GroupState::toString).orElse("none") +
|
||||||
')';
|
')';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(groupId, type, protocol);
|
return Objects.hash(groupId, type, protocol, groupState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -103,6 +123,7 @@ public class GroupListing {
|
||||||
GroupListing that = (GroupListing) o;
|
GroupListing that = (GroupListing) o;
|
||||||
return Objects.equals(groupId, that.groupId) &&
|
return Objects.equals(groupId, that.groupId) &&
|
||||||
Objects.equals(type, that.type) &&
|
Objects.equals(type, that.type) &&
|
||||||
Objects.equals(protocol, that.protocol);
|
Objects.equals(protocol, that.protocol) &&
|
||||||
|
Objects.equals(groupState, that.groupState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,8 @@ import org.apache.kafka.clients.admin.internals.RemoveMembersFromConsumerGroupHa
|
||||||
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
||||||
import org.apache.kafka.common.Cluster;
|
import org.apache.kafka.common.Cluster;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
|
||||||
import org.apache.kafka.common.ElectionType;
|
import org.apache.kafka.common.ElectionType;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.KafkaException;
|
import org.apache.kafka.common.KafkaException;
|
||||||
import org.apache.kafka.common.KafkaFuture;
|
import org.apache.kafka.common.KafkaFuture;
|
||||||
|
@ -72,7 +72,6 @@ import org.apache.kafka.common.Metric;
|
||||||
import org.apache.kafka.common.MetricName;
|
import org.apache.kafka.common.MetricName;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.PartitionInfo;
|
import org.apache.kafka.common.PartitionInfo;
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
|
||||||
import org.apache.kafka.common.TopicCollection;
|
import org.apache.kafka.common.TopicCollection;
|
||||||
import org.apache.kafka.common.TopicCollection.TopicIdCollection;
|
import org.apache.kafka.common.TopicCollection.TopicIdCollection;
|
||||||
import org.apache.kafka.common.TopicCollection.TopicNameCollection;
|
import org.apache.kafka.common.TopicCollection.TopicNameCollection;
|
||||||
|
@ -3596,8 +3595,13 @@ public class KafkaAdminClient extends AdminClient {
|
||||||
.stream()
|
.stream()
|
||||||
.map(GroupType::toString)
|
.map(GroupType::toString)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
List<String> groupStates = options.groupStates()
|
||||||
|
.stream()
|
||||||
|
.map(GroupState::toString)
|
||||||
|
.collect(Collectors.toList());
|
||||||
return new ListGroupsRequest.Builder(new ListGroupsRequestData()
|
return new ListGroupsRequest.Builder(new ListGroupsRequestData()
|
||||||
.setTypesFilter(groupTypes)
|
.setTypesFilter(groupTypes)
|
||||||
|
.setStatesFilter(groupStates)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3610,10 +3614,17 @@ public class KafkaAdminClient extends AdminClient {
|
||||||
type = Optional.of(GroupType.parse(group.groupType()));
|
type = Optional.of(GroupType.parse(group.groupType()));
|
||||||
}
|
}
|
||||||
final String protocolType = group.protocolType();
|
final String protocolType = group.protocolType();
|
||||||
|
final Optional<GroupState> groupState;
|
||||||
|
if (group.groupState() == null || group.groupState().isEmpty()) {
|
||||||
|
groupState = Optional.empty();
|
||||||
|
} else {
|
||||||
|
groupState = Optional.of(GroupState.parse(group.groupState()));
|
||||||
|
}
|
||||||
final GroupListing groupListing = new GroupListing(
|
final GroupListing groupListing = new GroupListing(
|
||||||
groupId,
|
groupId,
|
||||||
type,
|
type,
|
||||||
protocolType
|
protocolType,
|
||||||
|
groupState
|
||||||
);
|
);
|
||||||
results.addListing(groupListing);
|
results.addListing(groupListing);
|
||||||
}
|
}
|
||||||
|
@ -3738,9 +3749,9 @@ public class KafkaAdminClient extends AdminClient {
|
||||||
runnable.call(new Call("listConsumerGroups", deadline, new ConstantNodeIdProvider(node.id())) {
|
runnable.call(new Call("listConsumerGroups", deadline, new ConstantNodeIdProvider(node.id())) {
|
||||||
@Override
|
@Override
|
||||||
ListGroupsRequest.Builder createRequest(int timeoutMs) {
|
ListGroupsRequest.Builder createRequest(int timeoutMs) {
|
||||||
List<String> states = options.states()
|
List<String> states = options.groupStates()
|
||||||
.stream()
|
.stream()
|
||||||
.map(ConsumerGroupState::toString)
|
.map(GroupState::toString)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
List<String> groupTypes = options.types()
|
List<String> groupTypes = options.types()
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -3756,17 +3767,17 @@ public class KafkaAdminClient extends AdminClient {
|
||||||
String protocolType = group.protocolType();
|
String protocolType = group.protocolType();
|
||||||
if (protocolType.equals(ConsumerProtocol.PROTOCOL_TYPE) || protocolType.isEmpty()) {
|
if (protocolType.equals(ConsumerProtocol.PROTOCOL_TYPE) || protocolType.isEmpty()) {
|
||||||
final String groupId = group.groupId();
|
final String groupId = group.groupId();
|
||||||
final Optional<ConsumerGroupState> state = group.groupState().isEmpty()
|
final Optional<GroupState> groupState = group.groupState().isEmpty()
|
||||||
? Optional.empty()
|
? Optional.empty()
|
||||||
: Optional.of(ConsumerGroupState.parse(group.groupState()));
|
: Optional.of(GroupState.parse(group.groupState()));
|
||||||
final Optional<GroupType> type = group.groupType().isEmpty()
|
final Optional<GroupType> type = group.groupType().isEmpty()
|
||||||
? Optional.empty()
|
? Optional.empty()
|
||||||
: Optional.of(GroupType.parse(group.groupType()));
|
: Optional.of(GroupType.parse(group.groupType()));
|
||||||
final ConsumerGroupListing groupListing = new ConsumerGroupListing(
|
final ConsumerGroupListing groupListing = new ConsumerGroupListing(
|
||||||
groupId,
|
groupId,
|
||||||
protocolType.isEmpty(),
|
groupState,
|
||||||
state,
|
type,
|
||||||
type
|
protocolType.isEmpty()
|
||||||
);
|
);
|
||||||
results.addListing(groupListing);
|
results.addListing(groupListing);
|
||||||
}
|
}
|
||||||
|
@ -3927,7 +3938,7 @@ public class KafkaAdminClient extends AdminClient {
|
||||||
ListGroupsRequest.Builder createRequest(int timeoutMs) {
|
ListGroupsRequest.Builder createRequest(int timeoutMs) {
|
||||||
List<String> states = options.states()
|
List<String> states = options.states()
|
||||||
.stream()
|
.stream()
|
||||||
.map(ShareGroupState::toString)
|
.map(GroupState::toString)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
List<String> types = Collections.singletonList(GroupType.SHARE.toString());
|
List<String> types = Collections.singletonList(GroupType.SHARE.toString());
|
||||||
return new ListGroupsRequest.Builder(new ListGroupsRequestData()
|
return new ListGroupsRequest.Builder(new ListGroupsRequestData()
|
||||||
|
@ -3938,9 +3949,9 @@ public class KafkaAdminClient extends AdminClient {
|
||||||
|
|
||||||
private void maybeAddShareGroup(ListGroupsResponseData.ListedGroup group) {
|
private void maybeAddShareGroup(ListGroupsResponseData.ListedGroup group) {
|
||||||
final String groupId = group.groupId();
|
final String groupId = group.groupId();
|
||||||
final Optional<ShareGroupState> state = group.groupState().isEmpty()
|
final Optional<GroupState> state = group.groupState().isEmpty()
|
||||||
? Optional.empty()
|
? Optional.empty()
|
||||||
: Optional.of(ShareGroupState.parse(group.groupState()));
|
: Optional.of(GroupState.parse(group.groupState()));
|
||||||
final ShareGroupListing groupListing = new ShareGroupListing(groupId, state);
|
final ShareGroupListing groupListing = new ShareGroupListing(groupId, state);
|
||||||
results.addListing(groupListing);
|
results.addListing(groupListing);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,14 @@
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.ConsumerGroupState;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.annotation.InterfaceStability;
|
import org.apache.kafka.common.annotation.InterfaceStability;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for {@link Admin#listConsumerGroups()}.
|
* Options for {@link Admin#listConsumerGroups()}.
|
||||||
|
@ -33,17 +35,30 @@ import java.util.Set;
|
||||||
@InterfaceStability.Evolving
|
@InterfaceStability.Evolving
|
||||||
public class ListConsumerGroupsOptions extends AbstractOptions<ListConsumerGroupsOptions> {
|
public class ListConsumerGroupsOptions extends AbstractOptions<ListConsumerGroupsOptions> {
|
||||||
|
|
||||||
private Set<ConsumerGroupState> states = Collections.emptySet();
|
private Set<GroupState> groupStates = Collections.emptySet();
|
||||||
|
|
||||||
private Set<GroupType> types = Collections.emptySet();
|
private Set<GroupType> types = Collections.emptySet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If groupStates is set, only groups in these states will be returned by listGroups().
|
||||||
|
* Otherwise, all groups are returned.
|
||||||
|
* This operation is supported by brokers with version 2.6.0 or later.
|
||||||
|
*/
|
||||||
|
public ListConsumerGroupsOptions inGroupStates(Set<GroupState> groupStates) {
|
||||||
|
this.groupStates = (groupStates == null || groupStates.isEmpty()) ? Collections.emptySet() : Set.copyOf(groupStates);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If states is set, only groups in these states will be returned by listConsumerGroups().
|
* If states is set, only groups in these states will be returned by listConsumerGroups().
|
||||||
* Otherwise, all groups are returned.
|
* Otherwise, all groups are returned.
|
||||||
* This operation is supported by brokers with version 2.6.0 or later.
|
* This operation is supported by brokers with version 2.6.0 or later.
|
||||||
|
* @deprecated Since 4.0. Use {@link #inGroupStates(Set)}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public ListConsumerGroupsOptions inStates(Set<ConsumerGroupState> states) {
|
public ListConsumerGroupsOptions inStates(Set<ConsumerGroupState> states) {
|
||||||
this.states = (states == null || states.isEmpty()) ? Collections.emptySet() : new HashSet<>(states);
|
this.groupStates = (states == null || states.isEmpty())
|
||||||
|
? Collections.emptySet()
|
||||||
|
: states.stream().map(state -> GroupState.parse(state.name())).collect(Collectors.toSet());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +72,19 @@ public class ListConsumerGroupsOptions extends AbstractOptions<ListConsumerGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of States that are requested or empty if no states have been specified.
|
* Returns the list of group states that are requested or empty if no states have been specified.
|
||||||
*/
|
*/
|
||||||
|
public Set<GroupState> groupStates() {
|
||||||
|
return groupStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of States that are requested or empty if no states have been specified.
|
||||||
|
* @deprecated Since 4.0. Use {@link #inGroupStates(Set)}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public Set<ConsumerGroupState> states() {
|
public Set<ConsumerGroupState> states() {
|
||||||
return states;
|
return groupStates.stream().map(groupState -> ConsumerGroupState.parse(groupState.name())).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.annotation.InterfaceStability;
|
import org.apache.kafka.common.annotation.InterfaceStability;
|
||||||
|
|
||||||
|
@ -31,8 +32,19 @@ import java.util.Set;
|
||||||
@InterfaceStability.Evolving
|
@InterfaceStability.Evolving
|
||||||
public class ListGroupsOptions extends AbstractOptions<ListGroupsOptions> {
|
public class ListGroupsOptions extends AbstractOptions<ListGroupsOptions> {
|
||||||
|
|
||||||
|
private Set<GroupState> groupStates = Collections.emptySet();
|
||||||
private Set<GroupType> types = Collections.emptySet();
|
private Set<GroupType> types = Collections.emptySet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If groupStates is set, only groups in these states will be returned by listGroups().
|
||||||
|
* Otherwise, all groups are returned.
|
||||||
|
* This operation is supported by brokers with version 2.6.0 or later.
|
||||||
|
*/
|
||||||
|
public ListGroupsOptions inGroupStates(Set<GroupState> groupStates) {
|
||||||
|
this.groupStates = (groupStates == null || groupStates.isEmpty()) ? Collections.emptySet() : Set.copyOf(groupStates);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If types is set, only groups of these types will be returned by listGroups().
|
* If types is set, only groups of these types will be returned by listGroups().
|
||||||
* Otherwise, all groups are returned.
|
* Otherwise, all groups are returned.
|
||||||
|
@ -42,6 +54,13 @@ public class ListGroupsOptions extends AbstractOptions<ListGroupsOptions> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of group states that are requested or empty if no states have been specified.
|
||||||
|
*/
|
||||||
|
public Set<GroupState> groupStates() {
|
||||||
|
return groupStates;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of group types that are requested or empty if no types have been specified.
|
* Returns the list of group types that are requested or empty if no types have been specified.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.annotation.InterfaceStability;
|
import org.apache.kafka.common.annotation.InterfaceStability;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -32,12 +32,12 @@ import java.util.Set;
|
||||||
@InterfaceStability.Evolving
|
@InterfaceStability.Evolving
|
||||||
public class ListShareGroupsOptions extends AbstractOptions<ListShareGroupsOptions> {
|
public class ListShareGroupsOptions extends AbstractOptions<ListShareGroupsOptions> {
|
||||||
|
|
||||||
private Set<ShareGroupState> states = Collections.emptySet();
|
private Set<GroupState> states = Collections.emptySet();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If states is set, only groups in these states will be returned. Otherwise, all groups are returned.
|
* If states is set, only groups in these states will be returned. Otherwise, all groups are returned.
|
||||||
*/
|
*/
|
||||||
public ListShareGroupsOptions inStates(Set<ShareGroupState> states) {
|
public ListShareGroupsOptions inStates(Set<GroupState> states) {
|
||||||
this.states = (states == null) ? Collections.emptySet() : new HashSet<>(states);
|
this.states = (states == null) ? Collections.emptySet() : new HashSet<>(states);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ public class ListShareGroupsOptions extends AbstractOptions<ListShareGroupsOptio
|
||||||
/**
|
/**
|
||||||
* Return the list of States that are requested or empty if no states have been specified.
|
* Return the list of States that are requested or empty if no states have been specified.
|
||||||
*/
|
*/
|
||||||
public Set<ShareGroupState> states() {
|
public Set<GroupState> states() {
|
||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,8 +17,8 @@
|
||||||
|
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
|
||||||
import org.apache.kafka.common.acl.AclOperation;
|
import org.apache.kafka.common.acl.AclOperation;
|
||||||
import org.apache.kafka.common.annotation.InterfaceStability;
|
import org.apache.kafka.common.annotation.InterfaceStability;
|
||||||
|
|
||||||
|
@ -36,26 +36,26 @@ import java.util.stream.Collectors;
|
||||||
public class ShareGroupDescription {
|
public class ShareGroupDescription {
|
||||||
private final String groupId;
|
private final String groupId;
|
||||||
private final Collection<MemberDescription> members;
|
private final Collection<MemberDescription> members;
|
||||||
private final ShareGroupState state;
|
private final GroupState groupState;
|
||||||
private final Node coordinator;
|
private final Node coordinator;
|
||||||
private final Set<AclOperation> authorizedOperations;
|
private final Set<AclOperation> authorizedOperations;
|
||||||
|
|
||||||
public ShareGroupDescription(String groupId,
|
public ShareGroupDescription(String groupId,
|
||||||
Collection<MemberDescription> members,
|
Collection<MemberDescription> members,
|
||||||
ShareGroupState state,
|
GroupState groupState,
|
||||||
Node coordinator) {
|
Node coordinator) {
|
||||||
this(groupId, members, state, coordinator, Collections.emptySet());
|
this(groupId, members, groupState, coordinator, Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShareGroupDescription(String groupId,
|
public ShareGroupDescription(String groupId,
|
||||||
Collection<MemberDescription> members,
|
Collection<MemberDescription> members,
|
||||||
ShareGroupState state,
|
GroupState groupState,
|
||||||
Node coordinator,
|
Node coordinator,
|
||||||
Set<AclOperation> authorizedOperations) {
|
Set<AclOperation> authorizedOperations) {
|
||||||
this.groupId = groupId == null ? "" : groupId;
|
this.groupId = groupId == null ? "" : groupId;
|
||||||
this.members = members == null ? Collections.emptyList() :
|
this.members = members == null ? Collections.emptyList() :
|
||||||
Collections.unmodifiableList(new ArrayList<>(members));
|
Collections.unmodifiableList(new ArrayList<>(members));
|
||||||
this.state = state;
|
this.groupState = groupState;
|
||||||
this.coordinator = coordinator;
|
this.coordinator = coordinator;
|
||||||
this.authorizedOperations = authorizedOperations;
|
this.authorizedOperations = authorizedOperations;
|
||||||
}
|
}
|
||||||
|
@ -67,14 +67,14 @@ public class ShareGroupDescription {
|
||||||
final ShareGroupDescription that = (ShareGroupDescription) o;
|
final ShareGroupDescription that = (ShareGroupDescription) o;
|
||||||
return Objects.equals(groupId, that.groupId) &&
|
return Objects.equals(groupId, that.groupId) &&
|
||||||
Objects.equals(members, that.members) &&
|
Objects.equals(members, that.members) &&
|
||||||
state == that.state &&
|
groupState == that.groupState &&
|
||||||
Objects.equals(coordinator, that.coordinator) &&
|
Objects.equals(coordinator, that.coordinator) &&
|
||||||
Objects.equals(authorizedOperations, that.authorizedOperations);
|
Objects.equals(authorizedOperations, that.authorizedOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(groupId, members, state, coordinator, authorizedOperations);
|
return Objects.hash(groupId, members, groupState, coordinator, authorizedOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,10 +92,10 @@ public class ShareGroupDescription {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The share group state, or UNKNOWN if the state is too new for us to parse.
|
* The group state, or UNKNOWN if the state is too new for us to parse.
|
||||||
*/
|
*/
|
||||||
public ShareGroupState state() {
|
public GroupState groupState() {
|
||||||
return state;
|
return groupState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,7 +116,7 @@ public class ShareGroupDescription {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "(groupId=" + groupId +
|
return "(groupId=" + groupId +
|
||||||
", members=" + members.stream().map(MemberDescription::toString).collect(Collectors.joining(",")) +
|
", members=" + members.stream().map(MemberDescription::toString).collect(Collectors.joining(",")) +
|
||||||
", state=" + state +
|
", groupState=" + groupState +
|
||||||
", coordinator=" + coordinator +
|
", coordinator=" + coordinator +
|
||||||
", authorizedOperations=" + authorizedOperations +
|
", authorizedOperations=" + authorizedOperations +
|
||||||
")";
|
")";
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.annotation.InterfaceStability;
|
import org.apache.kafka.common.annotation.InterfaceStability;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -31,7 +31,7 @@ import java.util.Optional;
|
||||||
@InterfaceStability.Evolving
|
@InterfaceStability.Evolving
|
||||||
public class ShareGroupListing {
|
public class ShareGroupListing {
|
||||||
private final String groupId;
|
private final String groupId;
|
||||||
private final Optional<ShareGroupState> state;
|
private final Optional<GroupState> groupState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance with the specified parameters.
|
* Create an instance with the specified parameters.
|
||||||
|
@ -46,11 +46,11 @@ public class ShareGroupListing {
|
||||||
* Create an instance with the specified parameters.
|
* Create an instance with the specified parameters.
|
||||||
*
|
*
|
||||||
* @param groupId Group Id
|
* @param groupId Group Id
|
||||||
* @param state The state of the share group
|
* @param groupState The state of the share group
|
||||||
*/
|
*/
|
||||||
public ShareGroupListing(String groupId, Optional<ShareGroupState> state) {
|
public ShareGroupListing(String groupId, Optional<GroupState> groupState) {
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
this.state = Objects.requireNonNull(state);
|
this.groupState = Objects.requireNonNull(groupState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,21 +63,21 @@ public class ShareGroupListing {
|
||||||
/**
|
/**
|
||||||
* The share group state.
|
* The share group state.
|
||||||
*/
|
*/
|
||||||
public Optional<ShareGroupState> state() {
|
public Optional<GroupState> groupState() {
|
||||||
return state;
|
return groupState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "(" +
|
return "(" +
|
||||||
"groupId='" + groupId + '\'' +
|
"groupId='" + groupId + '\'' +
|
||||||
", state=" + state +
|
", groupState=" + groupState +
|
||||||
')';
|
')';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(groupId, state);
|
return Objects.hash(groupId, groupState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -86,6 +86,6 @@ public class ShareGroupListing {
|
||||||
if (!(o instanceof ShareGroupListing)) return false;
|
if (!(o instanceof ShareGroupListing)) return false;
|
||||||
ShareGroupListing that = (ShareGroupListing) o;
|
ShareGroupListing that = (ShareGroupListing) o;
|
||||||
return Objects.equals(groupId, that.groupId) &&
|
return Objects.equals(groupId, that.groupId) &&
|
||||||
Objects.equals(state, that.state);
|
Objects.equals(groupState, that.groupState);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@ import org.apache.kafka.clients.admin.MemberAssignment;
|
||||||
import org.apache.kafka.clients.admin.MemberDescription;
|
import org.apache.kafka.clients.admin.MemberDescription;
|
||||||
import org.apache.kafka.clients.consumer.ConsumerPartitionAssignor.Assignment;
|
import org.apache.kafka.clients.consumer.ConsumerPartitionAssignor.Assignment;
|
||||||
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
|
@ -231,7 +231,7 @@ public class DescribeConsumerGroupsHandler implements AdminApiHandler<Coordinato
|
||||||
memberDescriptions,
|
memberDescriptions,
|
||||||
describedGroup.assignorName(),
|
describedGroup.assignorName(),
|
||||||
GroupType.CONSUMER,
|
GroupType.CONSUMER,
|
||||||
ConsumerGroupState.parse(describedGroup.groupState()),
|
GroupState.parse(describedGroup.groupState()),
|
||||||
coordinator,
|
coordinator,
|
||||||
authorizedOperations
|
authorizedOperations
|
||||||
);
|
);
|
||||||
|
@ -286,7 +286,7 @@ public class DescribeConsumerGroupsHandler implements AdminApiHandler<Coordinato
|
||||||
memberDescriptions,
|
memberDescriptions,
|
||||||
describedGroup.protocolData(),
|
describedGroup.protocolData(),
|
||||||
GroupType.CLASSIC,
|
GroupType.CLASSIC,
|
||||||
ConsumerGroupState.parse(describedGroup.groupState()),
|
GroupState.parse(describedGroup.groupState()),
|
||||||
coordinator,
|
coordinator,
|
||||||
authorizedOperations);
|
authorizedOperations);
|
||||||
completed.put(groupIdKey, consumerGroupDescription);
|
completed.put(groupIdKey, consumerGroupDescription);
|
||||||
|
|
|
@ -19,8 +19,8 @@ package org.apache.kafka.clients.admin.internals;
|
||||||
import org.apache.kafka.clients.admin.MemberAssignment;
|
import org.apache.kafka.clients.admin.MemberAssignment;
|
||||||
import org.apache.kafka.clients.admin.MemberDescription;
|
import org.apache.kafka.clients.admin.MemberDescription;
|
||||||
import org.apache.kafka.clients.admin.ShareGroupDescription;
|
import org.apache.kafka.clients.admin.ShareGroupDescription;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.acl.AclOperation;
|
import org.apache.kafka.common.acl.AclOperation;
|
||||||
import org.apache.kafka.common.message.ShareGroupDescribeRequestData;
|
import org.apache.kafka.common.message.ShareGroupDescribeRequestData;
|
||||||
|
@ -129,7 +129,7 @@ public class DescribeShareGroupsHandler extends AdminApiHandler.Batched<Coordina
|
||||||
final ShareGroupDescription shareGroupDescription =
|
final ShareGroupDescription shareGroupDescription =
|
||||||
new ShareGroupDescription(groupIdKey.idValue,
|
new ShareGroupDescription(groupIdKey.idValue,
|
||||||
memberDescriptions,
|
memberDescriptions,
|
||||||
ShareGroupState.parse(describedGroup.groupState()),
|
GroupState.parse(describedGroup.groupState()),
|
||||||
coordinator,
|
coordinator,
|
||||||
authorizedOperations);
|
authorizedOperations);
|
||||||
completed.put(groupIdKey, shareGroupDescription);
|
completed.put(groupIdKey, shareGroupDescription);
|
||||||
|
@ -184,7 +184,7 @@ public class DescribeShareGroupsHandler extends AdminApiHandler.Batched<Coordina
|
||||||
final ShareGroupDescription shareGroupDescription =
|
final ShareGroupDescription shareGroupDescription =
|
||||||
new ShareGroupDescription(groupId.idValue,
|
new ShareGroupDescription(groupId.idValue,
|
||||||
Collections.emptySet(),
|
Collections.emptySet(),
|
||||||
ShareGroupState.DEAD,
|
GroupState.DEAD,
|
||||||
coordinator,
|
coordinator,
|
||||||
validAclOperations(describedGroup.authorizedOperations()));
|
validAclOperations(describedGroup.authorizedOperations()));
|
||||||
completed.put(groupId, shareGroupDescription);
|
completed.put(groupId, shareGroupDescription);
|
||||||
|
|
|
@ -25,7 +25,9 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The consumer group state.
|
* The consumer group state.
|
||||||
|
* @deprecated Since 4.0. Use {@link GroupState} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public enum ConsumerGroupState {
|
public enum ConsumerGroupState {
|
||||||
UNKNOWN("Unknown"),
|
UNKNOWN("Unknown"),
|
||||||
PREPARING_REBALANCE("PreparingRebalance"),
|
PREPARING_REBALANCE("PreparingRebalance"),
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
import org.apache.kafka.common.annotation.InterfaceStability;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The group state.
|
||||||
|
* <p>
|
||||||
|
* The following table shows the correspondence between the group states and types.
|
||||||
|
* <table>
|
||||||
|
* <thead>
|
||||||
|
* <tr><th>State</th><th>Classic group</th><th>Consumer group</th><th>Share group</th></tr>
|
||||||
|
* </thead>
|
||||||
|
* <tbody>
|
||||||
|
* <tr><td>UNKNOWN</td><td>Yes</td><td>Yes</td><td>Yes</td></tr>
|
||||||
|
* <tr><td>PREPARING_REBALANCE</td><td>Yes</td><td>Yes</td><td></td></tr>
|
||||||
|
* <tr><td>COMPLETING_REBALANCE</td><td>Yes</td><td>Yes</td><td></td></tr>
|
||||||
|
* <tr><td>STABLE</td><td>Yes</td><td>Yes</td><td>Yes</td></tr>
|
||||||
|
* <tr><td>DEAD</td><td>Yes</td><td>Yes</td><td>Yes</td></tr>
|
||||||
|
* <tr><td>EMPTY</td><td>Yes</td><td>Yes</td><td>Yes</td></tr>
|
||||||
|
* <tr><td>ASSIGNING</td><td></td><td>Yes</td><td></td></tr>
|
||||||
|
* <tr><td>RECONCILING</td><td></td><td>Yes</td><td></td></tr>
|
||||||
|
* </tbody>
|
||||||
|
* </table>
|
||||||
|
*/
|
||||||
|
@InterfaceStability.Evolving
|
||||||
|
public enum GroupState {
|
||||||
|
UNKNOWN("Unknown"),
|
||||||
|
PREPARING_REBALANCE("PreparingRebalance"),
|
||||||
|
COMPLETING_REBALANCE("CompletingRebalance"),
|
||||||
|
STABLE("Stable"),
|
||||||
|
DEAD("Dead"),
|
||||||
|
EMPTY("Empty"),
|
||||||
|
ASSIGNING("Assigning"),
|
||||||
|
RECONCILING("Reconciling");
|
||||||
|
|
||||||
|
private static final Map<String, GroupState> NAME_TO_ENUM = Arrays.stream(values())
|
||||||
|
.collect(Collectors.toMap(state -> state.name.toUpperCase(Locale.ROOT), Function.identity()));
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
GroupState(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Case-insensitive group state lookup by string name.
|
||||||
|
*/
|
||||||
|
public static GroupState parse(String name) {
|
||||||
|
GroupState state = NAME_TO_ENUM.get(name.toUpperCase(Locale.ROOT));
|
||||||
|
return state == null ? UNKNOWN : state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<GroupState> groupStatesForType(GroupType type) {
|
||||||
|
if (type == GroupType.CLASSIC) {
|
||||||
|
return Set.of(PREPARING_REBALANCE, COMPLETING_REBALANCE, STABLE, DEAD, EMPTY);
|
||||||
|
} else if (type == GroupType.CONSUMER) {
|
||||||
|
return Set.of(PREPARING_REBALANCE, COMPLETING_REBALANCE, STABLE, DEAD, EMPTY, ASSIGNING, RECONCILING);
|
||||||
|
} else if (type == GroupType.SHARE) {
|
||||||
|
return Set.of(STABLE, DEAD, EMPTY);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Group type not known");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.common;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The share group state.
|
|
||||||
*/
|
|
||||||
public enum ShareGroupState {
|
|
||||||
UNKNOWN("Unknown"),
|
|
||||||
STABLE("Stable"),
|
|
||||||
DEAD("Dead"),
|
|
||||||
EMPTY("Empty");
|
|
||||||
|
|
||||||
private static final Map<String, ShareGroupState> NAME_TO_ENUM = Arrays.stream(values())
|
|
||||||
.collect(Collectors.toMap(state -> state.name.toUpperCase(Locale.ROOT), Function.identity()));
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
ShareGroupState(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Case-insensitive share group state lookup by string name.
|
|
||||||
*/
|
|
||||||
public static ShareGroupState parse(String name) {
|
|
||||||
ShareGroupState state = NAME_TO_ENUM.get(name.toUpperCase(Locale.ROOT));
|
|
||||||
return state == null ? UNKNOWN : state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.apache.kafka.clients.admin;
|
package org.apache.kafka.clients.admin;
|
||||||
|
|
||||||
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -33,16 +34,16 @@ public class GroupListingTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleConsumerGroup() {
|
public void testSimpleConsumerGroup() {
|
||||||
GroupListing gl = new GroupListing(GROUP_ID, Optional.of(GroupType.CLASSIC), "");
|
GroupListing gl = new GroupListing(GROUP_ID, Optional.of(GroupType.CLASSIC), "", Optional.of(GroupState.EMPTY));
|
||||||
assertTrue(gl.isSimpleConsumerGroup());
|
assertTrue(gl.isSimpleConsumerGroup());
|
||||||
|
|
||||||
gl = new GroupListing(GROUP_ID, Optional.of(GroupType.CLASSIC), ConsumerProtocol.PROTOCOL_TYPE);
|
gl = new GroupListing(GROUP_ID, Optional.of(GroupType.CLASSIC), ConsumerProtocol.PROTOCOL_TYPE, Optional.of(GroupState.STABLE));
|
||||||
assertFalse(gl.isSimpleConsumerGroup());
|
assertFalse(gl.isSimpleConsumerGroup());
|
||||||
|
|
||||||
gl = new GroupListing(GROUP_ID, Optional.of(GroupType.CONSUMER), "");
|
gl = new GroupListing(GROUP_ID, Optional.of(GroupType.CONSUMER), "", Optional.of(GroupState.EMPTY));
|
||||||
assertFalse(gl.isSimpleConsumerGroup());
|
assertFalse(gl.isSimpleConsumerGroup());
|
||||||
|
|
||||||
gl = new GroupListing(GROUP_ID, Optional.empty(), "");
|
gl = new GroupListing(GROUP_ID, Optional.empty(), "", Optional.empty());
|
||||||
assertFalse(gl.isSimpleConsumerGroup());
|
assertFalse(gl.isSimpleConsumerGroup());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,13 @@ import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
||||||
import org.apache.kafka.common.ClassicGroupState;
|
import org.apache.kafka.common.ClassicGroupState;
|
||||||
import org.apache.kafka.common.Cluster;
|
import org.apache.kafka.common.Cluster;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
|
||||||
import org.apache.kafka.common.ElectionType;
|
import org.apache.kafka.common.ElectionType;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.KafkaException;
|
import org.apache.kafka.common.KafkaException;
|
||||||
import org.apache.kafka.common.KafkaFuture;
|
import org.apache.kafka.common.KafkaFuture;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.PartitionInfo;
|
import org.apache.kafka.common.PartitionInfo;
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
|
||||||
import org.apache.kafka.common.TopicCollection;
|
import org.apache.kafka.common.TopicCollection;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.TopicPartitionReplica;
|
import org.apache.kafka.common.TopicPartitionReplica;
|
||||||
|
@ -3134,8 +3133,8 @@ public class KafkaAdminClientTest {
|
||||||
|
|
||||||
assertEquals(2, listings.size());
|
assertEquals(2, listings.size());
|
||||||
List<GroupListing> expected = new ArrayList<>();
|
List<GroupListing> expected = new ArrayList<>();
|
||||||
expected.add(new GroupListing("group-2", Optional.of(GroupType.CLASSIC), ""));
|
expected.add(new GroupListing("group-2", Optional.of(GroupType.CLASSIC), "", Optional.of(GroupState.EMPTY)));
|
||||||
expected.add(new GroupListing("group-1", Optional.of(GroupType.CONSUMER), ConsumerProtocol.PROTOCOL_TYPE));
|
expected.add(new GroupListing("group-1", Optional.of(GroupType.CONSUMER), ConsumerProtocol.PROTOCOL_TYPE, Optional.of(GroupState.STABLE)));
|
||||||
assertEquals(expected, listings);
|
assertEquals(expected, listings);
|
||||||
assertEquals(0, result.errors().get().size());
|
assertEquals(0, result.errors().get().size());
|
||||||
}
|
}
|
||||||
|
@ -3163,7 +3162,7 @@ public class KafkaAdminClientTest {
|
||||||
|
|
||||||
assertEquals(1, listings.size());
|
assertEquals(1, listings.size());
|
||||||
List<GroupListing> expected = new ArrayList<>();
|
List<GroupListing> expected = new ArrayList<>();
|
||||||
expected.add(new GroupListing("group-1", Optional.empty(), "any"));
|
expected.add(new GroupListing("group-1", Optional.empty(), "any", Optional.empty()));
|
||||||
assertEquals(expected, listings);
|
assertEquals(expected, listings);
|
||||||
assertEquals(0, result.errors().get().size());
|
assertEquals(0, result.errors().get().size());
|
||||||
}
|
}
|
||||||
|
@ -3199,8 +3198,8 @@ public class KafkaAdminClientTest {
|
||||||
|
|
||||||
assertEquals(2, listing.size());
|
assertEquals(2, listing.size());
|
||||||
List<GroupListing> expected = new ArrayList<>();
|
List<GroupListing> expected = new ArrayList<>();
|
||||||
expected.add(new GroupListing("group-2", Optional.of(GroupType.CONSUMER), ""));
|
expected.add(new GroupListing("group-2", Optional.of(GroupType.CONSUMER), "", Optional.of(GroupState.EMPTY)));
|
||||||
expected.add(new GroupListing("group-1", Optional.of(GroupType.CONSUMER), ConsumerProtocol.PROTOCOL_TYPE));
|
expected.add(new GroupListing("group-1", Optional.of(GroupType.CONSUMER), ConsumerProtocol.PROTOCOL_TYPE, Optional.of(GroupState.STABLE)));
|
||||||
assertEquals(expected, listing);
|
assertEquals(expected, listing);
|
||||||
assertEquals(0, result.errors().get().size());
|
assertEquals(0, result.errors().get().size());
|
||||||
}
|
}
|
||||||
|
@ -3387,8 +3386,8 @@ public class KafkaAdminClientTest {
|
||||||
|
|
||||||
assertEquals(2, listings.size());
|
assertEquals(2, listings.size());
|
||||||
List<ConsumerGroupListing> expected = new ArrayList<>();
|
List<ConsumerGroupListing> expected = new ArrayList<>();
|
||||||
expected.add(new ConsumerGroupListing("group-2", true, Optional.of(ConsumerGroupState.EMPTY)));
|
expected.add(new ConsumerGroupListing("group-2", Optional.of(GroupState.EMPTY), true));
|
||||||
expected.add(new ConsumerGroupListing("group-1", false, Optional.of(ConsumerGroupState.STABLE)));
|
expected.add(new ConsumerGroupListing("group-1", Optional.of(GroupState.STABLE), false));
|
||||||
assertEquals(expected, listings);
|
assertEquals(expected, listings);
|
||||||
assertEquals(0, result.errors().get().size());
|
assertEquals(0, result.errors().get().size());
|
||||||
}
|
}
|
||||||
|
@ -3403,7 +3402,7 @@ public class KafkaAdminClientTest {
|
||||||
env.kafkaClient().prepareResponse(prepareMetadataResponse(env.cluster(), Errors.NONE));
|
env.kafkaClient().prepareResponse(prepareMetadataResponse(env.cluster(), Errors.NONE));
|
||||||
|
|
||||||
env.kafkaClient().prepareResponseFrom(
|
env.kafkaClient().prepareResponseFrom(
|
||||||
expectListGroupsRequestWithFilters(singleton(ConsumerGroupState.STABLE.toString()), Collections.emptySet()),
|
expectListGroupsRequestWithFilters(singleton(GroupState.STABLE.toString()), Collections.emptySet()),
|
||||||
new ListGroupsResponse(new ListGroupsResponseData()
|
new ListGroupsResponse(new ListGroupsResponseData()
|
||||||
.setErrorCode(Errors.NONE.code())
|
.setErrorCode(Errors.NONE.code())
|
||||||
.setGroups(singletonList(
|
.setGroups(singletonList(
|
||||||
|
@ -3414,13 +3413,13 @@ public class KafkaAdminClientTest {
|
||||||
.setGroupType(GroupType.CLASSIC.toString())))),
|
.setGroupType(GroupType.CLASSIC.toString())))),
|
||||||
env.cluster().nodeById(0));
|
env.cluster().nodeById(0));
|
||||||
|
|
||||||
final ListConsumerGroupsOptions options = new ListConsumerGroupsOptions().inStates(singleton(ConsumerGroupState.STABLE));
|
final ListConsumerGroupsOptions options = new ListConsumerGroupsOptions().inGroupStates(singleton(GroupState.STABLE));
|
||||||
final ListConsumerGroupsResult result = env.adminClient().listConsumerGroups(options);
|
final ListConsumerGroupsResult result = env.adminClient().listConsumerGroups(options);
|
||||||
Collection<ConsumerGroupListing> listings = result.valid().get();
|
Collection<ConsumerGroupListing> listings = result.valid().get();
|
||||||
|
|
||||||
assertEquals(1, listings.size());
|
assertEquals(1, listings.size());
|
||||||
List<ConsumerGroupListing> expected = new ArrayList<>();
|
List<ConsumerGroupListing> expected = new ArrayList<>();
|
||||||
expected.add(new ConsumerGroupListing("group-1", false, Optional.of(ConsumerGroupState.STABLE), Optional.of(GroupType.CLASSIC)));
|
expected.add(new ConsumerGroupListing("group-1", Optional.of(GroupState.STABLE), Optional.of(GroupType.CLASSIC), false));
|
||||||
assertEquals(expected, listings);
|
assertEquals(expected, listings);
|
||||||
assertEquals(0, result.errors().get().size());
|
assertEquals(0, result.errors().get().size());
|
||||||
|
|
||||||
|
@ -3449,8 +3448,8 @@ public class KafkaAdminClientTest {
|
||||||
|
|
||||||
assertEquals(2, listings2.size());
|
assertEquals(2, listings2.size());
|
||||||
List<ConsumerGroupListing> expected2 = new ArrayList<>();
|
List<ConsumerGroupListing> expected2 = new ArrayList<>();
|
||||||
expected2.add(new ConsumerGroupListing("group-2", true, Optional.of(ConsumerGroupState.EMPTY), Optional.of(GroupType.CONSUMER)));
|
expected2.add(new ConsumerGroupListing("group-2", Optional.of(GroupState.EMPTY), Optional.of(GroupType.CONSUMER), true));
|
||||||
expected2.add(new ConsumerGroupListing("group-1", false, Optional.of(ConsumerGroupState.STABLE), Optional.of(GroupType.CONSUMER)));
|
expected2.add(new ConsumerGroupListing("group-1", Optional.of(GroupState.STABLE), Optional.of(GroupType.CONSUMER), false));
|
||||||
assertEquals(expected2, listings2);
|
assertEquals(expected2, listings2);
|
||||||
assertEquals(0, result.errors().get().size());
|
assertEquals(0, result.errors().get().size());
|
||||||
}
|
}
|
||||||
|
@ -3488,7 +3487,7 @@ public class KafkaAdminClientTest {
|
||||||
env.kafkaClient().prepareUnsupportedVersionResponse(
|
env.kafkaClient().prepareUnsupportedVersionResponse(
|
||||||
body -> body instanceof ListGroupsRequest);
|
body -> body instanceof ListGroupsRequest);
|
||||||
|
|
||||||
options = new ListConsumerGroupsOptions().inStates(singleton(ConsumerGroupState.STABLE));
|
options = new ListConsumerGroupsOptions().inGroupStates(singleton(GroupState.STABLE));
|
||||||
result = env.adminClient().listConsumerGroups(options);
|
result = env.adminClient().listConsumerGroups(options);
|
||||||
TestUtils.assertFutureThrows(result.all(), UnsupportedVersionException.class);
|
TestUtils.assertFutureThrows(result.all(), UnsupportedVersionException.class);
|
||||||
}
|
}
|
||||||
|
@ -3507,23 +3506,23 @@ public class KafkaAdminClientTest {
|
||||||
|
|
||||||
// Check if we can list groups with older broker if we specify states and don't specify types.
|
// Check if we can list groups with older broker if we specify states and don't specify types.
|
||||||
env.kafkaClient().prepareResponseFrom(
|
env.kafkaClient().prepareResponseFrom(
|
||||||
expectListGroupsRequestWithFilters(singleton(ConsumerGroupState.STABLE.toString()), Collections.emptySet()),
|
expectListGroupsRequestWithFilters(singleton(GroupState.STABLE.toString()), Collections.emptySet()),
|
||||||
new ListGroupsResponse(new ListGroupsResponseData()
|
new ListGroupsResponse(new ListGroupsResponseData()
|
||||||
.setErrorCode(Errors.NONE.code())
|
.setErrorCode(Errors.NONE.code())
|
||||||
.setGroups(Collections.singletonList(
|
.setGroups(Collections.singletonList(
|
||||||
new ListGroupsResponseData.ListedGroup()
|
new ListGroupsResponseData.ListedGroup()
|
||||||
.setGroupId("group-1")
|
.setGroupId("group-1")
|
||||||
.setProtocolType(ConsumerProtocol.PROTOCOL_TYPE)
|
.setProtocolType(ConsumerProtocol.PROTOCOL_TYPE)
|
||||||
.setGroupState(ConsumerGroupState.STABLE.toString())))),
|
.setGroupState(GroupState.STABLE.toString())))),
|
||||||
env.cluster().nodeById(0));
|
env.cluster().nodeById(0));
|
||||||
|
|
||||||
ListConsumerGroupsOptions options = new ListConsumerGroupsOptions().inStates(singleton(ConsumerGroupState.STABLE));
|
ListConsumerGroupsOptions options = new ListConsumerGroupsOptions().inGroupStates(singleton(GroupState.STABLE));
|
||||||
ListConsumerGroupsResult result = env.adminClient().listConsumerGroups(options);
|
ListConsumerGroupsResult result = env.adminClient().listConsumerGroups(options);
|
||||||
|
|
||||||
Collection<ConsumerGroupListing> listing = result.all().get();
|
Collection<ConsumerGroupListing> listing = result.all().get();
|
||||||
assertEquals(1, listing.size());
|
assertEquals(1, listing.size());
|
||||||
List<ConsumerGroupListing> expected = Collections.singletonList(
|
List<ConsumerGroupListing> expected = Collections.singletonList(
|
||||||
new ConsumerGroupListing("group-1", false, Optional.of(ConsumerGroupState.STABLE))
|
new ConsumerGroupListing("group-1", Optional.of(GroupState.STABLE), false)
|
||||||
);
|
);
|
||||||
assertEquals(expected, listing);
|
assertEquals(expected, listing);
|
||||||
|
|
||||||
|
@ -4109,7 +4108,7 @@ public class KafkaAdminClientTest {
|
||||||
),
|
),
|
||||||
"range",
|
"range",
|
||||||
GroupType.CONSUMER,
|
GroupType.CONSUMER,
|
||||||
ConsumerGroupState.STABLE,
|
GroupState.STABLE,
|
||||||
env.cluster().controller(),
|
env.cluster().controller(),
|
||||||
Collections.emptySet()
|
Collections.emptySet()
|
||||||
));
|
));
|
||||||
|
@ -4129,7 +4128,7 @@ public class KafkaAdminClientTest {
|
||||||
),
|
),
|
||||||
"range",
|
"range",
|
||||||
GroupType.CLASSIC,
|
GroupType.CLASSIC,
|
||||||
ConsumerGroupState.STABLE,
|
GroupState.STABLE,
|
||||||
env.cluster().controller(),
|
env.cluster().controller(),
|
||||||
Collections.emptySet()
|
Collections.emptySet()
|
||||||
));
|
));
|
||||||
|
@ -5029,7 +5028,7 @@ public class KafkaAdminClientTest {
|
||||||
ShareGroupDescribeResponseData group0Data = new ShareGroupDescribeResponseData();
|
ShareGroupDescribeResponseData group0Data = new ShareGroupDescribeResponseData();
|
||||||
group0Data.groups().add(new ShareGroupDescribeResponseData.DescribedGroup()
|
group0Data.groups().add(new ShareGroupDescribeResponseData.DescribedGroup()
|
||||||
.setGroupId(GROUP_ID)
|
.setGroupId(GROUP_ID)
|
||||||
.setGroupState(ShareGroupState.STABLE.toString())
|
.setGroupState(GroupState.STABLE.toString())
|
||||||
.setMembers(asList(memberOne, memberTwo)));
|
.setMembers(asList(memberOne, memberTwo)));
|
||||||
|
|
||||||
final List<TopicPartition> expectedTopicPartitions = new ArrayList<>();
|
final List<TopicPartition> expectedTopicPartitions = new ArrayList<>();
|
||||||
|
@ -5044,7 +5043,7 @@ public class KafkaAdminClientTest {
|
||||||
new MemberAssignment(new HashSet<>(expectedTopicPartitions))));
|
new MemberAssignment(new HashSet<>(expectedTopicPartitions))));
|
||||||
data.groups().add(new ShareGroupDescribeResponseData.DescribedGroup()
|
data.groups().add(new ShareGroupDescribeResponseData.DescribedGroup()
|
||||||
.setGroupId(GROUP_ID)
|
.setGroupId(GROUP_ID)
|
||||||
.setGroupState(ShareGroupState.STABLE.toString())
|
.setGroupState(GroupState.STABLE.toString())
|
||||||
.setMembers(asList(memberOne, memberTwo)));
|
.setMembers(asList(memberOne, memberTwo)));
|
||||||
|
|
||||||
env.kafkaClient().prepareResponse(new ShareGroupDescribeResponse(data));
|
env.kafkaClient().prepareResponse(new ShareGroupDescribeResponse(data));
|
||||||
|
@ -5097,7 +5096,7 @@ public class KafkaAdminClientTest {
|
||||||
ShareGroupDescribeResponseData group0Data = new ShareGroupDescribeResponseData();
|
ShareGroupDescribeResponseData group0Data = new ShareGroupDescribeResponseData();
|
||||||
group0Data.groups().add(new ShareGroupDescribeResponseData.DescribedGroup()
|
group0Data.groups().add(new ShareGroupDescribeResponseData.DescribedGroup()
|
||||||
.setGroupId(GROUP_ID)
|
.setGroupId(GROUP_ID)
|
||||||
.setGroupState(ShareGroupState.STABLE.toString())
|
.setGroupState(GroupState.STABLE.toString())
|
||||||
.setMembers(asList(
|
.setMembers(asList(
|
||||||
new ShareGroupDescribeResponseData.Member()
|
new ShareGroupDescribeResponseData.Member()
|
||||||
.setMemberId("0")
|
.setMemberId("0")
|
||||||
|
@ -5113,7 +5112,7 @@ public class KafkaAdminClientTest {
|
||||||
ShareGroupDescribeResponseData group1Data = new ShareGroupDescribeResponseData();
|
ShareGroupDescribeResponseData group1Data = new ShareGroupDescribeResponseData();
|
||||||
group1Data.groups().add(new ShareGroupDescribeResponseData.DescribedGroup()
|
group1Data.groups().add(new ShareGroupDescribeResponseData.DescribedGroup()
|
||||||
.setGroupId("group-1")
|
.setGroupId("group-1")
|
||||||
.setGroupState(ShareGroupState.STABLE.toString())
|
.setGroupState(GroupState.STABLE.toString())
|
||||||
.setMembers(asList(
|
.setMembers(asList(
|
||||||
new ShareGroupDescribeResponseData.Member()
|
new ShareGroupDescribeResponseData.Member()
|
||||||
.setMemberId("0")
|
.setMemberId("0")
|
||||||
|
@ -5231,7 +5230,7 @@ public class KafkaAdminClientTest {
|
||||||
Set<String> groupIds = new HashSet<>();
|
Set<String> groupIds = new HashSet<>();
|
||||||
for (ShareGroupListing listing : listings) {
|
for (ShareGroupListing listing : listings) {
|
||||||
groupIds.add(listing.groupId());
|
groupIds.add(listing.groupId());
|
||||||
assertTrue(listing.state().isPresent());
|
assertTrue(listing.groupState().isPresent());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(Set.of("share-group-1", "share-group-2", "share-group-3", "share-group-4"), groupIds);
|
assertEquals(Set.of("share-group-1", "share-group-2", "share-group-3", "share-group-4"), groupIds);
|
||||||
|
@ -5289,8 +5288,8 @@ public class KafkaAdminClientTest {
|
||||||
|
|
||||||
assertEquals(2, listings.size());
|
assertEquals(2, listings.size());
|
||||||
List<ShareGroupListing> expected = new ArrayList<>();
|
List<ShareGroupListing> expected = new ArrayList<>();
|
||||||
expected.add(new ShareGroupListing("share-group-1", Optional.of(ShareGroupState.STABLE)));
|
expected.add(new ShareGroupListing("share-group-1", Optional.of(GroupState.STABLE)));
|
||||||
expected.add(new ShareGroupListing("share-group-2", Optional.of(ShareGroupState.EMPTY)));
|
expected.add(new ShareGroupListing("share-group-2", Optional.of(GroupState.EMPTY)));
|
||||||
assertEquals(expected, listings);
|
assertEquals(expected, listings);
|
||||||
assertEquals(0, result.errors().get().size());
|
assertEquals(0, result.errors().get().size());
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.apache.kafka.clients.admin.internals.CoordinatorKey;
|
||||||
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
|
||||||
import org.apache.kafka.common.ElectionType;
|
import org.apache.kafka.common.ElectionType;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.KafkaFuture;
|
import org.apache.kafka.common.KafkaFuture;
|
||||||
import org.apache.kafka.common.Metric;
|
import org.apache.kafka.common.Metric;
|
||||||
|
@ -724,7 +725,7 @@ public class MockAdminClient extends AdminClient {
|
||||||
@Override
|
@Override
|
||||||
public synchronized ListGroupsResult listGroups(ListGroupsOptions options) {
|
public synchronized ListGroupsResult listGroups(ListGroupsOptions options) {
|
||||||
KafkaFutureImpl<Collection<Object>> future = new KafkaFutureImpl<>();
|
KafkaFutureImpl<Collection<Object>> future = new KafkaFutureImpl<>();
|
||||||
future.complete(groupConfigs.keySet().stream().map(g -> new GroupListing(g, Optional.of(GroupType.CONSUMER), ConsumerProtocol.PROTOCOL_TYPE)).collect(Collectors.toList()));
|
future.complete(groupConfigs.keySet().stream().map(g -> new GroupListing(g, Optional.of(GroupType.CONSUMER), ConsumerProtocol.PROTOCOL_TYPE, Optional.of(GroupState.STABLE))).collect(Collectors.toList()));
|
||||||
return new ListGroupsResult(future);
|
return new ListGroupsResult(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,9 @@ package org.apache.kafka.common.requests;
|
||||||
|
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.ConsumerGroupState;
|
||||||
import org.apache.kafka.common.ElectionType;
|
import org.apache.kafka.common.ElectionType;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.IsolationLevel;
|
import org.apache.kafka.common.IsolationLevel;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
|
||||||
import org.apache.kafka.common.TopicIdPartition;
|
import org.apache.kafka.common.TopicIdPartition;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.Uuid;
|
import org.apache.kafka.common.Uuid;
|
||||||
|
@ -1487,7 +1487,7 @@ public class RequestResponseTest {
|
||||||
.setGroupId("group")
|
.setGroupId("group")
|
||||||
.setErrorCode((short) 0)
|
.setErrorCode((short) 0)
|
||||||
.setErrorMessage(Errors.forCode((short) 0).message())
|
.setErrorMessage(Errors.forCode((short) 0).message())
|
||||||
.setGroupState(ShareGroupState.EMPTY.toString())
|
.setGroupState(GroupState.EMPTY.toString())
|
||||||
.setMembers(new ArrayList<>(0))
|
.setMembers(new ArrayList<>(0))
|
||||||
))
|
))
|
||||||
.setThrottleTimeMs(1000);
|
.setThrottleTimeMs(1000);
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.apache.kafka.clients.admin.Admin;
|
||||||
import org.apache.kafka.clients.admin.ConsumerGroupDescription;
|
import org.apache.kafka.clients.admin.ConsumerGroupDescription;
|
||||||
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
import org.apache.kafka.clients.producer.RecordMetadata;
|
import org.apache.kafka.clients.producer.RecordMetadata;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.KafkaFuture;
|
import org.apache.kafka.common.KafkaFuture;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.errors.UnknownMemberIdException;
|
import org.apache.kafka.common.errors.UnknownMemberIdException;
|
||||||
|
@ -297,11 +297,11 @@ public class MirrorCheckpointTask extends SourceTask {
|
||||||
for (String group : consumerGroups) {
|
for (String group : consumerGroups) {
|
||||||
try {
|
try {
|
||||||
ConsumerGroupDescription consumerGroupDesc = consumerGroupsDesc.get(group).get();
|
ConsumerGroupDescription consumerGroupDesc = consumerGroupsDesc.get(group).get();
|
||||||
ConsumerGroupState consumerGroupState = consumerGroupDesc.state();
|
GroupState consumerGroupState = consumerGroupDesc.groupState();
|
||||||
// sync offset to the target cluster only if the state of current consumer group is:
|
// sync offset to the target cluster only if the state of current consumer group is:
|
||||||
// (1) idle: because the consumer at target is not actively consuming the mirrored topic
|
// (1) idle: because the consumer at target is not actively consuming the mirrored topic
|
||||||
// (2) dead: the new consumer that is recently created at source and never existed at target
|
// (2) dead: the new consumer that is recently created at source and never existed at target
|
||||||
if (consumerGroupState == ConsumerGroupState.EMPTY) {
|
if (consumerGroupState == GroupState.EMPTY) {
|
||||||
idleConsumerGroupsOffset.put(
|
idleConsumerGroupsOffset.put(
|
||||||
group,
|
group,
|
||||||
adminCall(
|
adminCall(
|
||||||
|
|
|
@ -48,7 +48,7 @@ import org.apache.kafka.common.requests.DeleteRecordsRequest
|
||||||
import org.apache.kafka.common.resource.{PatternType, ResourcePattern, ResourceType}
|
import org.apache.kafka.common.resource.{PatternType, ResourcePattern, ResourceType}
|
||||||
import org.apache.kafka.common.serialization.{ByteArrayDeserializer, ByteArraySerializer}
|
import org.apache.kafka.common.serialization.{ByteArrayDeserializer, ByteArraySerializer}
|
||||||
import org.apache.kafka.common.utils.{Time, Utils}
|
import org.apache.kafka.common.utils.{Time, Utils}
|
||||||
import org.apache.kafka.common.{ConsumerGroupState, ElectionType, GroupType, IsolationLevel, ShareGroupState, TopicCollection, TopicPartition, TopicPartitionInfo, TopicPartitionReplica, Uuid}
|
import org.apache.kafka.common.{ConsumerGroupState, ElectionType, GroupState, GroupType, IsolationLevel, TopicCollection, TopicPartition, TopicPartitionInfo, TopicPartitionReplica, Uuid}
|
||||||
import org.apache.kafka.controller.ControllerRequestContextUtil.ANONYMOUS_CONTEXT
|
import org.apache.kafka.controller.ControllerRequestContextUtil.ANONYMOUS_CONTEXT
|
||||||
import org.apache.kafka.coordinator.group.{GroupConfig, GroupCoordinatorConfig}
|
import org.apache.kafka.coordinator.group.{GroupConfig, GroupCoordinatorConfig}
|
||||||
import org.apache.kafka.network.SocketServerConfigs
|
import org.apache.kafka.network.SocketServerConfigs
|
||||||
|
@ -1899,7 +1899,7 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
TestUtils.waitUntilTrue(() => {
|
TestUtils.waitUntilTrue(() => {
|
||||||
val matching = client.listConsumerGroups.all.get.asScala.filter(group =>
|
val matching = client.listConsumerGroups.all.get.asScala.filter(group =>
|
||||||
group.groupId == testGroupId &&
|
group.groupId == testGroupId &&
|
||||||
group.state.get == ConsumerGroupState.STABLE)
|
group.groupState.get == GroupState.STABLE)
|
||||||
matching.size == 1
|
matching.size == 1
|
||||||
}, s"Expected to be able to list $testGroupId")
|
}, s"Expected to be able to list $testGroupId")
|
||||||
|
|
||||||
|
@ -1907,6 +1907,279 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
val options = new ListConsumerGroupsOptions().withTypes(Set(groupType).asJava)
|
val options = new ListConsumerGroupsOptions().withTypes(Set(groupType).asJava)
|
||||||
val matching = client.listConsumerGroups(options).all.get.asScala.filter(group =>
|
val matching = client.listConsumerGroups(options).all.get.asScala.filter(group =>
|
||||||
group.groupId == testGroupId &&
|
group.groupId == testGroupId &&
|
||||||
|
group.groupState.get == GroupState.STABLE)
|
||||||
|
matching.size == 1
|
||||||
|
}, s"Expected to be able to list $testGroupId in group type $groupType")
|
||||||
|
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val options = new ListConsumerGroupsOptions().withTypes(Set(groupType).asJava)
|
||||||
|
.inGroupStates(Set(GroupState.STABLE).asJava)
|
||||||
|
val matching = client.listConsumerGroups(options).all.get.asScala.filter(group =>
|
||||||
|
group.groupId == testGroupId &&
|
||||||
|
group.groupState.get == GroupState.STABLE)
|
||||||
|
matching.size == 1
|
||||||
|
}, s"Expected to be able to list $testGroupId in group type $groupType and state Stable")
|
||||||
|
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val options = new ListConsumerGroupsOptions().inGroupStates(Set(GroupState.STABLE).asJava)
|
||||||
|
val matching = client.listConsumerGroups(options).all.get.asScala.filter(group =>
|
||||||
|
group.groupId == testGroupId &&
|
||||||
|
group.groupState.get == GroupState.STABLE)
|
||||||
|
matching.size == 1
|
||||||
|
}, s"Expected to be able to list $testGroupId in state Stable")
|
||||||
|
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val options = new ListConsumerGroupsOptions().inGroupStates(Set(GroupState.EMPTY).asJava)
|
||||||
|
val matching = client.listConsumerGroups(options).all.get.asScala.filter(
|
||||||
|
_.groupId == testGroupId)
|
||||||
|
matching.isEmpty
|
||||||
|
}, s"Expected to find zero groups")
|
||||||
|
|
||||||
|
val describeWithFakeGroupResult = client.describeConsumerGroups(Seq(testGroupId, fakeGroupId).asJava,
|
||||||
|
new DescribeConsumerGroupsOptions().includeAuthorizedOperations(true))
|
||||||
|
assertEquals(2, describeWithFakeGroupResult.describedGroups().size())
|
||||||
|
|
||||||
|
// Test that we can get information about the test consumer group.
|
||||||
|
assertTrue(describeWithFakeGroupResult.describedGroups().containsKey(testGroupId))
|
||||||
|
var testGroupDescription = describeWithFakeGroupResult.describedGroups().get(testGroupId).get()
|
||||||
|
|
||||||
|
assertEquals(testGroupId, testGroupDescription.groupId())
|
||||||
|
assertFalse(testGroupDescription.isSimpleConsumerGroup)
|
||||||
|
assertEquals(groupInstanceSet.size, testGroupDescription.members().size())
|
||||||
|
val members = testGroupDescription.members()
|
||||||
|
members.asScala.foreach(member => assertEquals(testClientId, member.clientId()))
|
||||||
|
val topicPartitionsByTopic = members.asScala.flatMap(_.assignment().topicPartitions().asScala).groupBy(_.topic())
|
||||||
|
topicSet.foreach { topic =>
|
||||||
|
val topicPartitions = topicPartitionsByTopic.getOrElse(topic, List.empty)
|
||||||
|
assertEquals(testNumPartitions, topicPartitions.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedOperations = AclEntry.supportedOperations(ResourceType.GROUP)
|
||||||
|
assertEquals(expectedOperations, testGroupDescription.authorizedOperations())
|
||||||
|
|
||||||
|
// Test that the fake group is listed as dead.
|
||||||
|
assertTrue(describeWithFakeGroupResult.describedGroups().containsKey(fakeGroupId))
|
||||||
|
val fakeGroupDescription = describeWithFakeGroupResult.describedGroups().get(fakeGroupId).get()
|
||||||
|
|
||||||
|
assertEquals(fakeGroupId, fakeGroupDescription.groupId())
|
||||||
|
assertEquals(0, fakeGroupDescription.members().size())
|
||||||
|
assertEquals("", fakeGroupDescription.partitionAssignor())
|
||||||
|
assertEquals(GroupState.DEAD, fakeGroupDescription.groupState())
|
||||||
|
assertEquals(expectedOperations, fakeGroupDescription.authorizedOperations())
|
||||||
|
|
||||||
|
// Test that all() returns 2 results
|
||||||
|
assertEquals(2, describeWithFakeGroupResult.all().get().size())
|
||||||
|
|
||||||
|
val testTopicPart0 = new TopicPartition(testTopicName, 0)
|
||||||
|
|
||||||
|
// Test listConsumerGroupOffsets
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val parts = client.listConsumerGroupOffsets(testGroupId).partitionsToOffsetAndMetadata().get()
|
||||||
|
parts.containsKey(testTopicPart0) && (parts.get(testTopicPart0).offset() == 1)
|
||||||
|
}, s"Expected the offset for partition 0 to eventually become 1.")
|
||||||
|
|
||||||
|
// Test listConsumerGroupOffsets with requireStable true
|
||||||
|
val options = new ListConsumerGroupOffsetsOptions().requireStable(true)
|
||||||
|
var parts = client.listConsumerGroupOffsets(testGroupId, options)
|
||||||
|
.partitionsToOffsetAndMetadata().get()
|
||||||
|
assertTrue(parts.containsKey(testTopicPart0))
|
||||||
|
assertEquals(1, parts.get(testTopicPart0).offset())
|
||||||
|
|
||||||
|
// Test listConsumerGroupOffsets with listConsumerGroupOffsetsSpec
|
||||||
|
val groupSpecs = Collections.singletonMap(testGroupId,
|
||||||
|
new ListConsumerGroupOffsetsSpec().topicPartitions(Collections.singleton(new TopicPartition(testTopicName, 0))))
|
||||||
|
parts = client.listConsumerGroupOffsets(groupSpecs).partitionsToOffsetAndMetadata().get()
|
||||||
|
assertTrue(parts.containsKey(testTopicPart0))
|
||||||
|
assertEquals(1, parts.get(testTopicPart0).offset())
|
||||||
|
|
||||||
|
// Test listConsumerGroupOffsets with listConsumerGroupOffsetsSpec and requireStable option
|
||||||
|
parts = client.listConsumerGroupOffsets(groupSpecs, options).partitionsToOffsetAndMetadata().get()
|
||||||
|
assertTrue(parts.containsKey(testTopicPart0))
|
||||||
|
assertEquals(1, parts.get(testTopicPart0).offset())
|
||||||
|
|
||||||
|
// Test delete non-exist consumer instance
|
||||||
|
val invalidInstanceId = "invalid-instance-id"
|
||||||
|
var removeMembersResult = client.removeMembersFromConsumerGroup(testGroupId, new RemoveMembersFromConsumerGroupOptions(
|
||||||
|
Collections.singleton(new MemberToRemove(invalidInstanceId))
|
||||||
|
))
|
||||||
|
|
||||||
|
TestUtils.assertFutureExceptionTypeEquals(removeMembersResult.all, classOf[UnknownMemberIdException])
|
||||||
|
val firstMemberFuture = removeMembersResult.memberResult(new MemberToRemove(invalidInstanceId))
|
||||||
|
TestUtils.assertFutureExceptionTypeEquals(firstMemberFuture, classOf[UnknownMemberIdException])
|
||||||
|
|
||||||
|
// Test consumer group deletion
|
||||||
|
var deleteResult = client.deleteConsumerGroups(Seq(testGroupId, fakeGroupId).asJava)
|
||||||
|
assertEquals(2, deleteResult.deletedGroups().size())
|
||||||
|
|
||||||
|
// Deleting the fake group ID should get GroupIdNotFoundException.
|
||||||
|
assertTrue(deleteResult.deletedGroups().containsKey(fakeGroupId))
|
||||||
|
assertFutureExceptionTypeEquals(deleteResult.deletedGroups().get(fakeGroupId),
|
||||||
|
classOf[GroupIdNotFoundException])
|
||||||
|
|
||||||
|
// Deleting the real group ID should get GroupNotEmptyException
|
||||||
|
assertTrue(deleteResult.deletedGroups().containsKey(testGroupId))
|
||||||
|
assertFutureExceptionTypeEquals(deleteResult.deletedGroups().get(testGroupId),
|
||||||
|
classOf[GroupNotEmptyException])
|
||||||
|
|
||||||
|
// Test delete one correct static member
|
||||||
|
val removeOptions = new RemoveMembersFromConsumerGroupOptions(Collections.singleton(new MemberToRemove(testInstanceId1)))
|
||||||
|
removeOptions.reason("test remove")
|
||||||
|
removeMembersResult = client.removeMembersFromConsumerGroup(testGroupId, removeOptions)
|
||||||
|
|
||||||
|
assertNull(removeMembersResult.all().get())
|
||||||
|
val validMemberFuture = removeMembersResult.memberResult(new MemberToRemove(testInstanceId1))
|
||||||
|
assertNull(validMemberFuture.get())
|
||||||
|
|
||||||
|
val describeTestGroupResult = client.describeConsumerGroups(Seq(testGroupId).asJava,
|
||||||
|
new DescribeConsumerGroupsOptions().includeAuthorizedOperations(true))
|
||||||
|
assertEquals(1, describeTestGroupResult.describedGroups().size())
|
||||||
|
|
||||||
|
testGroupDescription = describeTestGroupResult.describedGroups().get(testGroupId).get()
|
||||||
|
|
||||||
|
assertEquals(testGroupId, testGroupDescription.groupId)
|
||||||
|
assertFalse(testGroupDescription.isSimpleConsumerGroup)
|
||||||
|
assertEquals(consumerSet.size - 1, testGroupDescription.members().size())
|
||||||
|
|
||||||
|
// Delete all active members remaining (a static member + a dynamic member)
|
||||||
|
removeMembersResult = client.removeMembersFromConsumerGroup(testGroupId, new RemoveMembersFromConsumerGroupOptions())
|
||||||
|
assertNull(removeMembersResult.all().get())
|
||||||
|
|
||||||
|
// The group should contain no members now.
|
||||||
|
testGroupDescription = client.describeConsumerGroups(Seq(testGroupId).asJava,
|
||||||
|
new DescribeConsumerGroupsOptions().includeAuthorizedOperations(true))
|
||||||
|
.describedGroups().get(testGroupId).get()
|
||||||
|
assertTrue(testGroupDescription.members().isEmpty)
|
||||||
|
|
||||||
|
// Consumer group deletion on empty group should succeed
|
||||||
|
deleteResult = client.deleteConsumerGroups(Seq(testGroupId).asJava)
|
||||||
|
assertEquals(1, deleteResult.deletedGroups().size())
|
||||||
|
|
||||||
|
assertTrue(deleteResult.deletedGroups().containsKey(testGroupId))
|
||||||
|
assertNull(deleteResult.deletedGroups().get(testGroupId).get())
|
||||||
|
|
||||||
|
// Test alterConsumerGroupOffsets
|
||||||
|
val alterConsumerGroupOffsetsResult = client.alterConsumerGroupOffsets(testGroupId,
|
||||||
|
Collections.singletonMap(testTopicPart0, new OffsetAndMetadata(0L)))
|
||||||
|
assertNull(alterConsumerGroupOffsetsResult.all().get())
|
||||||
|
assertNull(alterConsumerGroupOffsetsResult.partitionResult(testTopicPart0).get())
|
||||||
|
|
||||||
|
// Verify alterConsumerGroupOffsets success
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val parts = client.listConsumerGroupOffsets(testGroupId).partitionsToOffsetAndMetadata().get()
|
||||||
|
parts.containsKey(testTopicPart0) && (parts.get(testTopicPart0).offset() == 0)
|
||||||
|
}, s"Expected the offset for partition 0 to eventually become 0.")
|
||||||
|
} finally {
|
||||||
|
consumerThreads.foreach {
|
||||||
|
case consumerThread =>
|
||||||
|
consumerThread.interrupt()
|
||||||
|
consumerThread.join()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
consumerSet.zip(groupInstanceSet).foreach(zipped => Utils.closeQuietly(zipped._1, zipped._2))
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Utils.closeQuietly(client, "adminClient")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the consumer group APIs.
|
||||||
|
*/
|
||||||
|
@ParameterizedTest(name = TestInfoUtils.TestWithParameterizedQuorumAndGroupProtocolNames)
|
||||||
|
@MethodSource(Array("getTestQuorumAndGroupProtocolParametersClassicGroupProtocolOnly_KAFKA_17960"))
|
||||||
|
def testConsumerGroupsDeprecatedConsumerGroupState(quorum: String, groupProtocol: String): Unit = {
|
||||||
|
val config = createConfig
|
||||||
|
client = Admin.create(config)
|
||||||
|
try {
|
||||||
|
// Verify that initially there are no consumer groups to list.
|
||||||
|
val list1 = client.listConsumerGroups()
|
||||||
|
assertEquals(0, list1.all().get().size())
|
||||||
|
assertEquals(0, list1.errors().get().size())
|
||||||
|
assertEquals(0, list1.valid().get().size())
|
||||||
|
val testTopicName = "test_topic"
|
||||||
|
val testTopicName1 = testTopicName + "1"
|
||||||
|
val testTopicName2 = testTopicName + "2"
|
||||||
|
val testNumPartitions = 2
|
||||||
|
|
||||||
|
client.createTopics(util.Arrays.asList(
|
||||||
|
new NewTopic(testTopicName, testNumPartitions, 1.toShort),
|
||||||
|
new NewTopic(testTopicName1, testNumPartitions, 1.toShort),
|
||||||
|
new NewTopic(testTopicName2, testNumPartitions, 1.toShort)
|
||||||
|
)).all().get()
|
||||||
|
waitForTopics(client, List(testTopicName, testTopicName1, testTopicName2), List())
|
||||||
|
|
||||||
|
val producer = createProducer()
|
||||||
|
try {
|
||||||
|
producer.send(new ProducerRecord(testTopicName, 0, null, null)).get()
|
||||||
|
} finally {
|
||||||
|
Utils.closeQuietly(producer, "producer")
|
||||||
|
}
|
||||||
|
|
||||||
|
val EMPTY_GROUP_INSTANCE_ID = ""
|
||||||
|
val testGroupId = "test_group_id"
|
||||||
|
val testClientId = "test_client_id"
|
||||||
|
val testInstanceId1 = "test_instance_id_1"
|
||||||
|
val testInstanceId2 = "test_instance_id_2"
|
||||||
|
val fakeGroupId = "fake_group_id"
|
||||||
|
|
||||||
|
def createProperties(groupInstanceId: String): Properties = {
|
||||||
|
val newConsumerConfig = new Properties(consumerConfig)
|
||||||
|
// We need to disable the auto commit because after the members got removed from group, the offset commit
|
||||||
|
// will cause the member rejoining and the test will be flaky (check ConsumerCoordinator#OffsetCommitResponseHandler)
|
||||||
|
newConsumerConfig.setProperty(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false")
|
||||||
|
newConsumerConfig.setProperty(ConsumerConfig.GROUP_ID_CONFIG, testGroupId)
|
||||||
|
newConsumerConfig.setProperty(ConsumerConfig.CLIENT_ID_CONFIG, testClientId)
|
||||||
|
if (groupInstanceId != EMPTY_GROUP_INSTANCE_ID) {
|
||||||
|
newConsumerConfig.setProperty(ConsumerConfig.GROUP_INSTANCE_ID_CONFIG, groupInstanceId)
|
||||||
|
}
|
||||||
|
newConsumerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains two static members and one dynamic member
|
||||||
|
val groupInstanceSet = Set(testInstanceId1, testInstanceId2, EMPTY_GROUP_INSTANCE_ID)
|
||||||
|
val consumerSet = groupInstanceSet.map { groupInstanceId => createConsumer(configOverrides = createProperties(groupInstanceId))}
|
||||||
|
val topicSet = Set(testTopicName, testTopicName1, testTopicName2)
|
||||||
|
|
||||||
|
val latch = new CountDownLatch(consumerSet.size)
|
||||||
|
try {
|
||||||
|
def createConsumerThread[K,V](consumer: Consumer[K,V], topic: String): Thread = {
|
||||||
|
new Thread {
|
||||||
|
override def run : Unit = {
|
||||||
|
consumer.subscribe(Collections.singleton(topic))
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
consumer.poll(JDuration.ofSeconds(5))
|
||||||
|
if (!consumer.assignment.isEmpty && latch.getCount > 0L)
|
||||||
|
latch.countDown()
|
||||||
|
consumer.commitSync()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case _: InterruptException => // Suppress the output to stderr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start consumers in a thread that will subscribe to a new group.
|
||||||
|
val consumerThreads = consumerSet.zip(topicSet).map(zipped => createConsumerThread(zipped._1, zipped._2))
|
||||||
|
val groupType = if (groupProtocol.equalsIgnoreCase(GroupProtocol.CONSUMER.name)) GroupType.CONSUMER else GroupType.CLASSIC
|
||||||
|
|
||||||
|
try {
|
||||||
|
consumerThreads.foreach(_.start())
|
||||||
|
assertTrue(latch.await(30000, TimeUnit.MILLISECONDS))
|
||||||
|
// Test that we can list the new group.
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val matching = client.listConsumerGroups.all.get.asScala.filter(group =>
|
||||||
|
group.groupId == testGroupId &&
|
||||||
|
group.state.get == ConsumerGroupState.STABLE)
|
||||||
|
matching.size == 1
|
||||||
|
}, s"Expected to be able to list $testGroupId")
|
||||||
|
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val options = new ListConsumerGroupsOptions().withTypes(Set(groupType).asJava)
|
||||||
|
val matching = client.listConsumerGroups(options).all.get.asScala.filter(group =>
|
||||||
|
group.groupId == testGroupId &&
|
||||||
group.state.get == ConsumerGroupState.STABLE)
|
group.state.get == ConsumerGroupState.STABLE)
|
||||||
matching.size == 1
|
matching.size == 1
|
||||||
}, s"Expected to be able to list $testGroupId in group type $groupType")
|
}, s"Expected to be able to list $testGroupId in group type $groupType")
|
||||||
|
@ -1923,7 +2196,7 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
TestUtils.waitUntilTrue(() => {
|
TestUtils.waitUntilTrue(() => {
|
||||||
val options = new ListConsumerGroupsOptions().inStates(Set(ConsumerGroupState.STABLE).asJava)
|
val options = new ListConsumerGroupsOptions().inStates(Set(ConsumerGroupState.STABLE).asJava)
|
||||||
val matching = client.listConsumerGroups(options).all.get.asScala.filter(group =>
|
val matching = client.listConsumerGroups(options).all.get.asScala.filter(group =>
|
||||||
group.groupId == testGroupId &&
|
group.groupId == testGroupId &&
|
||||||
group.state.get == ConsumerGroupState.STABLE)
|
group.state.get == ConsumerGroupState.STABLE)
|
||||||
matching.size == 1
|
matching.size == 1
|
||||||
}, s"Expected to be able to list $testGroupId in state Stable")
|
}, s"Expected to be able to list $testGroupId in state Stable")
|
||||||
|
@ -1931,7 +2204,7 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
TestUtils.waitUntilTrue(() => {
|
TestUtils.waitUntilTrue(() => {
|
||||||
val options = new ListConsumerGroupsOptions().inStates(Set(ConsumerGroupState.EMPTY).asJava)
|
val options = new ListConsumerGroupsOptions().inStates(Set(ConsumerGroupState.EMPTY).asJava)
|
||||||
val matching = client.listConsumerGroups(options).all.get.asScala.filter(
|
val matching = client.listConsumerGroups(options).all.get.asScala.filter(
|
||||||
_.groupId == testGroupId)
|
_.groupId == testGroupId)
|
||||||
matching.isEmpty
|
matching.isEmpty
|
||||||
}, s"Expected to find zero groups")
|
}, s"Expected to find zero groups")
|
||||||
|
|
||||||
|
@ -2046,8 +2319,8 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
|
|
||||||
// The group should contain no members now.
|
// The group should contain no members now.
|
||||||
testGroupDescription = client.describeConsumerGroups(Seq(testGroupId).asJava,
|
testGroupDescription = client.describeConsumerGroups(Seq(testGroupId).asJava,
|
||||||
new DescribeConsumerGroupsOptions().includeAuthorizedOperations(true))
|
new DescribeConsumerGroupsOptions().includeAuthorizedOperations(true))
|
||||||
.describedGroups().get(testGroupId).get()
|
.describedGroups().get(testGroupId).get()
|
||||||
assertTrue(testGroupDescription.members().isEmpty)
|
assertTrue(testGroupDescription.members().isEmpty)
|
||||||
|
|
||||||
// Consumer group deletion on empty group should succeed
|
// Consumer group deletion on empty group should succeed
|
||||||
|
@ -2068,13 +2341,13 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
val parts = client.listConsumerGroupOffsets(testGroupId).partitionsToOffsetAndMetadata().get()
|
val parts = client.listConsumerGroupOffsets(testGroupId).partitionsToOffsetAndMetadata().get()
|
||||||
parts.containsKey(testTopicPart0) && (parts.get(testTopicPart0).offset() == 0)
|
parts.containsKey(testTopicPart0) && (parts.get(testTopicPart0).offset() == 0)
|
||||||
}, s"Expected the offset for partition 0 to eventually become 0.")
|
}, s"Expected the offset for partition 0 to eventually become 0.")
|
||||||
} finally {
|
} finally {
|
||||||
consumerThreads.foreach {
|
consumerThreads.foreach {
|
||||||
case consumerThread =>
|
case consumerThread =>
|
||||||
consumerThread.interrupt()
|
consumerThread.interrupt()
|
||||||
consumerThread.join()
|
consumerThread.join()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
consumerSet.zip(groupInstanceSet).foreach(zipped => Utils.closeQuietly(zipped._1, zipped._2))
|
consumerSet.zip(groupInstanceSet).foreach(zipped => Utils.closeQuietly(zipped._1, zipped._2))
|
||||||
}
|
}
|
||||||
|
@ -2203,10 +2476,10 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
groups.size() == 4
|
groups.size() == 4
|
||||||
}, "Expected to find all groups")
|
}, "Expected to find all groups")
|
||||||
|
|
||||||
val classicGroupListing = new GroupListing(classicGroupId, Optional.of(GroupType.CLASSIC), "consumer")
|
val classicGroupListing = new GroupListing(classicGroupId, Optional.of(GroupType.CLASSIC), "consumer", Optional.of(GroupState.STABLE))
|
||||||
val consumerGroupListing = new GroupListing(consumerGroupId, Optional.of(GroupType.CONSUMER), "consumer")
|
val consumerGroupListing = new GroupListing(consumerGroupId, Optional.of(GroupType.CONSUMER), "consumer", Optional.of(GroupState.STABLE))
|
||||||
val shareGroupListing = new GroupListing(shareGroupId, Optional.of(GroupType.SHARE), "share")
|
val shareGroupListing = new GroupListing(shareGroupId, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
val simpleGroupListing = new GroupListing(simpleGroupId, Optional.of(GroupType.CLASSIC), "")
|
val simpleGroupListing = new GroupListing(simpleGroupId, Optional.of(GroupType.CLASSIC), "", Optional.of(GroupState.EMPTY))
|
||||||
|
|
||||||
var listGroupsResult = client.listGroups()
|
var listGroupsResult = client.listGroups()
|
||||||
assertTrue(listGroupsResult.errors().get().isEmpty)
|
assertTrue(listGroupsResult.errors().get().isEmpty)
|
||||||
|
@ -2327,6 +2600,11 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
val producer = createProducer()
|
val producer = createProducer()
|
||||||
try {
|
try {
|
||||||
// Verify that initially there are no share groups to list.
|
// Verify that initially there are no share groups to list.
|
||||||
|
val list = client.listGroups()
|
||||||
|
assertEquals(0, list.all().get().size())
|
||||||
|
assertEquals(0, list.errors().get().size())
|
||||||
|
assertEquals(0, list.valid().get().size())
|
||||||
|
|
||||||
val list1 = client.listShareGroups()
|
val list1 = client.listShareGroups()
|
||||||
assertEquals(0, list1.all().get().size())
|
assertEquals(0, list1.all().get().size())
|
||||||
assertEquals(0, list1.errors().get().size())
|
assertEquals(0, list1.errors().get().size())
|
||||||
|
@ -2350,21 +2628,40 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
TestUtils.waitUntilTrue(() => {
|
TestUtils.waitUntilTrue(() => {
|
||||||
client.listShareGroups.all.get.stream().filter(group =>
|
client.listShareGroups.all.get.stream().filter(group =>
|
||||||
group.groupId == testGroupId &&
|
group.groupId == testGroupId &&
|
||||||
group.state.get == ShareGroupState.STABLE).count() == 1
|
group.groupState.get == GroupState.STABLE).count() == 1
|
||||||
}, s"Expected to be able to list $testGroupId")
|
}, s"Expected to be able to list $testGroupId")
|
||||||
|
|
||||||
TestUtils.waitUntilTrue(() => {
|
TestUtils.waitUntilTrue(() => {
|
||||||
val options = new ListShareGroupsOptions().inStates(Collections.singleton(ShareGroupState.STABLE))
|
val options = new ListShareGroupsOptions().inStates(Collections.singleton(GroupState.STABLE))
|
||||||
client.listShareGroups(options).all.get.stream().filter(group =>
|
client.listShareGroups(options).all.get.stream().filter(group =>
|
||||||
group.groupId == testGroupId &&
|
group.groupId == testGroupId &&
|
||||||
group.state.get == ShareGroupState.STABLE).count() == 1
|
group.groupState.get == GroupState.STABLE).count() == 1
|
||||||
}, s"Expected to be able to list $testGroupId in state Stable")
|
}, s"Expected to be able to list $testGroupId in state Stable")
|
||||||
|
|
||||||
TestUtils.waitUntilTrue(() => {
|
TestUtils.waitUntilTrue(() => {
|
||||||
val options = new ListShareGroupsOptions().inStates(Collections.singleton(ShareGroupState.EMPTY))
|
val options = new ListShareGroupsOptions().inStates(Collections.singleton(GroupState.EMPTY))
|
||||||
client.listShareGroups(options).all.get.stream().filter(_.groupId == testGroupId).count() == 0
|
client.listShareGroups(options).all.get.stream().filter(_.groupId == testGroupId).count() == 0
|
||||||
}, s"Expected to find zero groups")
|
}, s"Expected to find zero groups")
|
||||||
|
|
||||||
|
// listGroups is equivalent to listShareGroups so ensure that works too
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
client.listGroups.all.get.stream().filter(group =>
|
||||||
|
group.groupId == testGroupId &&
|
||||||
|
group.groupState.get == GroupState.STABLE).count() == 1
|
||||||
|
}, s"Expected to be able to list $testGroupId")
|
||||||
|
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val options = new ListGroupsOptions().withTypes(Collections.singleton(GroupType.SHARE)).inGroupStates(Collections.singleton(GroupState.STABLE))
|
||||||
|
client.listGroups(options).all.get.stream().filter(group =>
|
||||||
|
group.groupId == testGroupId &&
|
||||||
|
group.groupState.get == GroupState.STABLE).count() == 1
|
||||||
|
}, s"Expected to be able to list $testGroupId in state Stable")
|
||||||
|
|
||||||
|
TestUtils.waitUntilTrue(() => {
|
||||||
|
val options = new ListGroupsOptions().withTypes(Collections.singleton(GroupType.SHARE)).inGroupStates(Collections.singleton(GroupState.EMPTY))
|
||||||
|
client.listGroups(options).all.get.stream().filter(_.groupId == testGroupId).count() == 0
|
||||||
|
}, s"Expected to find zero groups")
|
||||||
|
|
||||||
val describeWithFakeGroupResult = client.describeShareGroups(util.Arrays.asList(testGroupId, fakeGroupId),
|
val describeWithFakeGroupResult = client.describeShareGroups(util.Arrays.asList(testGroupId, fakeGroupId),
|
||||||
new DescribeShareGroupsOptions().includeAuthorizedOperations(true))
|
new DescribeShareGroupsOptions().includeAuthorizedOperations(true))
|
||||||
assertEquals(2, describeWithFakeGroupResult.describedGroups().size())
|
assertEquals(2, describeWithFakeGroupResult.describedGroups().size())
|
||||||
|
@ -2393,7 +2690,7 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
|
|
||||||
assertEquals(fakeGroupId, fakeGroupDescription.groupId())
|
assertEquals(fakeGroupId, fakeGroupDescription.groupId())
|
||||||
assertEquals(0, fakeGroupDescription.members().size())
|
assertEquals(0, fakeGroupDescription.members().size())
|
||||||
assertEquals(ShareGroupState.DEAD, fakeGroupDescription.state())
|
assertEquals(GroupState.DEAD, fakeGroupDescription.groupState())
|
||||||
assertNull(fakeGroupDescription.authorizedOperations())
|
assertNull(fakeGroupDescription.authorizedOperations())
|
||||||
|
|
||||||
// Test that all() returns 2 results
|
// Test that all() returns 2 results
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.apache.kafka.common.test.api.ClusterInstance
|
||||||
import org.apache.kafka.common.test.api._
|
import org.apache.kafka.common.test.api._
|
||||||
import org.apache.kafka.common.test.api.ClusterTestExtensions
|
import org.apache.kafka.common.test.api.ClusterTestExtensions
|
||||||
import kafka.utils.TestUtils
|
import kafka.utils.TestUtils
|
||||||
import org.apache.kafka.common.ShareGroupState
|
import org.apache.kafka.common.GroupState
|
||||||
import org.apache.kafka.common.message.ShareGroupDescribeResponseData.DescribedGroup
|
import org.apache.kafka.common.message.ShareGroupDescribeResponseData.DescribedGroup
|
||||||
import org.apache.kafka.common.message.{ShareGroupDescribeRequestData, ShareGroupDescribeResponseData, ShareGroupHeartbeatResponseData}
|
import org.apache.kafka.common.message.{ShareGroupDescribeRequestData, ShareGroupDescribeResponseData, ShareGroupHeartbeatResponseData}
|
||||||
import org.apache.kafka.common.protocol.{ApiKeys, Errors}
|
import org.apache.kafka.common.protocol.{ApiKeys, Errors}
|
||||||
|
@ -114,7 +114,7 @@ class ShareGroupDescribeRequestTest(cluster: ClusterInstance) extends GroupCoord
|
||||||
val expected = List(
|
val expected = List(
|
||||||
new DescribedGroup()
|
new DescribedGroup()
|
||||||
.setGroupId("grp-1")
|
.setGroupId("grp-1")
|
||||||
.setGroupState(ShareGroupState.STABLE.toString)
|
.setGroupState(GroupState.STABLE.toString)
|
||||||
.setGroupEpoch(1)
|
.setGroupEpoch(1)
|
||||||
.setAssignmentEpoch(1)
|
.setAssignmentEpoch(1)
|
||||||
.setAssignorName("simple")
|
.setAssignorName("simple")
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.kafka.coordinator.group.metrics;
|
package org.apache.kafka.coordinator.group.metrics;
|
||||||
|
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.MetricName;
|
import org.apache.kafka.common.MetricName;
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.internals.Topic;
|
import org.apache.kafka.common.internals.Topic;
|
||||||
import org.apache.kafka.common.metrics.Metrics;
|
import org.apache.kafka.common.metrics.Metrics;
|
||||||
|
@ -116,19 +116,19 @@ public class GroupCoordinatorMetricsTest {
|
||||||
GroupCoordinatorMetrics.METRICS_GROUP,
|
GroupCoordinatorMetrics.METRICS_GROUP,
|
||||||
"The number of share groups in empty state.",
|
"The number of share groups in empty state.",
|
||||||
"protocol", Group.GroupType.SHARE.toString(),
|
"protocol", Group.GroupType.SHARE.toString(),
|
||||||
"state", ShareGroupState.EMPTY.toString()),
|
"state", GroupState.EMPTY.toString()),
|
||||||
metrics.metricName(
|
metrics.metricName(
|
||||||
"group-count",
|
"group-count",
|
||||||
GroupCoordinatorMetrics.METRICS_GROUP,
|
GroupCoordinatorMetrics.METRICS_GROUP,
|
||||||
"The number of share groups in stable state.",
|
"The number of share groups in stable state.",
|
||||||
"protocol", Group.GroupType.SHARE.toString(),
|
"protocol", Group.GroupType.SHARE.toString(),
|
||||||
"state", ShareGroupState.STABLE.toString()),
|
"state", GroupState.STABLE.toString()),
|
||||||
metrics.metricName(
|
metrics.metricName(
|
||||||
"group-count",
|
"group-count",
|
||||||
GroupCoordinatorMetrics.METRICS_GROUP,
|
GroupCoordinatorMetrics.METRICS_GROUP,
|
||||||
"The number of share groups in dead state.",
|
"The number of share groups in dead state.",
|
||||||
"protocol", Group.GroupType.SHARE.toString(),
|
"protocol", Group.GroupType.SHARE.toString(),
|
||||||
"state", ShareGroupState.DEAD.toString())
|
"state", GroupState.DEAD.toString())
|
||||||
));
|
));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -38,7 +38,7 @@ import org.apache.kafka.clients.admin.MemberDescription;
|
||||||
import org.apache.kafka.clients.admin.OffsetSpec;
|
import org.apache.kafka.clients.admin.OffsetSpec;
|
||||||
import org.apache.kafka.clients.admin.TopicDescription;
|
import org.apache.kafka.clients.admin.TopicDescription;
|
||||||
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.KafkaException;
|
import org.apache.kafka.common.KafkaException;
|
||||||
import org.apache.kafka.common.KafkaFuture;
|
import org.apache.kafka.common.KafkaFuture;
|
||||||
|
@ -130,11 +130,12 @@ public class ConsumerGroupCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Set<ConsumerGroupState> consumerGroupStatesFromString(String input) {
|
static Set<GroupState> groupStatesFromString(String input) {
|
||||||
Set<ConsumerGroupState> parsedStates = Arrays.stream(input.split(",")).map(s -> ConsumerGroupState.parse(s.trim())).collect(Collectors.toSet());
|
Set<GroupState> parsedStates = Arrays.stream(input.split(",")).map(s -> GroupState.parse(s.trim())).collect(Collectors.toSet());
|
||||||
if (parsedStates.contains(ConsumerGroupState.UNKNOWN)) {
|
Set<GroupState> validStates = GroupState.groupStatesForType(GroupType.CONSUMER);
|
||||||
Collection<ConsumerGroupState> validStates = Arrays.stream(ConsumerGroupState.values()).filter(s -> s != ConsumerGroupState.UNKNOWN).collect(Collectors.toList());
|
if (!validStates.containsAll(parsedStates)) {
|
||||||
throw new IllegalArgumentException("Invalid state list '" + input + "'. Valid states are: " + validStates.stream().map(ConsumerGroupState::toString).collect(Collectors.joining(", ")));
|
throw new IllegalArgumentException("Invalid state list '" + input + "'. Valid states are: " +
|
||||||
|
validStates.stream().map(GroupState::toString).collect(Collectors.joining(", ")));
|
||||||
}
|
}
|
||||||
return parsedStates;
|
return parsedStates;
|
||||||
}
|
}
|
||||||
|
@ -204,7 +205,7 @@ public class ConsumerGroupCommand {
|
||||||
|
|
||||||
if (includeType || includeState) {
|
if (includeType || includeState) {
|
||||||
Set<GroupType> types = typeValues();
|
Set<GroupType> types = typeValues();
|
||||||
Set<ConsumerGroupState> states = stateValues();
|
Set<GroupState> states = stateValues();
|
||||||
List<ConsumerGroupListing> listings = listConsumerGroupsWithFilters(types, states);
|
List<ConsumerGroupListing> listings = listConsumerGroupsWithFilters(types, states);
|
||||||
|
|
||||||
printGroupInfo(listings, includeType, includeState);
|
printGroupInfo(listings, includeType, includeState);
|
||||||
|
@ -213,11 +214,11 @@ public class ConsumerGroupCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<ConsumerGroupState> stateValues() {
|
private Set<GroupState> stateValues() {
|
||||||
String stateValue = opts.options.valueOf(opts.stateOpt);
|
String stateValue = opts.options.valueOf(opts.stateOpt);
|
||||||
return (stateValue == null || stateValue.isEmpty())
|
return (stateValue == null || stateValue.isEmpty())
|
||||||
? Collections.emptySet()
|
? Collections.emptySet()
|
||||||
: consumerGroupStatesFromString(stateValue);
|
: groupStatesFromString(stateValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<GroupType> typeValues() {
|
private Set<GroupType> typeValues() {
|
||||||
|
@ -230,7 +231,7 @@ public class ConsumerGroupCommand {
|
||||||
private void printGroupInfo(List<ConsumerGroupListing> groups, boolean includeType, boolean includeState) {
|
private void printGroupInfo(List<ConsumerGroupListing> groups, boolean includeType, boolean includeState) {
|
||||||
Function<ConsumerGroupListing, String> groupId = ConsumerGroupListing::groupId;
|
Function<ConsumerGroupListing, String> groupId = ConsumerGroupListing::groupId;
|
||||||
Function<ConsumerGroupListing, String> groupType = groupListing -> groupListing.type().orElse(GroupType.UNKNOWN).toString();
|
Function<ConsumerGroupListing, String> groupType = groupListing -> groupListing.type().orElse(GroupType.UNKNOWN).toString();
|
||||||
Function<ConsumerGroupListing, String> groupState = groupListing -> groupListing.state().orElse(ConsumerGroupState.UNKNOWN).toString();
|
Function<ConsumerGroupListing, String> groupState = groupListing -> groupListing.groupState().orElse(GroupState.UNKNOWN).toString();
|
||||||
|
|
||||||
OptionalInt maybeMax = groups.stream().mapToInt(groupListing -> Math.max(15, groupId.apply(groupListing).length())).max();
|
OptionalInt maybeMax = groups.stream().mapToInt(groupListing -> Math.max(15, groupId.apply(groupListing).length())).max();
|
||||||
int maxGroupLen = maybeMax.orElse(15) + 10;
|
int maxGroupLen = maybeMax.orElse(15) + 10;
|
||||||
|
@ -270,26 +271,26 @@ public class ConsumerGroupCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ConsumerGroupListing> listConsumerGroupsWithFilters(Set<GroupType> types, Set<ConsumerGroupState> states) throws ExecutionException, InterruptedException {
|
List<ConsumerGroupListing> listConsumerGroupsWithFilters(Set<GroupType> types, Set<GroupState> states) throws ExecutionException, InterruptedException {
|
||||||
ListConsumerGroupsOptions listConsumerGroupsOptions = withTimeoutMs(new ListConsumerGroupsOptions());
|
ListConsumerGroupsOptions listConsumerGroupsOptions = withTimeoutMs(new ListConsumerGroupsOptions());
|
||||||
listConsumerGroupsOptions
|
listConsumerGroupsOptions
|
||||||
.inStates(states)
|
.inGroupStates(states)
|
||||||
.withTypes(types);
|
.withTypes(types);
|
||||||
ListConsumerGroupsResult result = adminClient.listConsumerGroups(listConsumerGroupsOptions);
|
ListConsumerGroupsResult result = adminClient.listConsumerGroups(listConsumerGroupsOptions);
|
||||||
return new ArrayList<>(result.all().get());
|
return new ArrayList<>(result.all().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldPrintMemberState(String group, Optional<ConsumerGroupState> state, Optional<Integer> numRows) {
|
private boolean shouldPrintMemberState(String group, Optional<GroupState> state, Optional<Integer> numRows) {
|
||||||
// numRows contains the number of data rows, if any, compiled from the API call in the caller method.
|
// numRows contains the number of data rows, if any, compiled from the API call in the caller method.
|
||||||
// if it's undefined or 0, there is no relevant group information to display.
|
// if it's undefined or 0, there is no relevant group information to display.
|
||||||
if (!numRows.isPresent()) {
|
if (numRows.isEmpty()) {
|
||||||
printError("The consumer group '" + group + "' does not exist.", Optional.empty());
|
printError("The consumer group '" + group + "' does not exist.", Optional.empty());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num = numRows.get();
|
int num = numRows.get();
|
||||||
|
|
||||||
ConsumerGroupState state0 = state.orElse(ConsumerGroupState.UNKNOWN);
|
GroupState state0 = state.orElse(GroupState.UNKNOWN);
|
||||||
switch (state0) {
|
switch (state0) {
|
||||||
case DEAD:
|
case DEAD:
|
||||||
printError("Consumer group '" + group + "' does not exist.", Optional.empty());
|
printError("Consumer group '" + group + "' does not exist.", Optional.empty());
|
||||||
|
@ -310,16 +311,16 @@ public class ConsumerGroupCommand {
|
||||||
throw new KafkaException("Expected a valid consumer group state, but found '" + state0 + "'.");
|
throw new KafkaException("Expected a valid consumer group state, but found '" + state0 + "'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return !state0.equals(ConsumerGroupState.DEAD) && num > 0;
|
return !state0.equals(GroupState.DEAD) && num > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Integer> size(Optional<? extends Collection<?>> colOpt) {
|
private Optional<Integer> size(Optional<? extends Collection<?>> colOpt) {
|
||||||
return colOpt.map(Collection::size);
|
return colOpt.map(Collection::size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printOffsets(Map<String, Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>>> offsets) {
|
private void printOffsets(Map<String, Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>>> offsets) {
|
||||||
offsets.forEach((groupId, tuple) -> {
|
offsets.forEach((groupId, tuple) -> {
|
||||||
Optional<ConsumerGroupState> state = tuple.getKey();
|
Optional<GroupState> state = tuple.getKey();
|
||||||
Optional<Collection<PartitionAssignmentState>> assignments = tuple.getValue();
|
Optional<Collection<PartitionAssignmentState>> assignments = tuple.getValue();
|
||||||
|
|
||||||
if (shouldPrintMemberState(groupId, state, size(assignments))) {
|
if (shouldPrintMemberState(groupId, state, size(assignments))) {
|
||||||
|
@ -361,14 +362,14 @@ public class ConsumerGroupCommand {
|
||||||
return "\n%" + (-maxGroupLen) + "s %" + (-maxTopicLen) + "s %-10s %-15s %-15s %-15s %" + (-maxConsumerIdLen) + "s %" + (-maxHostLen) + "s %s";
|
return "\n%" + (-maxGroupLen) + "s %" + (-maxTopicLen) + "s %-10s %-15s %-15s %-15s %" + (-maxConsumerIdLen) + "s %" + (-maxHostLen) + "s %s";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printMembers(Map<String, Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>>> members, boolean verbose) {
|
private void printMembers(Map<String, Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>>> members, boolean verbose) {
|
||||||
members.forEach((groupId, tuple) -> {
|
members.forEach((groupId, tuple) -> {
|
||||||
Optional<ConsumerGroupState> state = tuple.getKey();
|
Optional<GroupState> groupState = tuple.getKey();
|
||||||
Optional<Collection<MemberAssignmentState>> assignments = tuple.getValue();
|
Optional<Collection<MemberAssignmentState>> assignments = tuple.getValue();
|
||||||
int maxGroupLen = 15, maxConsumerIdLen = 15, maxGroupInstanceIdLen = 17, maxHostLen = 15, maxClientIdLen = 15;
|
int maxGroupLen = 15, maxConsumerIdLen = 15, maxGroupInstanceIdLen = 17, maxHostLen = 15, maxClientIdLen = 15;
|
||||||
boolean includeGroupInstanceId = false;
|
boolean includeGroupInstanceId = false;
|
||||||
|
|
||||||
if (shouldPrintMemberState(groupId, state, size(assignments))) {
|
if (shouldPrintMemberState(groupId, groupState, size(assignments))) {
|
||||||
// find proper columns width
|
// find proper columns width
|
||||||
if (assignments.isPresent()) {
|
if (assignments.isPresent()) {
|
||||||
for (MemberAssignmentState memberAssignment : assignments.get()) {
|
for (MemberAssignmentState memberAssignment : assignments.get()) {
|
||||||
|
@ -425,16 +426,16 @@ public class ConsumerGroupCommand {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printStates(Map<String, GroupState> states) {
|
private void printStates(Map<String, GroupInformation> states) {
|
||||||
states.forEach((groupId, state) -> {
|
states.forEach((groupId, state) -> {
|
||||||
if (shouldPrintMemberState(groupId, Optional.of(state.state), Optional.of(1))) {
|
if (shouldPrintMemberState(groupId, Optional.of(state.groupState), Optional.of(1))) {
|
||||||
String coordinator = state.coordinator.host() + ":" + state.coordinator.port() + " (" + state.coordinator.idString() + ")";
|
String coordinator = state.coordinator.host() + ":" + state.coordinator.port() + " (" + state.coordinator.idString() + ")";
|
||||||
int coordinatorColLen = Math.max(25, coordinator.length());
|
int coordinatorColLen = Math.max(25, coordinator.length());
|
||||||
|
|
||||||
String format = "\n%" + -coordinatorColLen + "s %-25s %-20s %-15s %s";
|
String format = "\n%" + -coordinatorColLen + "s %-25s %-20s %-15s %s";
|
||||||
|
|
||||||
System.out.printf(format, "GROUP", "COORDINATOR (ID)", "ASSIGNMENT-STRATEGY", "STATE", "#MEMBERS");
|
System.out.printf(format, "GROUP", "COORDINATOR (ID)", "ASSIGNMENT-STRATEGY", "STATE", "#MEMBERS");
|
||||||
System.out.printf(format, state.group, coordinator, state.assignmentStrategy, state.state, state.numMembers);
|
System.out.printf(format, state.group, coordinator, state.assignmentStrategy, state.groupState, state.numMembers);
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -450,15 +451,15 @@ public class ConsumerGroupCommand {
|
||||||
long subActions = Stream.of(membersOptPresent, offsetsOptPresent, stateOptPresent).filter(x -> x).count();
|
long subActions = Stream.of(membersOptPresent, offsetsOptPresent, stateOptPresent).filter(x -> x).count();
|
||||||
|
|
||||||
if (subActions == 0 || offsetsOptPresent) {
|
if (subActions == 0 || offsetsOptPresent) {
|
||||||
TreeMap<String, Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>>> offsets
|
TreeMap<String, Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>>> offsets
|
||||||
= collectGroupsOffsets(groupIds);
|
= collectGroupsOffsets(groupIds);
|
||||||
printOffsets(offsets);
|
printOffsets(offsets);
|
||||||
} else if (membersOptPresent) {
|
} else if (membersOptPresent) {
|
||||||
TreeMap<String, Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>>> members
|
TreeMap<String, Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>>> members
|
||||||
= collectGroupsMembers(groupIds, opts.options.has(opts.verboseOpt));
|
= collectGroupsMembers(groupIds, opts.options.has(opts.verboseOpt));
|
||||||
printMembers(members, opts.options.has(opts.verboseOpt));
|
printMembers(members, opts.options.has(opts.verboseOpt));
|
||||||
} else {
|
} else {
|
||||||
TreeMap<String, GroupState> states = collectGroupsState(groupIds);
|
TreeMap<String, GroupInformation> states = collectGroupsState(groupIds);
|
||||||
printStates(states);
|
printStates(states);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -530,7 +531,7 @@ public class ConsumerGroupCommand {
|
||||||
|
|
||||||
consumerGroups.forEach((groupId, groupDescription) -> {
|
consumerGroups.forEach((groupId, groupDescription) -> {
|
||||||
try {
|
try {
|
||||||
String state = groupDescription.get().state().toString();
|
String state = groupDescription.get().groupState().toString();
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "Empty":
|
case "Empty":
|
||||||
case "Dead":
|
case "Dead":
|
||||||
|
@ -689,19 +690,19 @@ public class ConsumerGroupCommand {
|
||||||
/**
|
/**
|
||||||
* Returns the state of the specified consumer group and partition assignment states
|
* Returns the state of the specified consumer group and partition assignment states
|
||||||
*/
|
*/
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> collectGroupOffsets(String groupId) throws Exception {
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> collectGroupOffsets(String groupId) throws Exception {
|
||||||
return collectGroupsOffsets(Collections.singletonList(groupId)).getOrDefault(groupId, new SimpleImmutableEntry<>(Optional.empty(), Optional.empty()));
|
return collectGroupsOffsets(Collections.singletonList(groupId)).getOrDefault(groupId, new SimpleImmutableEntry<>(Optional.empty(), Optional.empty()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns states of the specified consumer groups and partition assignment states
|
* Returns states of the specified consumer groups and partition assignment states
|
||||||
*/
|
*/
|
||||||
TreeMap<String, Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>>> collectGroupsOffsets(Collection<String> groupIds) throws Exception {
|
TreeMap<String, Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>>> collectGroupsOffsets(Collection<String> groupIds) throws Exception {
|
||||||
Map<String, ConsumerGroupDescription> consumerGroups = describeConsumerGroups(groupIds);
|
Map<String, ConsumerGroupDescription> consumerGroups = describeConsumerGroups(groupIds);
|
||||||
TreeMap<String, Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>>> groupOffsets = new TreeMap<>();
|
TreeMap<String, Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>>> groupOffsets = new TreeMap<>();
|
||||||
|
|
||||||
consumerGroups.forEach((groupId, consumerGroup) -> {
|
consumerGroups.forEach((groupId, consumerGroup) -> {
|
||||||
ConsumerGroupState state = consumerGroup.state();
|
GroupState state = consumerGroup.groupState();
|
||||||
Map<TopicPartition, OffsetAndMetadata> committedOffsets = getCommittedOffsets(groupId);
|
Map<TopicPartition, OffsetAndMetadata> committedOffsets = getCommittedOffsets(groupId);
|
||||||
// The admin client returns `null` as a value to indicate that there is not committed offset for a partition.
|
// The admin client returns `null` as a value to indicate that there is not committed offset for a partition.
|
||||||
Function<TopicPartition, Optional<Long>> getPartitionOffset = tp -> Optional.ofNullable(committedOffsets.get(tp)).map(OffsetAndMetadata::offset);
|
Function<TopicPartition, Optional<Long>> getPartitionOffset = tp -> Optional.ofNullable(committedOffsets.get(tp)).map(OffsetAndMetadata::offset);
|
||||||
|
@ -746,16 +747,16 @@ public class ConsumerGroupCommand {
|
||||||
return groupOffsets;
|
return groupOffsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> collectGroupMembers(String groupId, boolean verbose) throws Exception {
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> collectGroupMembers(String groupId, boolean verbose) throws Exception {
|
||||||
return collectGroupsMembers(Collections.singleton(groupId), verbose).get(groupId);
|
return collectGroupsMembers(Collections.singleton(groupId), verbose).get(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeMap<String, Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>>> collectGroupsMembers(Collection<String> groupIds, boolean verbose) throws Exception {
|
TreeMap<String, Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>>> collectGroupsMembers(Collection<String> groupIds, boolean verbose) throws Exception {
|
||||||
Map<String, ConsumerGroupDescription> consumerGroups = describeConsumerGroups(groupIds);
|
Map<String, ConsumerGroupDescription> consumerGroups = describeConsumerGroups(groupIds);
|
||||||
TreeMap<String, Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>>> res = new TreeMap<>();
|
TreeMap<String, Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>>> res = new TreeMap<>();
|
||||||
|
|
||||||
consumerGroups.forEach((groupId, consumerGroup) -> {
|
consumerGroups.forEach((groupId, consumerGroup) -> {
|
||||||
ConsumerGroupState state = consumerGroup.state();
|
GroupState state = consumerGroup.groupState();
|
||||||
List<MemberAssignmentState> memberAssignmentStates = consumerGroup.members().stream().map(consumer ->
|
List<MemberAssignmentState> memberAssignmentStates = consumerGroup.members().stream().map(consumer ->
|
||||||
new MemberAssignmentState(
|
new MemberAssignmentState(
|
||||||
groupId,
|
groupId,
|
||||||
|
@ -771,19 +772,19 @@ public class ConsumerGroupCommand {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupState collectGroupState(String groupId) throws Exception {
|
GroupInformation collectGroupState(String groupId) throws Exception {
|
||||||
return collectGroupsState(Collections.singleton(groupId)).get(groupId);
|
return collectGroupsState(Collections.singleton(groupId)).get(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeMap<String, GroupState> collectGroupsState(Collection<String> groupIds) throws Exception {
|
TreeMap<String, GroupInformation> collectGroupsState(Collection<String> groupIds) throws Exception {
|
||||||
Map<String, ConsumerGroupDescription> consumerGroups = describeConsumerGroups(groupIds);
|
Map<String, ConsumerGroupDescription> consumerGroups = describeConsumerGroups(groupIds);
|
||||||
TreeMap<String, GroupState> res = new TreeMap<>();
|
TreeMap<String, GroupInformation> res = new TreeMap<>();
|
||||||
consumerGroups.forEach((groupId, groupDescription) ->
|
consumerGroups.forEach((groupId, groupDescription) ->
|
||||||
res.put(groupId, new GroupState(
|
res.put(groupId, new GroupInformation(
|
||||||
groupId,
|
groupId,
|
||||||
groupDescription.coordinator(),
|
groupDescription.coordinator(),
|
||||||
groupDescription.partitionAssignor(),
|
groupDescription.partitionAssignor(),
|
||||||
groupDescription.state(),
|
groupDescription.groupState(),
|
||||||
groupDescription.members().size()
|
groupDescription.members().size()
|
||||||
)));
|
)));
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -16,21 +16,21 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.kafka.tools.consumer.group;
|
package org.apache.kafka.tools.consumer.group;
|
||||||
|
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
|
|
||||||
class GroupState {
|
class GroupInformation {
|
||||||
final String group;
|
final String group;
|
||||||
final Node coordinator;
|
final Node coordinator;
|
||||||
final String assignmentStrategy;
|
final String assignmentStrategy;
|
||||||
final ConsumerGroupState state;
|
final GroupState groupState;
|
||||||
final int numMembers;
|
final int numMembers;
|
||||||
|
|
||||||
GroupState(String group, Node coordinator, String assignmentStrategy, ConsumerGroupState state, int numMembers) {
|
GroupInformation(String group, Node coordinator, String assignmentStrategy, GroupState groupState, int numMembers) {
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.coordinator = coordinator;
|
this.coordinator = coordinator;
|
||||||
this.assignmentStrategy = assignmentStrategy;
|
this.assignmentStrategy = assignmentStrategy;
|
||||||
this.state = state;
|
this.groupState = groupState;
|
||||||
this.numMembers = numMembers;
|
this.numMembers = numMembers;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,14 +20,15 @@ import org.apache.kafka.clients.CommonClientConfigs;
|
||||||
import org.apache.kafka.clients.admin.AbstractOptions;
|
import org.apache.kafka.clients.admin.AbstractOptions;
|
||||||
import org.apache.kafka.clients.admin.Admin;
|
import org.apache.kafka.clients.admin.Admin;
|
||||||
import org.apache.kafka.clients.admin.DescribeShareGroupsResult;
|
import org.apache.kafka.clients.admin.DescribeShareGroupsResult;
|
||||||
|
import org.apache.kafka.clients.admin.GroupListing;
|
||||||
|
import org.apache.kafka.clients.admin.ListGroupsOptions;
|
||||||
|
import org.apache.kafka.clients.admin.ListGroupsResult;
|
||||||
import org.apache.kafka.clients.admin.ListOffsetsResult;
|
import org.apache.kafka.clients.admin.ListOffsetsResult;
|
||||||
import org.apache.kafka.clients.admin.ListShareGroupsOptions;
|
|
||||||
import org.apache.kafka.clients.admin.ListShareGroupsResult;
|
|
||||||
import org.apache.kafka.clients.admin.MemberDescription;
|
import org.apache.kafka.clients.admin.MemberDescription;
|
||||||
import org.apache.kafka.clients.admin.OffsetSpec;
|
import org.apache.kafka.clients.admin.OffsetSpec;
|
||||||
import org.apache.kafka.clients.admin.ShareGroupDescription;
|
import org.apache.kafka.clients.admin.ShareGroupDescription;
|
||||||
import org.apache.kafka.clients.admin.ShareGroupListing;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.utils.Utils;
|
import org.apache.kafka.common.utils.Utils;
|
||||||
import org.apache.kafka.server.util.CommandLineUtils;
|
import org.apache.kafka.server.util.CommandLineUtils;
|
||||||
|
@ -89,14 +90,13 @@ public class ShareGroupCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Set<ShareGroupState> shareGroupStatesFromString(String input) {
|
static Set<GroupState> groupStatesFromString(String input) {
|
||||||
Set<ShareGroupState> parsedStates =
|
Set<GroupState> parsedStates =
|
||||||
Arrays.stream(input.split(",")).map(s -> ShareGroupState.parse(s.trim())).collect(Collectors.toSet());
|
Arrays.stream(input.split(",")).map(s -> GroupState.parse(s.trim())).collect(Collectors.toSet());
|
||||||
if (parsedStates.contains(ShareGroupState.UNKNOWN)) {
|
Set<GroupState> validStates = GroupState.groupStatesForType(GroupType.SHARE);
|
||||||
Collection<ShareGroupState> validStates =
|
if (!validStates.containsAll(parsedStates)) {
|
||||||
Arrays.stream(ShareGroupState.values()).filter(s -> s != ShareGroupState.UNKNOWN).collect(Collectors.toList());
|
|
||||||
throw new IllegalArgumentException("Invalid state list '" + input + "'. Valid states are: " +
|
throw new IllegalArgumentException("Invalid state list '" + input + "'. Valid states are: " +
|
||||||
validStates.stream().map(Object::toString).collect(Collectors.joining(", ")));
|
validStates.stream().map(GroupState::toString).collect(Collectors.joining(", ")));
|
||||||
}
|
}
|
||||||
return parsedStates;
|
return parsedStates;
|
||||||
}
|
}
|
||||||
|
@ -128,10 +128,10 @@ public class ShareGroupCommand {
|
||||||
public void listGroups() throws ExecutionException, InterruptedException {
|
public void listGroups() throws ExecutionException, InterruptedException {
|
||||||
if (opts.options.has(opts.stateOpt)) {
|
if (opts.options.has(opts.stateOpt)) {
|
||||||
String stateValue = opts.options.valueOf(opts.stateOpt);
|
String stateValue = opts.options.valueOf(opts.stateOpt);
|
||||||
Set<ShareGroupState> states = (stateValue == null || stateValue.isEmpty())
|
Set<GroupState> states = (stateValue == null || stateValue.isEmpty())
|
||||||
? Collections.emptySet()
|
? Collections.emptySet()
|
||||||
: shareGroupStatesFromString(stateValue);
|
: groupStatesFromString(stateValue);
|
||||||
List<ShareGroupListing> listings = listShareGroupsWithState(states);
|
List<GroupListing> listings = listShareGroupsInStates(states);
|
||||||
|
|
||||||
printGroupInfo(listings);
|
printGroupInfo(listings);
|
||||||
} else
|
} else
|
||||||
|
@ -140,31 +140,32 @@ public class ShareGroupCommand {
|
||||||
|
|
||||||
List<String> listShareGroups() {
|
List<String> listShareGroups() {
|
||||||
try {
|
try {
|
||||||
ListShareGroupsResult result = adminClient.listShareGroups(withTimeoutMs(new ListShareGroupsOptions()));
|
ListGroupsResult result = adminClient.listGroups(withTimeoutMs(new ListGroupsOptions()).withTypes(Set.of(GroupType.SHARE)));
|
||||||
Collection<ShareGroupListing> listings = result.all().get();
|
Collection<GroupListing> listings = result.all().get();
|
||||||
return listings.stream().map(ShareGroupListing::groupId).collect(Collectors.toList());
|
return listings.stream().map(GroupListing::groupId).collect(Collectors.toList());
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ShareGroupListing> listShareGroupsWithState(Set<ShareGroupState> states) throws ExecutionException, InterruptedException {
|
List<GroupListing> listShareGroupsInStates(Set<GroupState> states) throws ExecutionException, InterruptedException {
|
||||||
ListShareGroupsOptions listShareGroupsOptions = withTimeoutMs(new ListShareGroupsOptions());
|
ListGroupsOptions listGroupsOptions = withTimeoutMs(new ListGroupsOptions());
|
||||||
listShareGroupsOptions.inStates(states);
|
listGroupsOptions.withTypes(Set.of(GroupType.SHARE));
|
||||||
ListShareGroupsResult result = adminClient.listShareGroups(listShareGroupsOptions);
|
listGroupsOptions.inGroupStates(states);
|
||||||
|
ListGroupsResult result = adminClient.listGroups(listGroupsOptions);
|
||||||
return new ArrayList<>(result.all().get());
|
return new ArrayList<>(result.all().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printGroupInfo(List<ShareGroupListing> groups) {
|
private void printGroupInfo(List<GroupListing> groups) {
|
||||||
// find proper columns width
|
// find proper columns width
|
||||||
int maxGroupLen = 15;
|
int maxGroupLen = 15;
|
||||||
for (ShareGroupListing group : groups) {
|
for (GroupListing group : groups) {
|
||||||
maxGroupLen = Math.max(maxGroupLen, group.groupId().length());
|
maxGroupLen = Math.max(maxGroupLen, group.groupId().length());
|
||||||
}
|
}
|
||||||
System.out.printf("%" + (-maxGroupLen) + "s %s\n", "GROUP", "STATE");
|
System.out.printf("%" + (-maxGroupLen) + "s %s\n", "GROUP", "STATE");
|
||||||
for (ShareGroupListing group : groups) {
|
for (GroupListing group : groups) {
|
||||||
String groupId = group.groupId();
|
String groupId = group.groupId();
|
||||||
String state = group.state().orElse(ShareGroupState.UNKNOWN).toString();
|
String state = group.groupState().orElse(GroupState.UNKNOWN).toString();
|
||||||
System.out.printf("%" + (-maxGroupLen) + "s %s\n", groupId, state);
|
System.out.printf("%" + (-maxGroupLen) + "s %s\n", groupId, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,14 +175,14 @@ public class ShareGroupCommand {
|
||||||
*
|
*
|
||||||
* @return Whether the group detail should be printed
|
* @return Whether the group detail should be printed
|
||||||
*/
|
*/
|
||||||
public static boolean maybePrintEmptyGroupState(String group, ShareGroupState state, int numRows) {
|
public static boolean maybePrintEmptyGroupState(String group, GroupState state, int numRows) {
|
||||||
if (state == ShareGroupState.DEAD) {
|
if (state == GroupState.DEAD) {
|
||||||
printError("Share group '" + group + "' does not exist.", Optional.empty());
|
printError("Share group '" + group + "' does not exist.", Optional.empty());
|
||||||
} else if (state == ShareGroupState.EMPTY) {
|
} else if (state == GroupState.EMPTY) {
|
||||||
System.err.println("\nShare group '" + group + "' has no active members.");
|
System.err.println("\nShare group '" + group + "' has no active members.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return !state.equals(ShareGroupState.DEAD) && numRows > 0;
|
return !state.equals(GroupState.DEAD) && numRows > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void describeGroups() throws ExecutionException, InterruptedException {
|
public void describeGroups() throws ExecutionException, InterruptedException {
|
||||||
|
@ -233,9 +234,9 @@ public class ShareGroupCommand {
|
||||||
|
|
||||||
private void printOffsets(ShareGroupDescription description) throws ExecutionException, InterruptedException {
|
private void printOffsets(ShareGroupDescription description) throws ExecutionException, InterruptedException {
|
||||||
Map<TopicPartition, Long> offsets = getOffsets(description.members());
|
Map<TopicPartition, Long> offsets = getOffsets(description.members());
|
||||||
if (maybePrintEmptyGroupState(description.groupId(), description.state(), offsets.size())) {
|
if (maybePrintEmptyGroupState(description.groupId(), description.groupState(), offsets.size())) {
|
||||||
String fmt = printOffsetFormat(description, offsets);
|
String fmt = printOffsetFormat(description, offsets);
|
||||||
System.out.printf(fmt, "GROUP", "TOPIC", "PARTITION", "OFFSET");
|
System.out.printf(fmt, "GROUP", "TOPIC", "PARTITION", "START-OFFSET");
|
||||||
|
|
||||||
for (Map.Entry<TopicPartition, Long> offset : offsets.entrySet()) {
|
for (Map.Entry<TopicPartition, Long> offset : offsets.entrySet()) {
|
||||||
System.out.printf(fmt, description.groupId(), offset.getKey().topic(), offset.getKey().partition(), offset.getValue());
|
System.out.printf(fmt, description.groupId(), offset.getKey().topic(), offset.getKey().partition(), offset.getValue());
|
||||||
|
@ -253,7 +254,7 @@ public class ShareGroupCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printStates(ShareGroupDescription description) {
|
private void printStates(ShareGroupDescription description) {
|
||||||
maybePrintEmptyGroupState(description.groupId(), description.state(), 1);
|
maybePrintEmptyGroupState(description.groupId(), description.groupState(), 1);
|
||||||
|
|
||||||
int groupLen = Math.max(15, description.groupId().length());
|
int groupLen = Math.max(15, description.groupId().length());
|
||||||
String coordinator = description.coordinator().host() + ":" + description.coordinator().port() + " (" + description.coordinator().idString() + ")";
|
String coordinator = description.coordinator().host() + ":" + description.coordinator().port() + " (" + description.coordinator().idString() + ")";
|
||||||
|
@ -261,14 +262,14 @@ public class ShareGroupCommand {
|
||||||
|
|
||||||
String fmt = "%" + -groupLen + "s %" + -coordinatorLen + "s %-15s %s\n";
|
String fmt = "%" + -groupLen + "s %" + -coordinatorLen + "s %-15s %s\n";
|
||||||
System.out.printf(fmt, "GROUP", "COORDINATOR (ID)", "STATE", "#MEMBERS");
|
System.out.printf(fmt, "GROUP", "COORDINATOR (ID)", "STATE", "#MEMBERS");
|
||||||
System.out.printf(fmt, description.groupId(), coordinator, description.state().toString(), description.members().size());
|
System.out.printf(fmt, description.groupId(), coordinator, description.groupState().toString(), description.members().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printMembers(ShareGroupDescription description) {
|
private void printMembers(ShareGroupDescription description) {
|
||||||
int groupLen = Math.max(15, description.groupId().length());
|
int groupLen = Math.max(15, description.groupId().length());
|
||||||
int maxConsumerIdLen = 15, maxHostLen = 15, maxClientIdLen = 15;
|
int maxConsumerIdLen = 15, maxHostLen = 15, maxClientIdLen = 15;
|
||||||
Collection<MemberDescription> members = description.members();
|
Collection<MemberDescription> members = description.members();
|
||||||
if (maybePrintEmptyGroupState(description.groupId(), description.state(), description.members().size())) {
|
if (maybePrintEmptyGroupState(description.groupId(), description.groupState(), description.members().size())) {
|
||||||
for (MemberDescription member : members) {
|
for (MemberDescription member : members) {
|
||||||
maxConsumerIdLen = Math.max(maxConsumerIdLen, member.consumerId().length());
|
maxConsumerIdLen = Math.max(maxConsumerIdLen, member.consumerId().length());
|
||||||
maxHostLen = Math.max(maxHostLen, member.host().length());
|
maxHostLen = Math.max(maxHostLen, member.host().length());
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.kafka.clients.consumer.GroupProtocol;
|
||||||
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
||||||
import org.apache.kafka.clients.consumer.KafkaShareConsumer;
|
import org.apache.kafka.clients.consumer.KafkaShareConsumer;
|
||||||
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.protocol.Errors;
|
import org.apache.kafka.common.protocol.Errors;
|
||||||
|
@ -198,9 +199,9 @@ public class GroupsCommandTest {
|
||||||
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
||||||
|
|
||||||
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
||||||
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer"),
|
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer"),
|
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("SG", Optional.of(GroupType.SHARE), "share")
|
new GroupListing("SG", Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
);
|
);
|
||||||
when(adminClient.listGroups()).thenReturn(result);
|
when(adminClient.listGroups()).thenReturn(result);
|
||||||
|
|
||||||
|
@ -225,9 +226,9 @@ public class GroupsCommandTest {
|
||||||
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
||||||
|
|
||||||
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
||||||
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer"),
|
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer"),
|
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("SG", Optional.of(GroupType.SHARE), "share")
|
new GroupListing("SG", Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
);
|
);
|
||||||
when(adminClient.listGroups()).thenReturn(result);
|
when(adminClient.listGroups()).thenReturn(result);
|
||||||
|
|
||||||
|
@ -251,9 +252,9 @@ public class GroupsCommandTest {
|
||||||
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
||||||
|
|
||||||
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
||||||
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer"),
|
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer"),
|
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("SG", Optional.of(GroupType.SHARE), "share")
|
new GroupListing("SG", Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
);
|
);
|
||||||
when(adminClient.listGroups()).thenReturn(result);
|
when(adminClient.listGroups()).thenReturn(result);
|
||||||
|
|
||||||
|
@ -276,9 +277,9 @@ public class GroupsCommandTest {
|
||||||
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
||||||
|
|
||||||
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
||||||
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer"),
|
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer"),
|
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("SG", Optional.of(GroupType.SHARE), "share")
|
new GroupListing("SG", Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
);
|
);
|
||||||
when(adminClient.listGroups()).thenReturn(result);
|
when(adminClient.listGroups()).thenReturn(result);
|
||||||
|
|
||||||
|
@ -302,9 +303,9 @@ public class GroupsCommandTest {
|
||||||
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
||||||
|
|
||||||
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
||||||
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer"),
|
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer"),
|
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("SG", Optional.of(GroupType.SHARE), "share")
|
new GroupListing("SG", Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
);
|
);
|
||||||
when(adminClient.listGroups()).thenReturn(result);
|
when(adminClient.listGroups()).thenReturn(result);
|
||||||
|
|
||||||
|
@ -327,9 +328,9 @@ public class GroupsCommandTest {
|
||||||
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
||||||
|
|
||||||
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
||||||
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer"),
|
new GroupListing("CGclassic", Optional.of(GroupType.CLASSIC), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer"),
|
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("SG", Optional.of(GroupType.SHARE), "share")
|
new GroupListing("SG", Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
);
|
);
|
||||||
when(adminClient.listGroups()).thenReturn(result);
|
when(adminClient.listGroups()).thenReturn(result);
|
||||||
|
|
||||||
|
@ -352,8 +353,8 @@ public class GroupsCommandTest {
|
||||||
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
GroupsCommand.GroupsService service = new GroupsCommand.GroupsService(adminClient);
|
||||||
|
|
||||||
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
ListGroupsResult result = AdminClientTestUtils.listGroupsResult(
|
||||||
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer"),
|
new GroupListing("CGconsumer", Optional.of(GroupType.CONSUMER), "consumer", Optional.of(GroupState.STABLE)),
|
||||||
new GroupListing("SG", Optional.of(GroupType.SHARE), "share")
|
new GroupListing("SG", Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
);
|
);
|
||||||
when(adminClient.listGroups()).thenReturn(result);
|
when(adminClient.listGroups()).thenReturn(result);
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.apache.kafka.clients.admin.OffsetSpec;
|
||||||
import org.apache.kafka.clients.admin.TopicDescription;
|
import org.apache.kafka.clients.admin.TopicDescription;
|
||||||
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||||
import org.apache.kafka.clients.consumer.RangeAssignor;
|
import org.apache.kafka.clients.consumer.RangeAssignor;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.KafkaFuture;
|
import org.apache.kafka.common.KafkaFuture;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
|
@ -85,14 +85,14 @@ public class ConsumerGroupServiceTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService groupService = consumerGroupService(args);
|
ConsumerGroupCommand.ConsumerGroupService groupService = consumerGroupService(args);
|
||||||
|
|
||||||
when(admin.describeConsumerGroups(ArgumentMatchers.eq(Collections.singletonList(GROUP)), any()))
|
when(admin.describeConsumerGroups(ArgumentMatchers.eq(Collections.singletonList(GROUP)), any()))
|
||||||
.thenReturn(describeGroupsResult(ConsumerGroupState.STABLE));
|
.thenReturn(describeGroupsResult(GroupState.STABLE));
|
||||||
when(admin.listConsumerGroupOffsets(ArgumentMatchers.eq(listConsumerGroupOffsetsSpec()), any()))
|
when(admin.listConsumerGroupOffsets(ArgumentMatchers.eq(listConsumerGroupOffsetsSpec()), any()))
|
||||||
.thenReturn(listGroupOffsetsResult(GROUP));
|
.thenReturn(listGroupOffsetsResult(GROUP));
|
||||||
when(admin.listOffsets(offsetsArgMatcher(), any()))
|
when(admin.listOffsets(offsetsArgMatcher(), any()))
|
||||||
.thenReturn(listOffsetsResult());
|
.thenReturn(listOffsetsResult());
|
||||||
|
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> statesAndAssignments = groupService.collectGroupOffsets(GROUP);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> statesAndAssignments = groupService.collectGroupOffsets(GROUP);
|
||||||
assertEquals(Optional.of(ConsumerGroupState.STABLE), statesAndAssignments.getKey());
|
assertEquals(Optional.of(GroupState.STABLE), statesAndAssignments.getKey());
|
||||||
assertTrue(statesAndAssignments.getValue().isPresent());
|
assertTrue(statesAndAssignments.getValue().isPresent());
|
||||||
assertEquals(TOPIC_PARTITIONS.size(), statesAndAssignments.getValue().get().size());
|
assertEquals(TOPIC_PARTITIONS.size(), statesAndAssignments.getValue().get().size());
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ public class ConsumerGroupServiceTest {
|
||||||
true,
|
true,
|
||||||
Collections.singleton(new MemberDescription("member1", Optional.of("instance1"), "client1", "host1", new MemberAssignment(assignedTopicPartitions))),
|
Collections.singleton(new MemberDescription("member1", Optional.of("instance1"), "client1", "host1", new MemberAssignment(assignedTopicPartitions))),
|
||||||
RangeAssignor.class.getName(),
|
RangeAssignor.class.getName(),
|
||||||
ConsumerGroupState.STABLE,
|
GroupState.STABLE,
|
||||||
new Node(1, "localhost", 9092));
|
new Node(1, "localhost", 9092));
|
||||||
|
|
||||||
Function<Collection<TopicPartition>, ArgumentMatcher<Map<TopicPartition, OffsetSpec>>> offsetsArgMatcher = expectedPartitions ->
|
Function<Collection<TopicPartition>, ArgumentMatcher<Map<TopicPartition, OffsetSpec>>> offsetsArgMatcher = expectedPartitions ->
|
||||||
|
@ -164,8 +164,8 @@ public class ConsumerGroupServiceTest {
|
||||||
)).thenReturn(new ListOffsetsResult(endOffsets.entrySet().stream().filter(e -> unassignedTopicPartitions.contains(e.getKey()))
|
)).thenReturn(new ListOffsetsResult(endOffsets.entrySet().stream().filter(e -> unassignedTopicPartitions.contains(e.getKey()))
|
||||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue))));
|
.collect(Collectors.toMap(Entry::getKey, Entry::getValue))));
|
||||||
|
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> statesAndAssignments = groupService.collectGroupOffsets(GROUP);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> statesAndAssignments = groupService.collectGroupOffsets(GROUP);
|
||||||
Optional<ConsumerGroupState> state = statesAndAssignments.getKey();
|
Optional<GroupState> state = statesAndAssignments.getKey();
|
||||||
Optional<Collection<PartitionAssignmentState>> assignments = statesAndAssignments.getValue();
|
Optional<Collection<PartitionAssignmentState>> assignments = statesAndAssignments.getValue();
|
||||||
|
|
||||||
Map<TopicPartition, Optional<Long>> returnedOffsets = assignments.map(results ->
|
Map<TopicPartition, Optional<Long>> returnedOffsets = assignments.map(results ->
|
||||||
|
@ -183,7 +183,7 @@ public class ConsumerGroupServiceTest {
|
||||||
expectedOffsets.put(testTopicPartition4, Optional.of(100L));
|
expectedOffsets.put(testTopicPartition4, Optional.of(100L));
|
||||||
expectedOffsets.put(testTopicPartition5, Optional.empty());
|
expectedOffsets.put(testTopicPartition5, Optional.empty());
|
||||||
|
|
||||||
assertEquals(Optional.of(ConsumerGroupState.STABLE), state);
|
assertEquals(Optional.of(GroupState.STABLE), state);
|
||||||
assertEquals(expectedOffsets, returnedOffsets);
|
assertEquals(expectedOffsets, returnedOffsets);
|
||||||
|
|
||||||
verify(admin, times(1)).describeConsumerGroups(ArgumentMatchers.eq(Collections.singletonList(GROUP)), any());
|
verify(admin, times(1)).describeConsumerGroups(ArgumentMatchers.eq(Collections.singletonList(GROUP)), any());
|
||||||
|
@ -203,7 +203,7 @@ public class ConsumerGroupServiceTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService groupService = consumerGroupService(args.toArray(new String[0]));
|
ConsumerGroupCommand.ConsumerGroupService groupService = consumerGroupService(args.toArray(new String[0]));
|
||||||
|
|
||||||
when(admin.describeConsumerGroups(ArgumentMatchers.eq(Collections.singletonList(GROUP)), any()))
|
when(admin.describeConsumerGroups(ArgumentMatchers.eq(Collections.singletonList(GROUP)), any()))
|
||||||
.thenReturn(describeGroupsResult(ConsumerGroupState.DEAD));
|
.thenReturn(describeGroupsResult(GroupState.DEAD));
|
||||||
when(admin.describeTopics(ArgumentMatchers.eq(topicsWithoutPartitionsSpecified), any()))
|
when(admin.describeTopics(ArgumentMatchers.eq(topicsWithoutPartitionsSpecified), any()))
|
||||||
.thenReturn(describeTopicsResult(topicsWithoutPartitionsSpecified));
|
.thenReturn(describeTopicsResult(topicsWithoutPartitionsSpecified));
|
||||||
when(admin.listOffsets(offsetsArgMatcher(), any()))
|
when(admin.listOffsets(offsetsArgMatcher(), any()))
|
||||||
|
@ -227,7 +227,7 @@ public class ConsumerGroupServiceTest {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private DescribeConsumerGroupsResult describeGroupsResult(ConsumerGroupState groupState) {
|
private DescribeConsumerGroupsResult describeGroupsResult(GroupState groupState) {
|
||||||
MemberDescription member1 = new MemberDescription("member1", Optional.of("instance1"), "client1", "host1", null);
|
MemberDescription member1 = new MemberDescription("member1", Optional.of("instance1"), "client1", "host1", null);
|
||||||
ConsumerGroupDescription description = new ConsumerGroupDescription(GROUP,
|
ConsumerGroupDescription description = new ConsumerGroupDescription(GROUP,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.apache.kafka.clients.admin.AdminClientConfig;
|
||||||
import org.apache.kafka.clients.consumer.GroupProtocol;
|
import org.apache.kafka.clients.consumer.GroupProtocol;
|
||||||
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
||||||
import org.apache.kafka.clients.consumer.RangeAssignor;
|
import org.apache.kafka.clients.consumer.RangeAssignor;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.errors.GroupIdNotFoundException;
|
import org.apache.kafka.common.errors.GroupIdNotFoundException;
|
||||||
import org.apache.kafka.common.errors.GroupNotEmptyException;
|
import org.apache.kafka.common.errors.GroupNotEmptyException;
|
||||||
import org.apache.kafka.common.protocol.Errors;
|
import org.apache.kafka.common.protocol.Errors;
|
||||||
|
@ -56,8 +56,8 @@ import static org.apache.kafka.clients.consumer.ConsumerConfig.GROUP_PROTOCOL_CO
|
||||||
import static org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG;
|
import static org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG;
|
||||||
import static org.apache.kafka.clients.consumer.ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG;
|
import static org.apache.kafka.clients.consumer.ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG;
|
||||||
import static org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG;
|
import static org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG;
|
||||||
import static org.apache.kafka.common.ConsumerGroupState.EMPTY;
|
import static org.apache.kafka.common.GroupState.EMPTY;
|
||||||
import static org.apache.kafka.common.ConsumerGroupState.STABLE;
|
import static org.apache.kafka.common.GroupState.STABLE;
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
@ -318,8 +318,8 @@ public class DeleteConsumerGroupsTest {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkGroupState(ConsumerGroupCommand.ConsumerGroupService service, String groupId, ConsumerGroupState state) throws Exception {
|
private boolean checkGroupState(ConsumerGroupCommand.ConsumerGroupService service, String groupId, GroupState state) throws Exception {
|
||||||
return Objects.equals(service.collectGroupState(groupId).state, state);
|
return Objects.equals(service.collectGroupState(groupId).groupState, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConsumerGroupCommand.ConsumerGroupService getConsumerGroupService(String[] args) {
|
private ConsumerGroupCommand.ConsumerGroupService getConsumerGroupService(String[] args) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ import org.apache.kafka.clients.consumer.GroupProtocol;
|
||||||
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
||||||
import org.apache.kafka.clients.consumer.RangeAssignor;
|
import org.apache.kafka.clients.consumer.RangeAssignor;
|
||||||
import org.apache.kafka.clients.consumer.RoundRobinAssignor;
|
import org.apache.kafka.clients.consumer.RoundRobinAssignor;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.errors.TimeoutException;
|
import org.apache.kafka.common.errors.TimeoutException;
|
||||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||||
|
@ -113,8 +113,8 @@ public class DescribeConsumerGroupTest {
|
||||||
// note the group to be queried is a different (non-existing) group
|
// note the group to be queried is a different (non-existing) group
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", missingGroup})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", missingGroup})
|
||||||
) {
|
) {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(missingGroup);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(missingGroup);
|
||||||
assertTrue(res.getKey().map(s -> s.equals(ConsumerGroupState.DEAD)).orElse(false) && res.getValue().map(Collection::isEmpty).orElse(false),
|
assertTrue(res.getKey().map(s -> s.equals(GroupState.DEAD)).orElse(false) && res.getValue().map(Collection::isEmpty).orElse(false),
|
||||||
"Expected the state to be 'Dead', with no members in the group '" + missingGroup + "'.");
|
"Expected the state to be 'Dead', with no members in the group '" + missingGroup + "'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,12 +132,12 @@ public class DescribeConsumerGroupTest {
|
||||||
// note the group to be queried is a different (non-existing) group
|
// note the group to be queried is a different (non-existing) group
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", missingGroup})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", missingGroup})
|
||||||
) {
|
) {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(missingGroup, false);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(missingGroup, false);
|
||||||
assertTrue(res.getKey().map(s -> s.equals(ConsumerGroupState.DEAD)).orElse(false) && res.getValue().map(Collection::isEmpty).orElse(false),
|
assertTrue(res.getKey().map(s -> s.equals(GroupState.DEAD)).orElse(false) && res.getValue().map(Collection::isEmpty).orElse(false),
|
||||||
"Expected the state to be 'Dead', with no members in the group '" + missingGroup + "'.");
|
"Expected the state to be 'Dead', with no members in the group '" + missingGroup + "'.");
|
||||||
|
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res2 = service.collectGroupMembers(missingGroup, true);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res2 = service.collectGroupMembers(missingGroup, true);
|
||||||
assertTrue(res2.getKey().map(s -> s.equals(ConsumerGroupState.DEAD)).orElse(false) && res2.getValue().map(Collection::isEmpty).orElse(false),
|
assertTrue(res2.getKey().map(s -> s.equals(GroupState.DEAD)).orElse(false) && res2.getValue().map(Collection::isEmpty).orElse(false),
|
||||||
"Expected the state to be 'Dead', with no members in the group '" + missingGroup + "' (verbose option).");
|
"Expected the state to be 'Dead', with no members in the group '" + missingGroup + "' (verbose option).");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,8 +155,8 @@ public class DescribeConsumerGroupTest {
|
||||||
// note the group to be queried is a different (non-existing) group
|
// note the group to be queried is a different (non-existing) group
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", missingGroup})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", missingGroup})
|
||||||
) {
|
) {
|
||||||
GroupState state = service.collectGroupState(missingGroup);
|
GroupInformation state = service.collectGroupState(missingGroup);
|
||||||
assertTrue(Objects.equals(state.state, ConsumerGroupState.DEAD) && state.numMembers == 0 &&
|
assertTrue(Objects.equals(state.groupState, GroupState.DEAD) && state.numMembers == 0 &&
|
||||||
state.coordinator != null && clusterInstance.brokerIds().contains(state.coordinator.id()),
|
state.coordinator != null && clusterInstance.brokerIds().contains(state.coordinator.id()),
|
||||||
"Expected the state to be 'Dead', with no members in the group '" + missingGroup + "'."
|
"Expected the state to be 'Dead', with no members in the group '" + missingGroup + "'."
|
||||||
);
|
);
|
||||||
|
@ -277,13 +277,13 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> groupOffsets = service.collectGroupOffsets(group);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> groupOffsets = service.collectGroupOffsets(group);
|
||||||
Optional<ConsumerGroupState> state = groupOffsets.getKey();
|
Optional<GroupState> state = groupOffsets.getKey();
|
||||||
Optional<Collection<PartitionAssignmentState>> assignments = groupOffsets.getValue();
|
Optional<Collection<PartitionAssignmentState>> assignments = groupOffsets.getValue();
|
||||||
|
|
||||||
Predicate<PartitionAssignmentState> isGrp = s -> Objects.equals(s.group, group);
|
Predicate<PartitionAssignmentState> isGrp = s -> Objects.equals(s.group, group);
|
||||||
|
|
||||||
boolean res = state.map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false) &&
|
boolean res = state.map(s -> s.equals(GroupState.STABLE)).orElse(false) &&
|
||||||
assignments.isPresent() &&
|
assignments.isPresent() &&
|
||||||
assignments.get().stream().filter(isGrp).count() == 1;
|
assignments.get().stream().filter(isGrp).count() == 1;
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ public class DescribeConsumerGroupTest {
|
||||||
return consumerGroupDescription.members().size() == 1 && consumerGroupDescription.members().iterator().next().assignment().topicPartitions().size() == 1;
|
return consumerGroupDescription.members().size() == 1 && consumerGroupDescription.members().iterator().next().assignment().topicPartitions().size() == 1;
|
||||||
}, "Expected a 'Stable' group status, rows and valid member information for group " + group + ".");
|
}, "Expected a 'Stable' group status, rows and valid member information for group " + group + ".");
|
||||||
|
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, true);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, true);
|
||||||
|
|
||||||
assertTrue(res.getValue().isPresent());
|
assertTrue(res.getValue().isPresent());
|
||||||
assertTrue(res.getValue().get().size() == 1 && res.getValue().get().iterator().next().assignment.size() == 1,
|
assertTrue(res.getValue().get().size() == 1 && res.getValue().get().iterator().next().assignment.size() == 1,
|
||||||
|
@ -344,8 +344,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
GroupState state = service.collectGroupState(group);
|
GroupInformation state = service.collectGroupState(group);
|
||||||
return Objects.equals(state.state, ConsumerGroupState.STABLE) &&
|
return Objects.equals(state.groupState, GroupState.STABLE) &&
|
||||||
state.numMembers == 1 &&
|
state.numMembers == 1 &&
|
||||||
state.coordinator != null &&
|
state.coordinator != null &&
|
||||||
clusterInstance.brokerIds().contains(state.coordinator.id());
|
clusterInstance.brokerIds().contains(state.coordinator.id());
|
||||||
|
@ -376,8 +376,8 @@ public class DescribeConsumerGroupTest {
|
||||||
|
|
||||||
try (ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})) {
|
try (ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
GroupState state = service.collectGroupState(group);
|
GroupInformation state = service.collectGroupState(group);
|
||||||
return Objects.equals(state.state, ConsumerGroupState.STABLE) &&
|
return Objects.equals(state.groupState, GroupState.STABLE) &&
|
||||||
state.numMembers == 1 &&
|
state.numMembers == 1 &&
|
||||||
Objects.equals(state.assignmentStrategy, expectedName) &&
|
Objects.equals(state.assignmentStrategy, expectedName) &&
|
||||||
state.coordinator != null &&
|
state.coordinator != null &&
|
||||||
|
@ -434,8 +434,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(group);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(group);
|
||||||
return res.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false)
|
return res.getKey().map(s -> s.equals(GroupState.STABLE)).orElse(false)
|
||||||
&& res.getValue().map(c -> c.stream().anyMatch(assignment -> Objects.equals(assignment.group, group) && assignment.offset.isPresent())).orElse(false);
|
&& res.getValue().map(c -> c.stream().anyMatch(assignment -> Objects.equals(assignment.group, group) && assignment.offset.isPresent())).orElse(false);
|
||||||
}, "Expected the group to initially become stable, and to find group in assignments after initial offset commit.");
|
}, "Expected the group to initially become stable, and to find group in assignments after initial offset commit.");
|
||||||
|
|
||||||
|
@ -443,12 +443,12 @@ public class DescribeConsumerGroupTest {
|
||||||
protocolConsumerGroupExecutor.close();
|
protocolConsumerGroupExecutor.close();
|
||||||
|
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> offsets = service.collectGroupOffsets(group);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> offsets = service.collectGroupOffsets(group);
|
||||||
Optional<ConsumerGroupState> state = offsets.getKey();
|
Optional<GroupState> state = offsets.getKey();
|
||||||
Optional<Collection<PartitionAssignmentState>> assignments = offsets.getValue();
|
Optional<Collection<PartitionAssignmentState>> assignments = offsets.getValue();
|
||||||
List<PartitionAssignmentState> testGroupAssignments = assignments.get().stream().filter(a -> Objects.equals(a.group, group)).collect(Collectors.toList());
|
List<PartitionAssignmentState> testGroupAssignments = assignments.get().stream().filter(a -> Objects.equals(a.group, group)).collect(Collectors.toList());
|
||||||
PartitionAssignmentState assignment = testGroupAssignments.get(0);
|
PartitionAssignmentState assignment = testGroupAssignments.get(0);
|
||||||
return state.map(s -> s.equals(ConsumerGroupState.EMPTY)).orElse(false) &&
|
return state.map(s -> s.equals(GroupState.EMPTY)).orElse(false) &&
|
||||||
testGroupAssignments.size() == 1 &&
|
testGroupAssignments.size() == 1 &&
|
||||||
assignment.consumerId.map(c -> c.trim().equals(ConsumerGroupCommand.MISSING_COLUMN_VALUE)).orElse(false) && // the member should be gone
|
assignment.consumerId.map(c -> c.trim().equals(ConsumerGroupCommand.MISSING_COLUMN_VALUE)).orElse(false) && // the member should be gone
|
||||||
assignment.clientId.map(c -> c.trim().equals(ConsumerGroupCommand.MISSING_COLUMN_VALUE)).orElse(false) &&
|
assignment.clientId.map(c -> c.trim().equals(ConsumerGroupCommand.MISSING_COLUMN_VALUE)).orElse(false) &&
|
||||||
|
@ -471,8 +471,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, false);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, false);
|
||||||
return res.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false)
|
return res.getKey().map(s -> s.equals(GroupState.STABLE)).orElse(false)
|
||||||
&& res.getValue().map(c -> c.stream().anyMatch(m -> Objects.equals(m.group, group))).orElse(false);
|
&& res.getValue().map(c -> c.stream().anyMatch(m -> Objects.equals(m.group, group))).orElse(false);
|
||||||
}, "Expected the group to initially become stable, and to find group in assignments after initial offset commit.");
|
}, "Expected the group to initially become stable, and to find group in assignments after initial offset commit.");
|
||||||
|
|
||||||
|
@ -480,8 +480,8 @@ public class DescribeConsumerGroupTest {
|
||||||
protocolConsumerGroupExecutor.close();
|
protocolConsumerGroupExecutor.close();
|
||||||
|
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, false);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, false);
|
||||||
return res.getKey().map(s -> s.equals(ConsumerGroupState.EMPTY)).orElse(false) && res.getValue().isPresent() && res.getValue().get().isEmpty();
|
return res.getKey().map(s -> s.equals(GroupState.EMPTY)).orElse(false) && res.getValue().isPresent() && res.getValue().get().isEmpty();
|
||||||
}, "Expected no member in describe group members results for group '" + group + "'");
|
}, "Expected no member in describe group members results for group '" + group + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,8 +500,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
GroupState state = service.collectGroupState(group);
|
GroupInformation state = service.collectGroupState(group);
|
||||||
return Objects.equals(state.state, ConsumerGroupState.STABLE) &&
|
return Objects.equals(state.groupState, GroupState.STABLE) &&
|
||||||
state.numMembers == 1 &&
|
state.numMembers == 1 &&
|
||||||
state.coordinator != null &&
|
state.coordinator != null &&
|
||||||
clusterInstance.brokerIds().contains(state.coordinator.id());
|
clusterInstance.brokerIds().contains(state.coordinator.id());
|
||||||
|
@ -511,8 +511,8 @@ public class DescribeConsumerGroupTest {
|
||||||
protocolConsumerGroupExecutor.close();
|
protocolConsumerGroupExecutor.close();
|
||||||
|
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
GroupState state = service.collectGroupState(group);
|
GroupInformation state = service.collectGroupState(group);
|
||||||
return Objects.equals(state.state, ConsumerGroupState.EMPTY) && state.numMembers == 0;
|
return Objects.equals(state.groupState, GroupState.EMPTY) && state.numMembers == 0;
|
||||||
}, "Expected the group to become empty after the only member leaving.");
|
}, "Expected the group to become empty after the only member leaving.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -556,8 +556,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(group);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(group);
|
||||||
return res.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).isPresent() &&
|
return res.getKey().map(s -> s.equals(GroupState.STABLE)).isPresent() &&
|
||||||
res.getValue().isPresent() &&
|
res.getValue().isPresent() &&
|
||||||
res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 1 &&
|
res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 1 &&
|
||||||
res.getValue().get().stream().filter(x -> Objects.equals(x.group, group) && x.partition.isPresent()).count() == 1;
|
res.getValue().get().stream().filter(x -> Objects.equals(x.group, group) && x.partition.isPresent()).count() == 1;
|
||||||
|
@ -579,8 +579,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, false);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, false);
|
||||||
return res.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false) &&
|
return res.getKey().map(s -> s.equals(GroupState.STABLE)).orElse(false) &&
|
||||||
res.getValue().isPresent() &&
|
res.getValue().isPresent() &&
|
||||||
res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 2 &&
|
res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 2 &&
|
||||||
res.getValue().get().stream().filter(x -> Objects.equals(x.group, group) && x.numPartitions == 1).count() == 1 &&
|
res.getValue().get().stream().filter(x -> Objects.equals(x.group, group) && x.numPartitions == 1).count() == 1 &&
|
||||||
|
@ -588,8 +588,8 @@ public class DescribeConsumerGroupTest {
|
||||||
res.getValue().get().stream().allMatch(s -> s.assignment.isEmpty());
|
res.getValue().get().stream().allMatch(s -> s.assignment.isEmpty());
|
||||||
}, "Expected rows for consumers with no assigned partitions in describe group results");
|
}, "Expected rows for consumers with no assigned partitions in describe group results");
|
||||||
|
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, true);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, true);
|
||||||
assertTrue(res.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false)
|
assertTrue(res.getKey().map(s -> s.equals(GroupState.STABLE)).orElse(false)
|
||||||
&& res.getValue().map(c -> c.stream().anyMatch(s -> !s.assignment.isEmpty())).orElse(false),
|
&& res.getValue().map(c -> c.stream().anyMatch(s -> !s.assignment.isEmpty())).orElse(false),
|
||||||
"Expected additional columns in verbose version of describe members");
|
"Expected additional columns in verbose version of describe members");
|
||||||
}
|
}
|
||||||
|
@ -609,8 +609,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
GroupState state = service.collectGroupState(group);
|
GroupInformation state = service.collectGroupState(group);
|
||||||
return Objects.equals(state.state, ConsumerGroupState.STABLE) && state.numMembers == 2;
|
return Objects.equals(state.groupState, GroupState.STABLE) && state.numMembers == 2;
|
||||||
}, "Expected two consumers in describe group results");
|
}, "Expected two consumers in describe group results");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -654,8 +654,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(group);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(group);
|
||||||
return res.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false) &&
|
return res.getKey().map(s -> s.equals(GroupState.STABLE)).orElse(false) &&
|
||||||
res.getValue().isPresent() &&
|
res.getValue().isPresent() &&
|
||||||
res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 2 &&
|
res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 2 &&
|
||||||
res.getValue().get().stream().filter(x -> Objects.equals(x.group, group) && x.partition.isPresent()).count() == 2 &&
|
res.getValue().get().stream().filter(x -> Objects.equals(x.group, group) && x.partition.isPresent()).count() == 2 &&
|
||||||
|
@ -678,16 +678,16 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, false);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, false);
|
||||||
return res.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false) &&
|
return res.getKey().map(s -> s.equals(GroupState.STABLE)).orElse(false) &&
|
||||||
res.getValue().isPresent() &&
|
res.getValue().isPresent() &&
|
||||||
res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 2 &&
|
res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 2 &&
|
||||||
res.getValue().get().stream().filter(x -> Objects.equals(x.group, group) && x.numPartitions == 1).count() == 2 &&
|
res.getValue().get().stream().filter(x -> Objects.equals(x.group, group) && x.numPartitions == 1).count() == 2 &&
|
||||||
res.getValue().get().stream().noneMatch(x -> Objects.equals(x.group, group) && x.numPartitions == 0);
|
res.getValue().get().stream().noneMatch(x -> Objects.equals(x.group, group) && x.numPartitions == 0);
|
||||||
}, "Expected two rows (one row per consumer) in describe group members results.");
|
}, "Expected two rows (one row per consumer) in describe group members results.");
|
||||||
|
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, true);
|
Entry<Optional<GroupState>, Optional<Collection<MemberAssignmentState>>> res = service.collectGroupMembers(group, true);
|
||||||
assertTrue(res.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false) && res.getValue().map(s -> s.stream().filter(x -> x.assignment.isEmpty()).count()).orElse(0L) == 0,
|
assertTrue(res.getKey().map(s -> s.equals(GroupState.STABLE)).orElse(false) && res.getValue().map(s -> s.stream().filter(x -> x.assignment.isEmpty()).count()).orElse(0L) == 0,
|
||||||
"Expected additional columns in verbose version of describe members");
|
"Expected additional columns in verbose version of describe members");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -706,8 +706,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
GroupState state = service.collectGroupState(group);
|
GroupInformation state = service.collectGroupState(group);
|
||||||
return Objects.equals(state.state, ConsumerGroupState.STABLE) && Objects.equals(state.group, group) && state.numMembers == 2;
|
return Objects.equals(state.groupState, GroupState.STABLE) && Objects.equals(state.group, group) && state.numMembers == 2;
|
||||||
}, "Expected a stable group with two members in describe group state result.");
|
}, "Expected a stable group with two members in describe group state result.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -726,8 +726,8 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(group);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> res = service.collectGroupOffsets(group);
|
||||||
return res.getKey().map(s -> s.equals(ConsumerGroupState.EMPTY)).orElse(false)
|
return res.getKey().map(s -> s.equals(GroupState.EMPTY)).orElse(false)
|
||||||
&& res.getValue().isPresent() && res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 2;
|
&& res.getValue().isPresent() && res.getValue().get().stream().filter(s -> Objects.equals(s.group, group)).count() == 2;
|
||||||
}, "Expected a stable group with two members in describe group state result.");
|
}, "Expected a stable group with two members in describe group state result.");
|
||||||
}
|
}
|
||||||
|
@ -841,11 +841,11 @@ public class DescribeConsumerGroupTest {
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
ConsumerGroupCommand.ConsumerGroupService service = consumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--describe", "--group", group})
|
||||||
) {
|
) {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
Entry<Optional<ConsumerGroupState>, Optional<Collection<PartitionAssignmentState>>> groupOffsets = service.collectGroupOffsets(group);
|
Entry<Optional<GroupState>, Optional<Collection<PartitionAssignmentState>>> groupOffsets = service.collectGroupOffsets(group);
|
||||||
|
|
||||||
Predicate<PartitionAssignmentState> isGrp = s -> Objects.equals(s.group, group);
|
Predicate<PartitionAssignmentState> isGrp = s -> Objects.equals(s.group, group);
|
||||||
|
|
||||||
boolean res = groupOffsets.getKey().map(s -> s.equals(ConsumerGroupState.STABLE)).orElse(false) &&
|
boolean res = groupOffsets.getKey().map(s -> s.equals(GroupState.STABLE)).orElse(false) &&
|
||||||
groupOffsets.getValue().isPresent() &&
|
groupOffsets.getValue().isPresent() &&
|
||||||
groupOffsets.getValue().get().stream().filter(isGrp).count() == 1;
|
groupOffsets.getValue().get().stream().filter(isGrp).count() == 1;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import org.apache.kafka.clients.admin.NewTopic;
|
||||||
import org.apache.kafka.clients.consumer.GroupProtocol;
|
import org.apache.kafka.clients.consumer.GroupProtocol;
|
||||||
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
||||||
import org.apache.kafka.clients.consumer.RangeAssignor;
|
import org.apache.kafka.clients.consumer.RangeAssignor;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.GroupType;
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||||
|
@ -98,7 +98,7 @@ public class ListConsumerGroupTest {
|
||||||
try (AutoCloseable topicPartitionsConsumerGroupExecutor = consumerGroupClosable(topicPartitionsGroup, Collections.singleton(new TopicPartition(topic, 0)));
|
try (AutoCloseable topicPartitionsConsumerGroupExecutor = consumerGroupClosable(topicPartitionsGroup, Collections.singleton(new TopicPartition(topic, 0)));
|
||||||
AutoCloseable topicConsumerGroupExecutor = consumerGroupClosable(GroupProtocol.CLASSIC, topicGroup, topic);
|
AutoCloseable topicConsumerGroupExecutor = consumerGroupClosable(GroupProtocol.CLASSIC, topicGroup, topic);
|
||||||
AutoCloseable protocolConsumerGroupExecutor = consumerGroupClosable(groupProtocol, protocolGroup, topic);
|
AutoCloseable protocolConsumerGroupExecutor = consumerGroupClosable(groupProtocol, protocolGroup, topic);
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = getConsumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--list"});
|
ConsumerGroupCommand.ConsumerGroupService service = getConsumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--list"})
|
||||||
) {
|
) {
|
||||||
Set<String> expectedGroups = set(Arrays.asList(topicPartitionsGroup, topicGroup, protocolGroup));
|
Set<String> expectedGroups = set(Arrays.asList(topicPartitionsGroup, topicGroup, protocolGroup));
|
||||||
final AtomicReference<Set> foundGroups = new AtomicReference<>();
|
final AtomicReference<Set> foundGroups = new AtomicReference<>();
|
||||||
|
@ -131,50 +131,50 @@ public class ListConsumerGroupTest {
|
||||||
|
|
||||||
try (AutoCloseable topicPartitionsConsumerGroupExecutor = consumerGroupClosable(topicPartitionsGroup, Collections.singleton(new TopicPartition(topic, 0)));
|
try (AutoCloseable topicPartitionsConsumerGroupExecutor = consumerGroupClosable(topicPartitionsGroup, Collections.singleton(new TopicPartition(topic, 0)));
|
||||||
AutoCloseable protocolConsumerGroupExecutor = consumerGroupClosable(groupProtocol, protocolGroup, topic);
|
AutoCloseable protocolConsumerGroupExecutor = consumerGroupClosable(groupProtocol, protocolGroup, topic);
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = getConsumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--list", "--state"});
|
ConsumerGroupCommand.ConsumerGroupService service = getConsumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--list", "--state"})
|
||||||
) {
|
) {
|
||||||
Set<ConsumerGroupListing> expectedListing = Set.of(
|
Set<ConsumerGroupListing> expectedListing = Set.of(
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
topicPartitionsGroup,
|
topicPartitionsGroup,
|
||||||
true,
|
Optional.of(GroupState.EMPTY),
|
||||||
Optional.of(ConsumerGroupState.EMPTY),
|
Optional.of(GroupType.CLASSIC),
|
||||||
Optional.of(GroupType.CLASSIC)
|
true
|
||||||
),
|
),
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
protocolGroup,
|
protocolGroup,
|
||||||
false,
|
Optional.of(GroupState.STABLE),
|
||||||
Optional.of(ConsumerGroupState.STABLE),
|
Optional.of(GroupType.parse(groupProtocol.name())),
|
||||||
Optional.of(GroupType.parse(groupProtocol.name()))
|
false
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
assertGroupListing(
|
assertGroupListing(
|
||||||
service,
|
service,
|
||||||
Collections.emptySet(),
|
Collections.emptySet(),
|
||||||
EnumSet.allOf(ConsumerGroupState.class),
|
EnumSet.allOf(GroupState.class),
|
||||||
expectedListing
|
expectedListing
|
||||||
);
|
);
|
||||||
|
|
||||||
expectedListing = Set.of(
|
expectedListing = Set.of(
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
protocolGroup,
|
protocolGroup,
|
||||||
false,
|
Optional.of(GroupState.STABLE),
|
||||||
Optional.of(ConsumerGroupState.STABLE),
|
Optional.of(GroupType.parse(groupProtocol.name())),
|
||||||
Optional.of(GroupType.parse(groupProtocol.name()))
|
false
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
assertGroupListing(
|
assertGroupListing(
|
||||||
service,
|
service,
|
||||||
Collections.emptySet(),
|
Collections.emptySet(),
|
||||||
Set.of(ConsumerGroupState.STABLE),
|
Set.of(GroupState.STABLE),
|
||||||
expectedListing
|
expectedListing
|
||||||
);
|
);
|
||||||
|
|
||||||
assertGroupListing(
|
assertGroupListing(
|
||||||
service,
|
service,
|
||||||
Collections.emptySet(),
|
Collections.emptySet(),
|
||||||
Set.of(ConsumerGroupState.PREPARING_REBALANCE),
|
Set.of(GroupState.PREPARING_REBALANCE),
|
||||||
Collections.emptySet()
|
Collections.emptySet()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -194,20 +194,20 @@ public class ListConsumerGroupTest {
|
||||||
|
|
||||||
try (AutoCloseable topicPartitionsConsumerGroupExecutor = consumerGroupClosable(topicPartitionsGroup, Collections.singleton(new TopicPartition(topic, 0)));
|
try (AutoCloseable topicPartitionsConsumerGroupExecutor = consumerGroupClosable(topicPartitionsGroup, Collections.singleton(new TopicPartition(topic, 0)));
|
||||||
AutoCloseable protocolConsumerGroupExecutor = consumerGroupClosable(groupProtocol, protocolGroup, topic);
|
AutoCloseable protocolConsumerGroupExecutor = consumerGroupClosable(groupProtocol, protocolGroup, topic);
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = getConsumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--list", "--state"});
|
ConsumerGroupCommand.ConsumerGroupService service = getConsumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--list", "--state"})
|
||||||
) {
|
) {
|
||||||
Set<ConsumerGroupListing> expectedListing = Set.of(
|
Set<ConsumerGroupListing> expectedListing = Set.of(
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
topicPartitionsGroup,
|
topicPartitionsGroup,
|
||||||
true,
|
Optional.of(GroupState.EMPTY),
|
||||||
Optional.of(ConsumerGroupState.EMPTY),
|
Optional.of(GroupType.CLASSIC),
|
||||||
Optional.of(GroupType.CLASSIC)
|
true
|
||||||
),
|
),
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
protocolGroup,
|
protocolGroup,
|
||||||
false,
|
Optional.of(GroupState.STABLE),
|
||||||
Optional.of(ConsumerGroupState.STABLE),
|
Optional.of(GroupType.CLASSIC),
|
||||||
Optional.of(GroupType.CLASSIC)
|
false
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ public class ListConsumerGroupTest {
|
||||||
try (AutoCloseable topicPartitionsConsumerGroupExecutor = consumerGroupClosable(topicPartitionsGroup, Collections.singleton(new TopicPartition(topic, 0)));
|
try (AutoCloseable topicPartitionsConsumerGroupExecutor = consumerGroupClosable(topicPartitionsGroup, Collections.singleton(new TopicPartition(topic, 0)));
|
||||||
AutoCloseable topicConsumerGroupExecutor = consumerGroupClosable(GroupProtocol.CLASSIC, topicGroup, topic);
|
AutoCloseable topicConsumerGroupExecutor = consumerGroupClosable(GroupProtocol.CLASSIC, topicGroup, topic);
|
||||||
AutoCloseable protocolConsumerGroupExecutor = consumerGroupClosable(groupProtocol, protocolGroup, topic);
|
AutoCloseable protocolConsumerGroupExecutor = consumerGroupClosable(groupProtocol, protocolGroup, topic);
|
||||||
ConsumerGroupCommand.ConsumerGroupService service = getConsumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--list"});
|
ConsumerGroupCommand.ConsumerGroupService service = getConsumerGroupService(new String[]{"--bootstrap-server", clusterInstance.bootstrapServers(), "--list"})
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
@ -258,21 +258,21 @@ public class ListConsumerGroupTest {
|
||||||
Set<ConsumerGroupListing> expectedListing = Set.of(
|
Set<ConsumerGroupListing> expectedListing = Set.of(
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
topicPartitionsGroup,
|
topicPartitionsGroup,
|
||||||
true,
|
Optional.of(GroupState.EMPTY),
|
||||||
Optional.of(ConsumerGroupState.EMPTY),
|
Optional.of(GroupType.CLASSIC),
|
||||||
Optional.of(GroupType.CLASSIC)
|
true
|
||||||
),
|
),
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
topicGroup,
|
topicGroup,
|
||||||
false,
|
Optional.of(GroupState.STABLE),
|
||||||
Optional.of(ConsumerGroupState.STABLE),
|
Optional.of(GroupType.CLASSIC),
|
||||||
Optional.of(GroupType.CLASSIC)
|
false
|
||||||
),
|
),
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
protocolGroup,
|
protocolGroup,
|
||||||
false,
|
Optional.of(GroupState.STABLE),
|
||||||
Optional.of(ConsumerGroupState.STABLE),
|
Optional.of(GroupType.CONSUMER),
|
||||||
Optional.of(GroupType.CONSUMER)
|
false
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -288,9 +288,9 @@ public class ListConsumerGroupTest {
|
||||||
expectedListing = Set.of(
|
expectedListing = Set.of(
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
protocolGroup,
|
protocolGroup,
|
||||||
false,
|
Optional.of(GroupState.STABLE),
|
||||||
Optional.of(ConsumerGroupState.STABLE),
|
Optional.of(GroupType.CONSUMER),
|
||||||
Optional.of(GroupType.CONSUMER)
|
false
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -304,15 +304,15 @@ public class ListConsumerGroupTest {
|
||||||
expectedListing = Set.of(
|
expectedListing = Set.of(
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
topicPartitionsGroup,
|
topicPartitionsGroup,
|
||||||
true,
|
Optional.of(GroupState.EMPTY),
|
||||||
Optional.of(ConsumerGroupState.EMPTY),
|
Optional.of(GroupType.CLASSIC),
|
||||||
Optional.of(GroupType.CLASSIC)
|
true
|
||||||
),
|
),
|
||||||
new ConsumerGroupListing(
|
new ConsumerGroupListing(
|
||||||
topicGroup,
|
topicGroup,
|
||||||
false,
|
Optional.of(GroupState.STABLE),
|
||||||
Optional.of(ConsumerGroupState.STABLE),
|
Optional.of(GroupType.CLASSIC),
|
||||||
Optional.of(GroupType.CLASSIC)
|
false
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -548,20 +548,20 @@ public class ListConsumerGroupTest {
|
||||||
/**
|
/**
|
||||||
* Validates the consumer group listings returned against expected values using specified filters.
|
* Validates the consumer group listings returned against expected values using specified filters.
|
||||||
*
|
*
|
||||||
* @param service The service to list consumer groups.
|
* @param service The service to list consumer groups.
|
||||||
* @param typeFilterSet Filters for group types, empty for no filter.
|
* @param typeFilterSet Filters for group types, empty for no filter.
|
||||||
* @param stateFilterSet Filters for group states, empty for no filter.
|
* @param groupStateFilterSet Filters for group states, empty for no filter.
|
||||||
* @param expectedListing Expected consumer group listings.
|
* @param expectedListing Expected consumer group listings.
|
||||||
*/
|
*/
|
||||||
private static void assertGroupListing(
|
private static void assertGroupListing(
|
||||||
ConsumerGroupCommand.ConsumerGroupService service,
|
ConsumerGroupCommand.ConsumerGroupService service,
|
||||||
Set<GroupType> typeFilterSet,
|
Set<GroupType> typeFilterSet,
|
||||||
Set<ConsumerGroupState> stateFilterSet,
|
Set<GroupState> groupStateFilterSet,
|
||||||
Set<ConsumerGroupListing> expectedListing
|
Set<ConsumerGroupListing> expectedListing
|
||||||
) throws Exception {
|
) throws Exception {
|
||||||
final AtomicReference<Set<ConsumerGroupListing>> foundListing = new AtomicReference<>();
|
final AtomicReference<Set<ConsumerGroupListing>> foundListing = new AtomicReference<>();
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
foundListing.set(set(service.listConsumerGroupsWithFilters(set(typeFilterSet), set(stateFilterSet))));
|
foundListing.set(set(service.listConsumerGroupsWithFilters(set(typeFilterSet), set(groupStateFilterSet))));
|
||||||
return Objects.equals(set(expectedListing), foundListing.get());
|
return Objects.equals(set(expectedListing), foundListing.get());
|
||||||
}, () -> "Expected to show groups " + expectedListing + ", but found " + foundListing.get() + ".");
|
}, () -> "Expected to show groups " + expectedListing + ", but found " + foundListing.get() + ".");
|
||||||
}
|
}
|
||||||
|
@ -614,29 +614,29 @@ public class ListConsumerGroupTest {
|
||||||
class ListConsumerGroupUnitTest {
|
class ListConsumerGroupUnitTest {
|
||||||
@Test
|
@Test
|
||||||
public void testConsumerGroupStatesFromString() {
|
public void testConsumerGroupStatesFromString() {
|
||||||
Set<ConsumerGroupState> result = ConsumerGroupCommand.consumerGroupStatesFromString("Stable");
|
Set<GroupState> result = ConsumerGroupCommand.groupStatesFromString("Stable");
|
||||||
Assertions.assertEquals(ListConsumerGroupTest.set(Collections.singleton(ConsumerGroupState.STABLE)), result);
|
Assertions.assertEquals(ListConsumerGroupTest.set(Collections.singleton(GroupState.STABLE)), result);
|
||||||
|
|
||||||
result = ConsumerGroupCommand.consumerGroupStatesFromString("Stable, PreparingRebalance");
|
result = ConsumerGroupCommand.groupStatesFromString("Stable, PreparingRebalance");
|
||||||
Assertions.assertEquals(ListConsumerGroupTest.set(Arrays.asList(ConsumerGroupState.STABLE, ConsumerGroupState.PREPARING_REBALANCE)), result);
|
Assertions.assertEquals(ListConsumerGroupTest.set(Arrays.asList(GroupState.STABLE, GroupState.PREPARING_REBALANCE)), result);
|
||||||
|
|
||||||
result = ConsumerGroupCommand.consumerGroupStatesFromString("Dead,CompletingRebalance,");
|
result = ConsumerGroupCommand.groupStatesFromString("Dead,CompletingRebalance,");
|
||||||
Assertions.assertEquals(ListConsumerGroupTest.set(Arrays.asList(ConsumerGroupState.DEAD, ConsumerGroupState.COMPLETING_REBALANCE)), result);
|
Assertions.assertEquals(ListConsumerGroupTest.set(Arrays.asList(GroupState.DEAD, GroupState.COMPLETING_REBALANCE)), result);
|
||||||
|
|
||||||
result = ConsumerGroupCommand.consumerGroupStatesFromString("stable");
|
result = ConsumerGroupCommand.groupStatesFromString("stable");
|
||||||
Assertions.assertEquals(ListConsumerGroupTest.set(Collections.singletonList(ConsumerGroupState.STABLE)), result);
|
Assertions.assertEquals(ListConsumerGroupTest.set(Collections.singletonList(GroupState.STABLE)), result);
|
||||||
|
|
||||||
result = ConsumerGroupCommand.consumerGroupStatesFromString("stable, assigning");
|
result = ConsumerGroupCommand.groupStatesFromString("stable, assigning");
|
||||||
Assertions.assertEquals(ListConsumerGroupTest.set(Arrays.asList(ConsumerGroupState.STABLE, ConsumerGroupState.ASSIGNING)), result);
|
Assertions.assertEquals(ListConsumerGroupTest.set(Arrays.asList(GroupState.STABLE, GroupState.ASSIGNING)), result);
|
||||||
|
|
||||||
result = ConsumerGroupCommand.consumerGroupStatesFromString("dead,reconciling,");
|
result = ConsumerGroupCommand.groupStatesFromString("dead,reconciling,");
|
||||||
Assertions.assertEquals(ListConsumerGroupTest.set(Arrays.asList(ConsumerGroupState.DEAD, ConsumerGroupState.RECONCILING)), result);
|
Assertions.assertEquals(ListConsumerGroupTest.set(Arrays.asList(GroupState.DEAD, GroupState.RECONCILING)), result);
|
||||||
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class, () -> ConsumerGroupCommand.consumerGroupStatesFromString("bad, wrong"));
|
Assertions.assertThrows(IllegalArgumentException.class, () -> ConsumerGroupCommand.groupStatesFromString("bad, wrong"));
|
||||||
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class, () -> ConsumerGroupCommand.consumerGroupStatesFromString(" bad, Stable"));
|
Assertions.assertThrows(IllegalArgumentException.class, () -> ConsumerGroupCommand.groupStatesFromString(" bad, Stable"));
|
||||||
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class, () -> ConsumerGroupCommand.consumerGroupStatesFromString(" , ,"));
|
Assertions.assertThrows(IllegalArgumentException.class, () -> ConsumerGroupCommand.groupStatesFromString(" , ,"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.apache.kafka.clients.consumer.RangeAssignor;
|
||||||
import org.apache.kafka.clients.producer.KafkaProducer;
|
import org.apache.kafka.clients.producer.KafkaProducer;
|
||||||
import org.apache.kafka.clients.producer.Producer;
|
import org.apache.kafka.clients.producer.Producer;
|
||||||
import org.apache.kafka.clients.producer.ProducerRecord;
|
import org.apache.kafka.clients.producer.ProducerRecord;
|
||||||
import org.apache.kafka.common.ConsumerGroupState;
|
import org.apache.kafka.common.GroupState;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.serialization.ByteArraySerializer;
|
import org.apache.kafka.common.serialization.ByteArraySerializer;
|
||||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||||
|
@ -834,10 +834,10 @@ public class ResetConsumerGroupOffsetTest {
|
||||||
private void awaitConsumerGroupInactive(ConsumerGroupCommand.ConsumerGroupService service,
|
private void awaitConsumerGroupInactive(ConsumerGroupCommand.ConsumerGroupService service,
|
||||||
String group) throws Exception {
|
String group) throws Exception {
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
ConsumerGroupState state = service.collectGroupState(group).state;
|
GroupState state = service.collectGroupState(group).groupState;
|
||||||
return Objects.equals(state, ConsumerGroupState.EMPTY) || Objects.equals(state, ConsumerGroupState.DEAD);
|
return Objects.equals(state, GroupState.EMPTY) || Objects.equals(state, GroupState.DEAD);
|
||||||
}, "Expected that consumer group is inactive. Actual state: " +
|
}, "Expected that consumer group is inactive. Actual state: " +
|
||||||
service.collectGroupState(group).state);
|
service.collectGroupState(group).groupState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetAndAssertOffsetsCommitted(ClusterInstance cluster,
|
private void resetAndAssertOffsetsCommitted(ClusterInstance cluster,
|
||||||
|
|
|
@ -18,18 +18,19 @@ package org.apache.kafka.tools.consumer.group;
|
||||||
|
|
||||||
import org.apache.kafka.clients.admin.Admin;
|
import org.apache.kafka.clients.admin.Admin;
|
||||||
import org.apache.kafka.clients.admin.DescribeShareGroupsResult;
|
import org.apache.kafka.clients.admin.DescribeShareGroupsResult;
|
||||||
|
import org.apache.kafka.clients.admin.GroupListing;
|
||||||
import org.apache.kafka.clients.admin.KafkaAdminClient;
|
import org.apache.kafka.clients.admin.KafkaAdminClient;
|
||||||
|
import org.apache.kafka.clients.admin.ListGroupsOptions;
|
||||||
|
import org.apache.kafka.clients.admin.ListGroupsResult;
|
||||||
import org.apache.kafka.clients.admin.ListOffsetsResult;
|
import org.apache.kafka.clients.admin.ListOffsetsResult;
|
||||||
import org.apache.kafka.clients.admin.ListShareGroupsOptions;
|
|
||||||
import org.apache.kafka.clients.admin.ListShareGroupsResult;
|
|
||||||
import org.apache.kafka.clients.admin.MemberAssignment;
|
import org.apache.kafka.clients.admin.MemberAssignment;
|
||||||
import org.apache.kafka.clients.admin.MemberDescription;
|
import org.apache.kafka.clients.admin.MemberDescription;
|
||||||
import org.apache.kafka.clients.admin.MockAdminClient;
|
import org.apache.kafka.clients.admin.MockAdminClient;
|
||||||
import org.apache.kafka.clients.admin.ShareGroupDescription;
|
import org.apache.kafka.clients.admin.ShareGroupDescription;
|
||||||
import org.apache.kafka.clients.admin.ShareGroupListing;
|
import org.apache.kafka.common.GroupState;
|
||||||
|
import org.apache.kafka.common.GroupType;
|
||||||
import org.apache.kafka.common.KafkaFuture;
|
import org.apache.kafka.common.KafkaFuture;
|
||||||
import org.apache.kafka.common.Node;
|
import org.apache.kafka.common.Node;
|
||||||
import org.apache.kafka.common.ShareGroupState;
|
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.test.TestUtils;
|
import org.apache.kafka.test.TestUtils;
|
||||||
import org.apache.kafka.tools.consumer.group.ShareGroupCommand.ShareGroupService;
|
import org.apache.kafka.tools.consumer.group.ShareGroupCommand.ShareGroupService;
|
||||||
|
@ -66,12 +67,12 @@ public class ShareGroupCommandTest {
|
||||||
|
|
||||||
String[] cgcArgs = new String[]{"--bootstrap-server", bootstrapServer, "--list"};
|
String[] cgcArgs = new String[]{"--bootstrap-server", bootstrapServer, "--list"};
|
||||||
Admin adminClient = mock(KafkaAdminClient.class);
|
Admin adminClient = mock(KafkaAdminClient.class);
|
||||||
ListShareGroupsResult result = mock(ListShareGroupsResult.class);
|
ListGroupsResult result = mock(ListGroupsResult.class);
|
||||||
when(result.all()).thenReturn(KafkaFuture.completedFuture(Arrays.asList(
|
when(result.all()).thenReturn(KafkaFuture.completedFuture(Arrays.asList(
|
||||||
new ShareGroupListing(firstGroup, Optional.of(ShareGroupState.STABLE)),
|
new GroupListing(firstGroup, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE)),
|
||||||
new ShareGroupListing(secondGroup, Optional.of(ShareGroupState.EMPTY))
|
new GroupListing(secondGroup, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.EMPTY))
|
||||||
)));
|
)));
|
||||||
when(adminClient.listShareGroups(any(ListShareGroupsOptions.class))).thenReturn(result);
|
when(adminClient.listGroups(any(ListGroupsOptions.class))).thenReturn(result);
|
||||||
ShareGroupService service = getShareGroupService(cgcArgs, adminClient);
|
ShareGroupService service = getShareGroupService(cgcArgs, adminClient);
|
||||||
Set<String> expectedGroups = new HashSet<>(Arrays.asList(firstGroup, secondGroup));
|
Set<String> expectedGroups = new HashSet<>(Arrays.asList(firstGroup, secondGroup));
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ public class ShareGroupCommandTest {
|
||||||
Collections.singletonList(new MemberDescription("memid1", "clId1", "host1", new MemberAssignment(
|
Collections.singletonList(new MemberDescription("memid1", "clId1", "host1", new MemberAssignment(
|
||||||
Collections.singleton(new TopicPartition("topic1", 0))
|
Collections.singleton(new TopicPartition("topic1", 0))
|
||||||
))),
|
))),
|
||||||
ShareGroupState.STABLE,
|
GroupState.STABLE,
|
||||||
new Node(0, "host1", 9090));
|
new Node(0, "host1", 9090));
|
||||||
resultMap.put(firstGroup, exp);
|
resultMap.put(firstGroup, exp);
|
||||||
|
|
||||||
|
@ -133,11 +134,11 @@ public class ShareGroupCommandTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPrintEmptyGroupState() {
|
public void testPrintEmptyGroupState() {
|
||||||
assertFalse(ShareGroupService.maybePrintEmptyGroupState("group", ShareGroupState.EMPTY, 0));
|
assertFalse(ShareGroupService.maybePrintEmptyGroupState("group", GroupState.EMPTY, 0));
|
||||||
assertFalse(ShareGroupService.maybePrintEmptyGroupState("group", ShareGroupState.DEAD, 0));
|
assertFalse(ShareGroupService.maybePrintEmptyGroupState("group", GroupState.DEAD, 0));
|
||||||
assertFalse(ShareGroupService.maybePrintEmptyGroupState("group", ShareGroupState.STABLE, 0));
|
assertFalse(ShareGroupService.maybePrintEmptyGroupState("group", GroupState.STABLE, 0));
|
||||||
assertTrue(ShareGroupService.maybePrintEmptyGroupState("group", ShareGroupState.STABLE, 1));
|
assertTrue(ShareGroupService.maybePrintEmptyGroupState("group", GroupState.STABLE, 1));
|
||||||
assertTrue(ShareGroupService.maybePrintEmptyGroupState("group", ShareGroupState.UNKNOWN, 1));
|
assertTrue(ShareGroupService.maybePrintEmptyGroupState("group", GroupState.UNKNOWN, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -155,59 +156,61 @@ public class ShareGroupCommandTest {
|
||||||
|
|
||||||
String[] cgcArgs = new String[]{"--bootstrap-server", bootstrapServer, "--list", "--state"};
|
String[] cgcArgs = new String[]{"--bootstrap-server", bootstrapServer, "--list", "--state"};
|
||||||
Admin adminClient = mock(KafkaAdminClient.class);
|
Admin adminClient = mock(KafkaAdminClient.class);
|
||||||
ListShareGroupsResult resultWithAllStates = mock(ListShareGroupsResult.class);
|
ListGroupsResult resultWithAllStates = mock(ListGroupsResult.class);
|
||||||
when(resultWithAllStates.all()).thenReturn(KafkaFuture.completedFuture(Arrays.asList(
|
when(resultWithAllStates.all()).thenReturn(KafkaFuture.completedFuture(Arrays.asList(
|
||||||
new ShareGroupListing(firstGroup, Optional.of(ShareGroupState.STABLE)),
|
new GroupListing(firstGroup, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE)),
|
||||||
new ShareGroupListing(secondGroup, Optional.of(ShareGroupState.EMPTY))
|
new GroupListing(secondGroup, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.EMPTY))
|
||||||
)));
|
)));
|
||||||
when(adminClient.listShareGroups(any(ListShareGroupsOptions.class))).thenReturn(resultWithAllStates);
|
when(adminClient.listGroups(any(ListGroupsOptions.class))).thenReturn(resultWithAllStates);
|
||||||
ShareGroupService service = getShareGroupService(cgcArgs, adminClient);
|
ShareGroupService service = getShareGroupService(cgcArgs, adminClient);
|
||||||
Set<ShareGroupListing> expectedListing = new HashSet<>(Arrays.asList(
|
Set<GroupListing> expectedListing = new HashSet<>(Arrays.asList(
|
||||||
new ShareGroupListing(firstGroup, Optional.of(ShareGroupState.STABLE)),
|
new GroupListing(firstGroup, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE)),
|
||||||
new ShareGroupListing(secondGroup, Optional.of(ShareGroupState.EMPTY))));
|
new GroupListing(secondGroup, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.EMPTY))));
|
||||||
|
|
||||||
final Set[] foundListing = new Set[]{Collections.emptySet()};
|
final Set[] foundListing = new Set[]{Collections.emptySet()};
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
foundListing[0] = new HashSet<>(service.listShareGroupsWithState(new HashSet<>(Arrays.asList(ShareGroupState.values()))));
|
foundListing[0] = new HashSet<>(service.listShareGroupsInStates(new HashSet<>(Arrays.asList(GroupState.values()))));
|
||||||
return Objects.equals(expectedListing, foundListing[0]);
|
return Objects.equals(expectedListing, foundListing[0]);
|
||||||
}, "Expected to show groups " + expectedListing + ", but found " + foundListing[0]);
|
}, "Expected to show groups " + expectedListing + ", but found " + foundListing[0]);
|
||||||
|
|
||||||
ListShareGroupsResult resultWithStableState = mock(ListShareGroupsResult.class);
|
ListGroupsResult resultWithStableState = mock(ListGroupsResult.class);
|
||||||
when(resultWithStableState.all()).thenReturn(KafkaFuture.completedFuture(Collections.singletonList(
|
when(resultWithStableState.all()).thenReturn(KafkaFuture.completedFuture(Collections.singletonList(
|
||||||
new ShareGroupListing(firstGroup, Optional.of(ShareGroupState.STABLE))
|
new GroupListing(firstGroup, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE))
|
||||||
)));
|
)));
|
||||||
when(adminClient.listShareGroups(any(ListShareGroupsOptions.class))).thenReturn(resultWithStableState);
|
when(adminClient.listGroups(any(ListGroupsOptions.class))).thenReturn(resultWithStableState);
|
||||||
Set<ShareGroupListing> expectedListingStable = Collections.singleton(
|
Set<GroupListing> expectedListingStable = Collections.singleton(
|
||||||
new ShareGroupListing(firstGroup, Optional.of(ShareGroupState.STABLE)));
|
new GroupListing(firstGroup, Optional.of(GroupType.SHARE), "share", Optional.of(GroupState.STABLE)));
|
||||||
|
|
||||||
foundListing[0] = Collections.emptySet();
|
foundListing[0] = Collections.emptySet();
|
||||||
|
|
||||||
TestUtils.waitForCondition(() -> {
|
TestUtils.waitForCondition(() -> {
|
||||||
foundListing[0] = new HashSet<>(service.listShareGroupsWithState(Collections.singleton(ShareGroupState.STABLE)));
|
foundListing[0] = new HashSet<>(service.listShareGroupsInStates(Collections.singleton(GroupState.STABLE)));
|
||||||
return Objects.equals(expectedListingStable, foundListing[0]);
|
return Objects.equals(expectedListingStable, foundListing[0]);
|
||||||
}, "Expected to show groups " + expectedListingStable + ", but found " + foundListing[0]);
|
}, "Expected to show groups " + expectedListingStable + ", but found " + foundListing[0]);
|
||||||
service.close();
|
service.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testShareGroupStatesFromString() {
|
public void testGroupStatesFromString() {
|
||||||
Set<ShareGroupState> result = ShareGroupCommand.shareGroupStatesFromString("Stable");
|
Set<GroupState> result = ShareGroupCommand.groupStatesFromString("Stable");
|
||||||
assertEquals(Collections.singleton(ShareGroupState.STABLE), result);
|
assertEquals(Collections.singleton(GroupState.STABLE), result);
|
||||||
|
|
||||||
result = ShareGroupCommand.shareGroupStatesFromString("stable");
|
result = ShareGroupCommand.groupStatesFromString("stable");
|
||||||
assertEquals(new HashSet<>(Collections.singletonList(ShareGroupState.STABLE)), result);
|
assertEquals(new HashSet<>(Collections.singletonList(GroupState.STABLE)), result);
|
||||||
|
|
||||||
result = ShareGroupCommand.shareGroupStatesFromString("dead");
|
result = ShareGroupCommand.groupStatesFromString("dead");
|
||||||
assertEquals(new HashSet<>(Collections.singletonList(ShareGroupState.DEAD)), result);
|
assertEquals(new HashSet<>(Collections.singletonList(GroupState.DEAD)), result);
|
||||||
|
|
||||||
result = ShareGroupCommand.shareGroupStatesFromString("empty");
|
result = ShareGroupCommand.groupStatesFromString("empty");
|
||||||
assertEquals(new HashSet<>(Collections.singletonList(ShareGroupState.EMPTY)), result);
|
assertEquals(new HashSet<>(Collections.singletonList(GroupState.EMPTY)), result);
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> ShareGroupCommand.shareGroupStatesFromString("bad, wrong"));
|
assertThrows(IllegalArgumentException.class, () -> ShareGroupCommand.groupStatesFromString("assigning"));
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> ShareGroupCommand.shareGroupStatesFromString(" bad, Stable"));
|
assertThrows(IllegalArgumentException.class, () -> ShareGroupCommand.groupStatesFromString("bad, wrong"));
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> ShareGroupCommand.shareGroupStatesFromString(" , ,"));
|
assertThrows(IllegalArgumentException.class, () -> ShareGroupCommand.groupStatesFromString(" bad, Stable"));
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> ShareGroupCommand.groupStatesFromString(" , ,"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ShareGroupService getShareGroupService(String[] args, Admin adminClient) {
|
ShareGroupService getShareGroupService(String[] args, Admin adminClient) {
|
||||||
|
|
Loading…
Reference in New Issue