Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.coordinator.group.streams.assignor;
package org.apache.kafka.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;

import java.util.Map;
import java.util.Objects;
Expand All @@ -24,6 +27,8 @@
*
* @param members The member assignments keyed by member ID.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public record GroupAssignment(Map<String, MemberAssignment> members) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The KIP defines this as a class, not a record. -- Given that this is an output/return type, not an input to the assignor, it might actually be ok to keep as record, and we could also update the KIP?

\cc @lianetm for input

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I will update my kip and make it into a record.


public GroupAssignment {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;

import java.util.Collection;
import java.util.Map;

/**
* The group metadata specifications required to compute the target assignment.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface GroupSpec {

/**
* @return The member Ids of all members in the group.
*/
Collection<String> memberIds();

/**
* Gets the static metadata for a given member.
*
* @param memberId The member Id.
* @return The static member metadata.
*/
MemberAssignmentMetadata memberMetadata(String memberId);

/**
* Gets the current assignment state for a given member.
*
* @param memberId The member Id.
* @return The current member assignment state.
*/
MemberAssignmentState memberAssignmentState(String memberId);

/**
* @return Any configurations passed to the assignor.
*/
Map<String, String> configs();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;

import java.util.Map;
import java.util.Set;

/**
* The task assignment for a streams group member.
*
* <p>Only active and standby tasks are assigned by the {@link TaskAssignor}. Warm-up tasks are
* not assigned by the assignor; they are decided during reconciliation.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface MemberAssignment {

/**
* @return The active tasks assigned to this member keyed by subtopology Id.
*/
Map<String, Set<Integer>> activeTasks();

/**
* @return The standby tasks assigned to this member keyed by subtopology Id.
*/
Map<String, Set<Integer>> standbyTasks();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;

import java.util.Map;
import java.util.Optional;

/**
* Interface representing the static metadata for a streams group member.
*
* <p>The metadata contains the per-member information that does not change during the assignment
* computation. The member's current task assignment state is exposed separately through
* {@link MemberAssignmentState}.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface MemberAssignmentMetadata {

/**
* @return The instance ID if provided.
*/
Optional<String> instanceId();

/**
* @return The rack ID if provided.
*/
Optional<String> rackId();

/**
* @return The process ID.
*/
String processId();

/**
* @return The client tags for a rack-aware assignment.
*/
Map<String, String> clientTags();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;

import java.util.Map;
import java.util.Set;

/**
* Interface representing the current assignment state for a streams group member.
*
* <p>This is the assignment the member currently owns and is used by the {@link TaskAssignor}
* to compute a new, sticky target assignment. Note that the assignor does not assign warm-up
* tasks itself (they are decided during reconciliation), but the currently-assigned warm-up
* tasks are exposed here so that the assignor can take them into account.
*
* <p>The task-set accessors ({@link #activeTasks()}, {@link #standbyTasks()},
* {@link #warmupTasks()}) are keyed by subtopology ID, mirroring the grouped {@code TaskIds}
* wire format ({@code subtopologyId, [partitions]}). In contrast, {@link #taskOffsets()} and
* {@link #taskEndOffsets()} are keyed by {@link TaskId}, mirroring the flat {@code TaskOffset}
* wire format ({@code subtopologyId, partition, offset}). The two shapes differ because the
* underlying wire representations differ.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface MemberAssignmentState {

/**
* @return The current active tasks keyed by subtopology Id.
*/
Map<String, Set<Integer>> activeTasks();

/**
* @return The current standby tasks keyed by subtopology Id.
*/
Map<String, Set<Integer>> standbyTasks();

/**
* @return The current warm-up tasks keyed by subtopology Id.
*/
Map<String, Set<Integer>> warmupTasks();

/**
* @return The last received cumulative task offsets of assigned tasks or dormant tasks.
*/
Map<TaskId, Long> taskOffsets();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One argument during KIP discussion was, to return data based on wire format. That's why we don't have Set<TaskId> activeTasks() but Map<String, Set<Integer>> activeTasks().

And I just realize that the wire-format between both task-ids (which is an array of []TaskId and each TaskId is String,[]int32 format) and task-offsets (which is an array of []TaskOffset and each TaskOffset is string,int32,int64) have a different format...

But I am now wondering what the best representation for string,int32,int64 triple would be -- while Map<TaskId, Long> (with TaskId == String + int) is not a bad fit, it's somewhat a mismatch to what we do for activeTasks() above... 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think maybe keep it the Map<TaskId, Long> is more reasonable, I have update the interface of Taskid


/**
* @return The last received cumulative task end offsets of assigned tasks or dormant tasks.
*/
Map<TaskId, Long> taskEndOffsets();

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.coordinator.group.streams.assignor;
package org.apache.kafka.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;

/**
* Server side task assignor used by streams groups.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface TaskAssignor {

/**
* Unique name for this assignor.
* Unique name for this assignor. Used in configuration to select this assignor.
*/
String name();

Expand All @@ -33,7 +38,7 @@ public interface TaskAssignor {
* @param topologyDescriber The task metadata describer.
* @return The new assignment for the group.
*
* @throws TaskAssignorException For empty groups
* @throws TaskAssignorException If the assignment cannot be computed.
*/
GroupAssignment assign(
GroupSpec groupSpec,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.coordinator.group.streams.assignor;
package org.apache.kafka.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;
import org.apache.kafka.common.errors.ApiException;

/**
* Exception thrown by {@link TaskAssignor#assign(GroupSpec, TopologyDescriber)}}. The exception is only used internally.
* Exception thrown by {@link TaskAssignor#assign(GroupSpec, TopologyDescriber)} when the group's tasks
* cannot be assigned. Custom {@link TaskAssignor} implementations should throw this exception to signal
* an assignment failure.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public class TaskAssignorException extends ApiException {

public TaskAssignorException(String message) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;

/**
* The identifier for a task.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface TaskId extends Comparable<TaskId> {

/**
* @return The unique identifier of the subtopology.
*/
String subtopologyId();

/**
* @return The partition of the input topics this task is processing.
*/
int partition();

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.coordinator.group.streams.assignor;
package org.apache.kafka.coordinator.group.api.assignor.streams;

import org.apache.kafka.common.annotation.InterfaceAudience;
import org.apache.kafka.common.annotation.InterfaceStability;

import java.util.List;
import java.util.NoSuchElementException;

/**
* The topology describer is used by the {@link TaskAssignor} to get topic and task metadata of the group's topology.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface TopologyDescriber {

/**
* Map of topic names to topic metadata.
* The IDs of all subtopologies in the group's topology.
*
* @return The list of subtopologies IDs.
* @return The list of subtopology IDs.
*/
List<String> subtopologies();

Expand Down
Loading