Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
37b5c63
first version
guozhangwang Sep 6, 2019
4a34116
add unit tests
guozhangwang Sep 7, 2019
d964cea
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Sep 10, 2019
f5b8f96
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Sep 11, 2019
b07bac2
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Sep 12, 2019
29f8942
fix unit tests
guozhangwang Sep 12, 2019
3000d6f
add RebalanceInProgressException
guozhangwang Sep 13, 2019
8464fa7
rebase from trunk
guozhangwang Sep 17, 2019
f804b3d
refactor unit tests
guozhangwang Sep 17, 2019
3c62029
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Sep 17, 2019
284ef77
poll with 1ms than 0ms for discover coordinator
guozhangwang Sep 18, 2019
9825063
refactor the newly added test with auto-tick 1ms
guozhangwang Sep 18, 2019
929048a
rebase from trunk
guozhangwang Oct 21, 2019
58adc09
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Oct 22, 2019
c79a93d
fix unit tests
guozhangwang Oct 22, 2019
75c4e88
rebase from trunk again
guozhangwang Oct 22, 2019
95ef3fb
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Oct 27, 2019
3b844ff
use RetriableCommitFailedException
guozhangwang Oct 27, 2019
781514d
unit tests
guozhangwang Oct 27, 2019
8b08a3e
handle retrialbe commit failed in streams
guozhangwang Oct 27, 2019
8fa5d74
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Oct 29, 2019
d75fcb9
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Oct 29, 2019
b176e4e
fix unit tests
guozhangwang Oct 29, 2019
7a826f9
remove imports
guozhangwang Oct 29, 2019
31e372e
fix a real bug exposed by flaky test
guozhangwang Oct 30, 2019
366739b
remove debugging info
guozhangwang Oct 30, 2019
faf787e
minor fix on CommitFailed class
guozhangwang Oct 30, 2019
c71af82
rebase from trunk
guozhangwang Nov 8, 2019
ffe6146
more java doc
guozhangwang Nov 8, 2019
518049c
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Dec 11, 2019
30d5e53
address comments
guozhangwang Dec 12, 2019
fa3456d
address comments
guozhangwang Dec 24, 2019
0220d96
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Jan 2, 2020
27d013a
address comment
guozhangwang Jan 2, 2020
94611cc
do not reset if generation has changed
guozhangwang Jan 2, 2020
b529847
remove restrictive check on commit
guozhangwang Jan 3, 2020
33fd785
check assignment
guozhangwang Jan 3, 2020
1df9808
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Jan 3, 2020
3057405
more comments
guozhangwang Jan 3, 2020
21f531d
remember the fatal exception and throw
guozhangwang Jan 4, 2020
effb29d
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Jan 6, 2020
6fbd976
comments
guozhangwang Jan 6, 2020
f4e7111
Merge branch 'trunk' of https://github.com/apache/kafka into K8421-re…
guozhangwang Jan 8, 2020
91b6606
move back to RebalanceInProgressException
guozhangwang Jan 8, 2020
0ad52ec
address comments
guozhangwang Jan 9, 2020
27f76b7
rebase from trunk
guozhangwang Jan 9, 2020
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 @@ -28,6 +28,10 @@ public class CommitFailedException extends KafkaException {

private static final long serialVersionUID = 1L;

public CommitFailedException(final String message) {
super(message);
}

public CommitFailedException() {
super("Commit cannot be completed since the group has already " +
"rebalanced and assigned the partitions to another member. This means that the time " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1210,9 +1210,11 @@ private ConsumerRecords<K, V> poll(final Timer timer, final boolean includeMetad
client.maybeTriggerWakeup();

if (includeMetadataInTimeout) {
if (!updateAssignmentMetadataIfNeeded(timer)) {
return ConsumerRecords.empty();
}
// try to update assignment metadata BUT do not need to block on the timer,
// since even if we are 1) in the middle of a rebalance or 2) have partitions
// with unknown starting positions we may still want to return some data
// as long as there are some partitions fetchable
updateAssignmentMetadataIfNeeded(time.timer(1L));

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.

This is the tricky part: I have to use a non-zero timer so that blocking rpc like find-coordinator is guaranteed to poll for once, otherwise the future.hasExpired will trigger immediately and we would doomed to be not finishing that call.

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.

I am wondering about two things here:

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.

If the timer is set to zero, it means that we would not send the request at all -- note that client.send call just queued up the request in the queue, and only client.poll would write it to the socket, and that's why I cannot have the timer to be 0.

Setting it to be 1 should be robust since the timer is initialized at the beginning of the call and until the first client.poll() it would not be checked, and hence not returned early.

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've further polished it to only use timer(1) if the remaining is still > 0, in this case we still make sure that consumer.poll(0) can return instantaneously.

} else {
while (!updateAssignmentMetadataIfNeeded(time.timer(Long.MAX_VALUE))) {
log.warn("Still waiting for metadata");
Expand Down Expand Up @@ -1285,12 +1287,6 @@ private Map<TopicPartition, List<ConsumerRecord<K, V>>> pollForFetches(Timer tim
});
timer.update(pollTimer.currentTimeMs());

// after the long poll, we should check whether the group needs to rebalance
// prior to returning data so that the group can stabilize faster
if (coordinator != null && coordinator.rejoinNeededOrPending()) {
return Collections.emptyMap();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this dropped? Are you thinking it's just redundant?

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.

Isn't this the "main point" of this PR -- we don't want to return nothing just because a rebalance is needed or pending

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh, yeah... that makes sense.

return fetcher.fetchedRecords();
}

Expand Down Expand Up @@ -1324,6 +1320,8 @@ private Map<TopicPartition, List<ConsumerRecord<K, V>>> pollForFetches(Timer tim
* @throws org.apache.kafka.common.errors.TimeoutException if the timeout specified by {@code default.api.timeout.ms} expires
* before successful completion of the offset commit
* @throws org.apache.kafka.common.errors.FencedInstanceIdException if this consumer instance gets fenced by broker.
* @throws org.apache.kafka.common.errors.RebalanceInProgressException if this consumer instance is in the middle of a rebalance;
* Commit could be retried after the rebalance is completed with the {@link #poll(Duration)} call.
*/
@Override
public void commitSync() {
Expand Down Expand Up @@ -1359,6 +1357,8 @@ public void commitSync() {
* @throws org.apache.kafka.common.errors.TimeoutException if the timeout expires before successful completion
* of the offset commit
* @throws org.apache.kafka.common.errors.FencedInstanceIdException if this consumer instance gets fenced by broker.
* @throws org.apache.kafka.common.errors.RebalanceInProgressException if this consumer instance is in the middle of a rebalance;
* Commit could be retried after the rebalance is completed with the {@link #poll(Duration)} call.
*/
@Override
public void commitSync(Duration timeout) {
Expand All @@ -1380,7 +1380,8 @@ public void commitSync(Duration timeout) {
* This commits offsets to Kafka. The offsets committed using this API will be used on the first fetch after every
* rebalance and also on startup. As such, if you need to store offsets in anything other than Kafka, this API
* should not be used. The committed offset should be the next message your application will consume,
* i.e. lastProcessedMessageOffset + 1.
* i.e. lastProcessedMessageOffset + 1. If automatic group management with {@link #subscribe(Collection)} is used,
* then the committed offsets must belong to the currently auto-assigned partitions.
* <p>
* This is a synchronous commits and will block until either the commit succeeds or an unrecoverable error is
* encountered (in which case it is thrown to the caller), or the timeout specified by {@code default.api.timeout.ms} expires
Expand All @@ -1406,6 +1407,8 @@ public void commitSync(Duration timeout) {
* @throws org.apache.kafka.common.errors.TimeoutException if the timeout expires before successful completion
* of the offset commit
* @throws org.apache.kafka.common.errors.FencedInstanceIdException if this consumer instance gets fenced by broker.
* @throws org.apache.kafka.common.errors.RebalanceInProgressException if this consumer instance is in the middle of a rebalance;
* Commit could be retried after the rebalance is completed with the {@link #poll(Duration)} call.
*/
@Override
public void commitSync(final Map<TopicPartition, OffsetAndMetadata> offsets) {
Expand All @@ -1418,7 +1421,8 @@ public void commitSync(final Map<TopicPartition, OffsetAndMetadata> offsets) {
* This commits offsets to Kafka. The offsets committed using this API will be used on the first fetch after every
* rebalance and also on startup. As such, if you need to store offsets in anything other than Kafka, this API
* should not be used. The committed offset should be the next message your application will consume,
* i.e. lastProcessedMessageOffset + 1.
* i.e. lastProcessedMessageOffset + 1. If automatic group management with {@link #subscribe(Collection)} is used,
* then the committed offsets must belong to the currently auto-assigned partitions.
* <p>
* This is a synchronous commits and will block until either the commit succeeds, an unrecoverable error is
* encountered (in which case it is thrown to the caller), or the timeout expires.
Expand All @@ -1444,12 +1448,15 @@ public void commitSync(final Map<TopicPartition, OffsetAndMetadata> offsets) {
* @throws org.apache.kafka.common.errors.TimeoutException if the timeout expires before successful completion
* of the offset commit
* @throws org.apache.kafka.common.errors.FencedInstanceIdException if this consumer instance gets fenced by broker.
* @throws org.apache.kafka.common.errors.RebalanceInProgressException if this consumer instance is in the middle of a rebalance;
* Commit could be retried after the rebalance is completed with the {@link #poll(Duration)} call.
*/
@Override
public void commitSync(final Map<TopicPartition, OffsetAndMetadata> offsets, final Duration timeout) {
acquireAndEnsureOpen();
try {
maybeThrowInvalidGroupIdException();
maybeThrowIfCommitOffsetsNotOwned(offsets);
offsets.forEach(this::updateLastSeenEpochIfNewer);
if (!coordinator.commitOffsetsSync(new HashMap<>(offsets), time.timer(timeout))) {
throw new TimeoutException("Timeout of " + timeout.toMillis() + "ms expired before successfully " +
Expand Down Expand Up @@ -1499,7 +1506,8 @@ public void commitAsync(OffsetCommitCallback callback) {
* This commits offsets to Kafka. The offsets committed using this API will be used on the first fetch after every
* rebalance and also on startup. As such, if you need to store offsets in anything other than Kafka, this API
* should not be used. The committed offset should be the next message your application will consume,
* i.e. lastProcessedMessageOffset + 1.
* i.e. lastProcessedMessageOffset + 1. If automatic group management with {@link #subscribe(Collection)} is used,
* then the committed offsets must belong to the currently auto-assigned partitions.
* <p>
* This is an asynchronous call and will not block. Any errors encountered are either passed to the callback
* (if provided) or discarded.
Expand All @@ -1519,14 +1527,29 @@ public void commitAsync(final Map<TopicPartition, OffsetAndMetadata> offsets, Of
acquireAndEnsureOpen();
try {
maybeThrowInvalidGroupIdException();
maybeThrowIfCommitOffsetsNotOwned(offsets);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Did you consider the compatibility of this change? We probably should have been more restrictive from the beginning, but is it a risk to change now? An alternative might be to just log a warning.

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.

Originally I thought about this as a bug fix since we only check generation id at the broker-side, but now as I examine the javadoc again, we never state that for these two functions users should only commit on auto-assigned partitions -- in fact, if that's the case then they likely should just use the other two overloads unless they do not want the current position to be committed -- like in Streams, if some records get deserialization errors.

So after a second thought I'm happy to take it out of this PR and still allow users to do so (perhaps I'd just make it clear that, if you use these two functions we would NOT check it so you can actually commit whatever you want and calling these two functions with your own caution). WYDT @hachikuji ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It seems safer that way, but it does feel a little unsatisfying to be left in this state forever since we probably should have always had the validation. Perhaps we could file a JIRA and add the validation in 3.0 or something?

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.

Now I actually feel if it is by-design to allow user this flexibility with auto-assignment but with their own caution? If we always want to restrict to assigned partitions then most likely they would not need these two overloads.

log.debug("Committing offsets: {}", offsets);
offsets.forEach(this::updateLastSeenEpochIfNewer);
coordinator.commitOffsetsAsync(new HashMap<>(offsets), callback);
} catch (CommitFailedException e) {
log.error("Failed to commit offsets asynchronously because they do not belong to dynamically assigned partitions");

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.

Just wondering, does this mean we did not used to check if all the offsets actually belonged to auto-assigned partitions? Or did we just straight throw the CommitFailedException? Is this a behavioral change?

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 this is not a behavior change: before this, the broker would check the commit offset request (but only on the generation id, as the coordinator did not keep trace who-owns-what), and if failed it will return the error code which would be translated to CommitFailed on the callback still.

With this we will set the same error on the callback, but much earlier and skip the offset commit request.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we still need this catch since we reverted the logic to verify only assigned partitions can be committed?

callback.onComplete(offsets, e);
} finally {
release();
}
}

private void maybeThrowIfCommitOffsetsNotOwned(final Map<TopicPartition, OffsetAndMetadata> offsets) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: The callers of this iterate the committed offsets twice. Once in order to check partition ownership and then a second time to invoke updateLastSeenEpochIfNewer. I wonder if we could consolidate these two loops.

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.

Yes, if we are not gonna remove this (see other comment above) I can do this.

if (subscriptions.partitionsAutoAssigned()) {
final Set<TopicPartition> partitions = assignment();
for (final TopicPartition tp : offsets.keySet()) {
if (!partitions.contains(tp))
throw new CommitFailedException("cannot commit offsets out of owned partitions with " +
"dynamic partition assigned from subscription");
Comment thread
guozhangwang marked this conversation as resolved.
Outdated
}
}
}

/**
* Overrides the fetch offsets that the consumer will use on the next {@link #poll(Duration) poll(timeout)}. If this API
* is invoked for the same partition more than once, the latest offset will be used on the next poll(). Note that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import org.apache.kafka.common.TopicPartition;

import java.time.Duration;
import java.util.Collection;
import java.util.Map;

Expand Down Expand Up @@ -45,6 +46,8 @@ public interface OffsetCommitCallback {
* configured groupId. See the exception for more details
* @throws org.apache.kafka.common.KafkaException for any other unrecoverable errors (e.g. if offset metadata
* is too large or if the committed offset is invalid).
* @throws org.apache.kafka.common.errors.RebalanceInProgressException if this consumer instance is in the middle of a rebalance;
* Commit could be retried after the rebalance is completed with the {@link KafkaConsumer#poll(Duration)} call.
*/
void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ public class RetriableCommitFailedException extends RetriableException {

private static final long serialVersionUID = 1L;

public static RetriableCommitFailedException withUnderlyingMessage(String additionalMessage) {

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.

This function is not used anywhere so remove.

return new RetriableCommitFailedException("Offset commit failed with a retriable exception. " +
"You should retry committing the latest consumed offsets. " +
"The underlying error was: " + additionalMessage);
}

public RetriableCommitFailedException(Throwable t) {
super("Offset commit failed with a retriable exception. You should retry committing " +
"the latest consumed offsets.", t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
public abstract class AbstractCoordinator implements Closeable {
public static final String HEARTBEAT_THREAD_PREFIX = "kafka-coordinator-heartbeat-thread";

private enum MemberState {
protected enum MemberState {
UNJOINED, // the client is not part of a group
REBALANCING, // the client has begun rebalancing
STABLE, // the client has joined and is sending heartbeats
Expand All @@ -125,12 +125,11 @@ private enum MemberState {
private HeartbeatThread heartbeatThread = null;
private boolean rejoinNeeded = true;
private boolean needsJoinPrepare = true;
private MemberState state = MemberState.UNJOINED;
private RequestFuture<ByteBuffer> joinFuture = null;
private Node coordinator = null;
private Generation generation = Generation.NO_GENERATION;

private RequestFuture<Void> findCoordinatorFuture = null;
protected MemberState state = MemberState.UNJOINED;

/**
* Initialize the coordination manager.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.apache.kafka.common.errors.FencedInstanceIdException;
import org.apache.kafka.common.errors.GroupAuthorizationException;
import org.apache.kafka.common.errors.InterruptException;
import org.apache.kafka.common.errors.RebalanceInProgressException;
import org.apache.kafka.common.errors.RetriableException;
import org.apache.kafka.common.errors.TimeoutException;
import org.apache.kafka.common.errors.TopicAuthorizationException;
Expand Down Expand Up @@ -1045,10 +1046,14 @@ private RequestFuture<Void> sendOffsetCommitRequest(final Map<TopicPartition, Of
if (subscriptions.partitionsAutoAssigned()) {
generation = generation();
// if the generation is null, we are not part of an active group (and we expect to be).
// the only thing we can do is fail the commit and let the user rejoin the group in poll()
// the only thing we can do is fail the commit and let the user rejoin the group in poll();
// we use RebalanceInProgressException to indicate this may not be a fatal error, and leave
// it to users whether they want to handle differently than CommitFailed
if (generation == null) {
log.info("Failing OffsetCommit request since the consumer is not part of an active group");
return RequestFuture.failure(new CommitFailedException());
return RequestFuture.failure(new RebalanceInProgressException("Offset commit cannot be completed since the " +

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.

This is the main proposal for returning a different exception.

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.

Is it always the case that if generation is null that it's due to a rebalance in progress? Or do we want to keep the ComitFailedException and add to the exception message that the exception could be due to a rebalance?

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.

This is a good question, I would replace this case with the RetriableCommitFailed exception (see my other comment below).

"consumer group is not part of an active group yet. You can try completing the rebalance " +

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.

Suggested change
"consumer group is not part of an active group yet. You can try completing the rebalance " +
"consumer is not part of an active group yet. You can try completing the rebalance " +

"by calling poll() and then retry the operation"));
}
} else
generation = Generation.NO_GENERATION;
Expand Down Expand Up @@ -1127,15 +1132,19 @@ public void handle(OffsetCommitResponse commitResponse, RequestFuture<Void> futu
/* Consumer never tries to commit offset in between join-group and sync-group,
* and hence on broker-side it is not expected to see a commit offset request
* during CompletingRebalance phase; if it ever happens then broker would return
* this error. In this case we should just treat as a fatal CommitFailed exception.
* However, we do not need to reset generations and just request re-join, such that
* if the caller decides to proceed and poll, it would still try to proceed and re-join normally.
* this error. In this case we would just throw a RebalanceInProgressException
* and request re-join, but we do not need to reset generations.
* If the caller decides to proceed and poll, it would still try to proceed and re-join normally.
*/
requestRejoin();
future.raise(new RebalanceInProgressException());

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.

This is the main proposal for returning a different exception.

return;
} else if (error == Errors.UNKNOWN_MEMBER_ID) {
// need to reset generation and re-join group
resetGenerationOnResponseError(ApiKeys.OFFSET_COMMIT, error);
future.raise(new CommitFailedException());
return;
} else if (error == Errors.UNKNOWN_MEMBER_ID
Comment thread
guozhangwang marked this conversation as resolved.
|| error == Errors.ILLEGAL_GENERATION) {
} else if (error == Errors.ILLEGAL_GENERATION) {
// need to reset generation and re-join group
resetGenerationOnResponseError(ApiKeys.OFFSET_COMMIT, error);
future.raise(new CommitFailedException());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,21 +222,45 @@ public synchronized boolean assignFromUser(Set<TopicPartition> partitions) {
if (this.assignment.partitionSet().equals(partitions))
return false;

Map<TopicPartition, TopicPartitionState> assignedPartitionStates = partitionToStateMap(partitions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: should declare final as well?

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.

Ack.


assignmentId++;

// update the subscribed topics
Set<String> manualSubscribedTopics = new HashSet<>();
Map<TopicPartition, TopicPartitionState> partitionToState = new HashMap<>();
for (TopicPartition partition : partitions) {
TopicPartitionState state = assignment.stateValue(partition);
if (state == null)
state = new TopicPartitionState();
partitionToState.put(partition, state);
manualSubscribedTopics.add(partition.topic());
}
this.assignment.set(partitionToState);

this.assignment.set(assignedPartitionStates);
return changeSubscription(manualSubscribedTopics);
}

/**
* Change the assignment to the specified partitions returned from the coordinator, note this is
* different from {@link #assignFromUser(Set)} which directly set the assignment from user inputs.
*/
public synchronized void assignFromSubscribed(Collection<TopicPartition> assignments) {
if (!this.partitionsAutoAssigned())
throw new IllegalArgumentException("Attempt to dynamically assign partitions while manual assignment in use");


Comment thread
guozhangwang marked this conversation as resolved.
Outdated
final Map<TopicPartition, TopicPartitionState> assignedPartitionStates = partitionToStateMap(assignments);
assignmentId++;
this.assignment.set(assignedPartitionStates);
}

private Map<TopicPartition, TopicPartitionState> partitionToStateMap(Collection<TopicPartition> assignments) {
Map<TopicPartition, TopicPartitionState> map = new HashMap<>(assignments.size());
Comment thread
guozhangwang marked this conversation as resolved.
Outdated
for (final TopicPartition tp : assignments) {
if (assignment.contains(tp))
map.put(tp, assignment.stateValue(tp));
else
map.put(tp, new TopicPartitionState());
}
return map;
}

/**
* @return true if assignments matches subscription, otherwise false
*/
Expand All @@ -262,20 +286,6 @@ public synchronized boolean checkAssignmentMatchedSubscription(Collection<TopicP
return true;
}

/**
* Change the assignment to the specified partitions returned from the coordinator, note this is
* different from {@link #assignFromUser(Set)} which directly set the assignment from user inputs.
*/
public synchronized void assignFromSubscribed(Collection<TopicPartition> assignments) {
if (!this.partitionsAutoAssigned())
throw new IllegalArgumentException("Attempt to dynamically assign partitions while manual assignment in use");


Map<TopicPartition, TopicPartitionState> assignedPartitionStates = partitionToStateMap(assignments);
assignmentId++;
this.assignment.set(assignedPartitionStates);
}

private void registerRebalanceListener(ConsumerRebalanceListener listener) {
if (listener == null)
throw new IllegalArgumentException("RebalanceListener cannot be null");
Expand Down Expand Up @@ -410,7 +420,7 @@ synchronized List<TopicPartition> fetchablePartitions(Predicate<TopicPartition>
.collect(Collectors.toList());
}

synchronized boolean partitionsAutoAssigned() {
public synchronized boolean partitionsAutoAssigned() {

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.

Called by KafkaConsumer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: wonder if it's worth changing this name. For example: hasAutoAssignedPartitions?

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.

ack.

return this.subscriptionType == SubscriptionType.AUTO_TOPICS || this.subscriptionType == SubscriptionType.AUTO_PATTERN;
}

Expand Down Expand Up @@ -669,13 +679,6 @@ public synchronized ConsumerRebalanceListener rebalanceListener() {
return rebalanceListener;
}

private static Map<TopicPartition, TopicPartitionState> partitionToStateMap(Collection<TopicPartition> assignments) {
Map<TopicPartition, TopicPartitionState> map = new HashMap<>(assignments.size());
for (TopicPartition tp : assignments)
map.put(tp, new TopicPartitionState());
return map;
}

private static class TopicPartitionState {

private FetchState fetchState;
Expand Down
Loading