-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-8179: Part 3, Add PartitionsLost API for resetGenerations and metadata/subscription change #6884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
KAFKA-8179: Part 3, Add PartitionsLost API for resetGenerations and metadata/subscription change #6884
Changes from 52 commits
4cf89e6
094ab43
368f2bc
cb9fb49
f7ab5e2
347ca5f
b290283
d3af860
cb25f6c
801dd0b
0a4aa1d
3f2fe2e
bb98793
d1064c4
829d3c2
2b2d8b1
c22266b
1fa089a
16f75ab
e049e5f
92320ba
ff1b9a6
908fda4
3e1fb1c
2e9a291
bd60fd2
74a9895
c85ed6e
16c13ad
ec80f42
60b55e1
5325e56
72e70d6
ba97f99
e54d8e1
dfb3583
b3daf5e
cc4c292
99cafcd
dd37aa1
cf66222
d56b1f3
f67942f
e9a3071
11b1915
a862df0
8440e7d
30452db
091062e
c95e176
d112c5c
332c213
9ceb83f
dc4ad4d
3b0b667
cc30e1d
65f7c68
a4f584f
e4fdcb0
fb5b1e1
0436146
10c1f97
ca7bf32
8373b41
ddfe28f
4e46690
0c2486c
60f8c9c
b1d779f
6082f24
90e6050
33688bf
234f237
700be68
8edb9e1
ac67975
4b0e170
53cd8ea
f1baeb4
178f35e
0e51217
c1a265a
56b54a2
ef901b0
8220d8e
5ed5a0a
58913e4
6d07d42
fde3c9b
67b33ab
6041a79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -95,7 +95,7 @@ public interface ConsumerRebalanceListener { | |
| * invocation of {@link KafkaConsumer#poll(java.time.Duration)} in which this callback is being executed. This means it is not | ||
| * necessary to catch these exceptions and re-attempt to wakeup or interrupt the consumer thread. | ||
| * | ||
| * @param partitions The list of partitions that were assigned to the consumer on the last rebalance | ||
| * @param partitions The list of partitions that were assigned to the consumer and now need to be revoked | ||
| * @throws org.apache.kafka.common.errors.WakeupException If raised from a nested call to {@link KafkaConsumer} | ||
| * @throws org.apache.kafka.common.errors.InterruptException If raised from a nested call to {@link KafkaConsumer} | ||
| */ | ||
|
|
@@ -122,4 +122,30 @@ public interface ConsumerRebalanceListener { | |
| * @throws org.apache.kafka.common.errors.InterruptException If raised from a nested call to {@link KafkaConsumer} | ||
| */ | ||
| void onPartitionsAssigned(Collection<TopicPartition> partitions); | ||
|
|
||
| /** | ||
| * A callback method the user can implement to provide handling of cleaning up resources for partitions that have already | ||
| * been re-assigned to other consumers. This method will not be called during normal execution as the owned partitions would | ||
| * first be revoked by calling the {@link ConsumerRebalanceListener#onPartitionsRevoked} first, before being re-assigned | ||
| * to other consumers. However, when the consumer is being kicked out of the group and hence were not aware when the new | ||
| * group is formed without itself, this function will then be called when the consumer finally be notified about the | ||
| * partitions that have already been emigrated to other consumers. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if it would be helpful to include an example of what you might/need to do differently than onPartitionsRevoked (eg not commit offsets)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea! |
||
| * | ||
| * By default it will just trigger {@link ConsumerRebalanceListener#onPartitionsRevoked}; for advanced users who want to distinguish | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: take out the word "advanced." Makes it seem like this is an uncommon need. |
||
| * the handling logic of revoked partitions v.s. emigrated partitions, they can override the default implementation. | ||
| * | ||
| * It is possible | ||
| * for a {@link org.apache.kafka.common.errors.WakeupException} or {@link org.apache.kafka.common.errors.InterruptException} | ||
| * to be raised from one these nested invocations. In this case, the exception will be propagated to the current | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: one of these nested invocations It sounds like this is a general statement about all 3 of these callbacks. Should we move it to the class doc? Also, should we mention that the callback will be retried if woken up or interrupted?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. Though the callback may not be |
||
| * invocation of {@link KafkaConsumer#poll(java.time.Duration)} in which this callback is being executed. This means it is not | ||
| * necessary to catch these exceptions and re-attempt to wakeup or interrupt the consumer thread. | ||
| * | ||
| * @param partitions The list of partitions that were assigned to the consumer and now have been re-assigned | ||
| * to other consumers | ||
| * @throws org.apache.kafka.common.errors.WakeupException If raised from a nested call to {@link KafkaConsumer} | ||
| * @throws org.apache.kafka.common.errors.InterruptException If raised from a nested call to {@link KafkaConsumer} | ||
| */ | ||
| default void onPartitionsLost(Collection<TopicPartition> partitions) { | ||
| onPartitionsRevoked(partitions); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -521,7 +521,7 @@ public void handle(JoinGroupResponse joinResponse, RequestFuture<ByteBuffer> fut | |
| future.raise(error); | ||
| } else if (error == Errors.UNKNOWN_MEMBER_ID) { | ||
| // reset the member id and retry immediately | ||
| resetGeneration(); | ||
| resetGeneration(this.getClass().getName() + " encountering " + error); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it useful to include |
||
| log.debug("Attempt to join group failed due to unknown member id."); | ||
| future.raise(error); | ||
| } else if (error == Errors.COORDINATOR_NOT_AVAILABLE | ||
|
|
@@ -643,7 +643,7 @@ public void handle(SyncGroupResponse syncResponse, | |
| } else if (error == Errors.UNKNOWN_MEMBER_ID | ||
| || error == Errors.ILLEGAL_GENERATION) { | ||
| log.debug("SyncGroup failed: {}", error.message()); | ||
| resetGeneration(); | ||
| resetGeneration(this.getClass().getName() + " encountering " + error); | ||
| future.raise(error); | ||
| } else if (error == Errors.COORDINATOR_NOT_AVAILABLE | ||
| || error == Errors.NOT_COORDINATOR) { | ||
|
|
@@ -797,7 +797,9 @@ final synchronized boolean hasValidMemberId() { | |
| /** | ||
| * Reset the generation and memberId because we have fallen out of the group. | ||
| */ | ||
| protected synchronized void resetGeneration() { | ||
| protected synchronized void resetGeneration(String errorMessage) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can make this a little clearer and save some duplication with a signature like this: protected synchronized void resetGenerationOnError(Errors error, ApiKeys api)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The thing is that resetGeneration is also called from And thinking about this, I think we should call resetGeneration before sending leave-group because we need to make sure
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I was suggesting separate methods. Something like this: |
||
| log.debug("Resetting generation and requesting re-join group due to {}", errorMessage); | ||
|
|
||
| this.generation = Generation.NO_GENERATION; | ||
| this.rejoinNeeded = true; | ||
| this.state = MemberState.UNJOINED; | ||
|
|
@@ -859,7 +861,7 @@ public synchronized void maybeLeaveGroup(String leaveReason) { | |
| client.pollNoWakeup(); | ||
| } | ||
|
|
||
| resetGeneration(); | ||
| resetGeneration("consumer proactively leaving group"); | ||
| } | ||
|
|
||
| protected boolean isDynamicMember() { | ||
|
|
@@ -913,14 +915,14 @@ public void handle(HeartbeatResponse heartbeatResponse, RequestFuture<Void> futu | |
| future.raise(error); | ||
| } else if (error == Errors.ILLEGAL_GENERATION) { | ||
| log.info("Attempt to heartbeat failed since generation {} is not current", generation.generationId); | ||
| resetGeneration(); | ||
| resetGeneration(this.getClass().getName() + " encountering " + error); | ||
| future.raise(error); | ||
| } else if (error == Errors.FENCED_INSTANCE_ID) { | ||
| log.error("Received fatal exception: group.instance.id gets fenced"); | ||
| future.raise(error); | ||
| } else if (error == Errors.UNKNOWN_MEMBER_ID) { | ||
| log.info("Attempt to heartbeat failed for since member id {} is not valid.", generation.memberId); | ||
| resetGeneration(); | ||
| resetGeneration(this.getClass().getName() + " encountering " + error); | ||
| future.raise(error); | ||
| } else if (error == Errors.GROUP_AUTHORIZATION_FAILED) { | ||
| future.raise(new GroupAuthorizationException(rebalanceConfig.groupId)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,7 @@ | |
| import java.util.concurrent.ConcurrentLinkedQueue; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| import java.util.concurrent.atomic.AtomicInteger; | ||
| import java.util.function.Predicate; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| /** | ||
|
|
@@ -234,7 +235,7 @@ private void maybeUpdateJoinedSubscription(Set<TopicPartition> assignedPartition | |
| // into the subscriptions as long as they still match the subscribed pattern | ||
|
|
||
| Set<String> addedTopics = new HashSet<>(); | ||
| //this is a copy because its handed to listener below | ||
| // this is a copy because its handed to listener below | ||
| for (TopicPartition tp : assignedPartitions) { | ||
| if (!joinedSubscription.contains(tp.topic())) | ||
| addedTopics.add(tp.topic()); | ||
|
|
@@ -252,6 +253,72 @@ private void maybeUpdateJoinedSubscription(Set<TopicPartition> assignedPartition | |
| } | ||
| } | ||
|
|
||
| private void maybeAssignPartitions(final Set<TopicPartition> assignedPartitions) { | ||
| if (!assignedPartitions.isEmpty()) { | ||
| log.info("Setting newly assigned partitions: {}", Utils.join(assignedPartitions, ", ")); | ||
|
|
||
| ConsumerRebalanceListener listener = subscriptions.rebalanceListener(); | ||
| try { | ||
| listener.onPartitionsAssigned(assignedPartitions); | ||
| } catch (WakeupException | InterruptException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| log.error("User provided listener {} failed on partition assignment", listener.getClass().getName(), e); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void maybeRevokePartitions(final Predicate<TopicPartition> predicate) { | ||
| Set<TopicPartition> ownedPartitions = new HashSet<>(subscriptions.assignedPartitions()); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be initialized to assignedPartitions, in onJoinComplete we should be revoking the set Minus(owned-partitions, assigned-partitions) -- this is revoking Minus(assigned-partitions, owned-partitions) Also we are mixing up the naming between owned/assigned which are important to differentiate.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup. |
||
|
|
||
| Set<TopicPartition> revokedPartitions = ownedPartitions.stream().filter(predicate).collect(Collectors.toSet()); | ||
|
|
||
| if (!revokedPartitions.isEmpty()) { | ||
| log.info("Revoke previously assigned partitions {}", revokedPartitions); | ||
|
|
||
| ConsumerRebalanceListener listener = subscriptions.rebalanceListener(); | ||
| try { | ||
| listener.onPartitionsRevoked(revokedPartitions); | ||
| } catch (WakeupException | InterruptException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| log.error("User provided listener {} failed on partition being revocation", listener.getClass().getName(), e); | ||
| } | ||
|
|
||
| Set<TopicPartition> leftPartitions = new HashSet<>(ownedPartitions); | ||
| leftPartitions.removeAll(revokedPartitions); | ||
|
|
||
| subscriptions.assignFromSubscribed(leftPartitions); | ||
| } | ||
| } | ||
|
|
||
| private void maybeLosePartitions(final String rootCause, final Predicate<TopicPartition> predicate) { | ||
| if (subscriptions.partitionsAutoAssigned()) { | ||
| Set<TopicPartition> ownedPartitions = new HashSet<>(subscriptions.assignedPartitions()); | ||
|
|
||
| Set<TopicPartition> lostPartitions = metadataSnapshot == null ? ownedPartitions : | ||
| ownedPartitions.stream().filter(predicate).collect(Collectors.toSet()); | ||
|
|
||
| if (!lostPartitions.isEmpty()) { | ||
| log.info("Lost previously assigned partitions {} due to {}", lostPartitions, rootCause); | ||
|
|
||
| ConsumerRebalanceListener listener = subscriptions.rebalanceListener(); | ||
| try { | ||
| listener.onPartitionsLost(lostPartitions); | ||
| } catch (WakeupException | InterruptException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| log.error("User provided listener {} failed on partition being lost", listener.getClass().getName(), e); | ||
| } | ||
|
|
||
| Set<TopicPartition> leftPartitions = new HashSet<>(ownedPartitions); | ||
| leftPartitions.removeAll(lostPartitions); | ||
|
|
||
| subscriptions.assignFromSubscribed(leftPartitions); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| protected void onJoinComplete(int generation, | ||
| String memberId, | ||
|
|
@@ -292,28 +359,38 @@ protected void onJoinComplete(int generation, | |
| this.nextAutoCommitTimer.updateAndReset(autoCommitIntervalMs); | ||
|
|
||
| // execute the user's callback after rebalance | ||
| ConsumerRebalanceListener listener = subscriptions.rebalanceListener(); | ||
|
|
||
| switch (protocol) { | ||
| case EAGER: | ||
| if (!ownedPartitions.isEmpty()) { | ||
| log.info("Coordinator has owned partitions {} that are not revoked with {} protocol, " + | ||
| "it is likely client is woken up before a previous pending rebalance completes its callback", ownedPartitions, protocol); | ||
| } | ||
|
|
||
| log.info("Setting newly assigned partitions: {}", Utils.join(assignedPartitions, ", ")); | ||
| try { | ||
| listener.onPartitionsAssigned(assignedPartitions); | ||
| } catch (WakeupException | InterruptException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| log.error("User provided listener {} failed on partition assignment", listener.getClass().getName(), e); | ||
| } | ||
| // TODO: after refactored the error handling, we need to exclude owned partitions from assigned partitions | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we resolve this todo now?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, my latest commit removed it :) not yet pushed to GH. |
||
|
|
||
| maybeAssignPartitions(assignedPartitions); | ||
| break; | ||
|
|
||
| case COOPERATIVE: | ||
| assignAndRevoke(listener, assignedPartitions, ownedPartitions); | ||
| Set<TopicPartition> addedPartitions = new HashSet<>(assignedPartitions); | ||
| Set<TopicPartition> revokedPartitions = new HashSet<>(ownedPartitions); | ||
| addedPartitions.removeAll(ownedPartitions); | ||
| revokedPartitions.removeAll(assignedPartitions); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't seem to be using this anymore?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah my bad, I think the rebasing messed up the logic here. And also the other comment you made below. |
||
|
|
||
| log.info("Updating with newly assigned partitions: {}, compare with already owned partitions: {}, " + | ||
| "newly added partitions: {}, revoking partitions: {}", | ||
| Utils.join(assignedPartitions, ", "), | ||
| Utils.join(ownedPartitions, ", "), | ||
| Utils.join(addedPartitions, ", "), | ||
| Utils.join(revokedPartitions, ", ")); | ||
|
|
||
| // add partitions that was not previously owned but are now assigned | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: was -> were |
||
| maybeAssignPartitions(addedPartitions); | ||
|
|
||
| // revoked partitions that was previously owned but no longer assigned | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: revoke partitions that were previously owned but are no longer assigned
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note this correction |
||
| maybeRevokePartitions(tp -> !assignedPartitions.contains(tp)); | ||
|
|
||
| // request re-join based on leader communicated error code | ||
| if (assignment.error() == ConsumerProtocol.AssignmentError.NEED_REJOIN) { | ||
| requestRejoin(); | ||
| } | ||
|
|
@@ -323,38 +400,6 @@ protected void onJoinComplete(int generation, | |
|
|
||
| } | ||
|
|
||
| private void assignAndRevoke(final ConsumerRebalanceListener listener, | ||
| final Set<TopicPartition> assignedPartitions, | ||
| final Set<TopicPartition> ownedPartitions) { | ||
| Set<TopicPartition> addedPartitions = new HashSet<>(assignedPartitions); | ||
| Set<TopicPartition> revokedPartitions = new HashSet<>(ownedPartitions); | ||
| addedPartitions.removeAll(ownedPartitions); | ||
| revokedPartitions.removeAll(assignedPartitions); | ||
|
|
||
| log.info("Updating with newly assigned partitions: {}, compare with already owned partitions: {}, " + | ||
| "newly added partitions: {}, revoking partitions: {}", | ||
| Utils.join(assignedPartitions, ", "), | ||
| Utils.join(ownedPartitions, ", "), | ||
| Utils.join(addedPartitions, ", "), | ||
| Utils.join(revokedPartitions, ", ")); | ||
|
|
||
| try { | ||
| listener.onPartitionsAssigned(addedPartitions); | ||
| } catch (WakeupException | InterruptException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| log.error("User provided listener {} failed on partition assignment", listener.getClass().getName(), e); | ||
| } | ||
|
|
||
| try { | ||
| listener.onPartitionsRevoked(revokedPartitions); | ||
| } catch (WakeupException | InterruptException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| log.error("User provided listener {} failed on partition revocation", listener.getClass().getName(), e); | ||
| } | ||
| } | ||
|
|
||
| void maybeUpdateSubscriptionMetadata() { | ||
| int version = metadata.updateVersion(); | ||
| if (version > metadataSnapshot.version) { | ||
|
|
@@ -587,45 +632,45 @@ protected void onJoinPrepare(int generation, String memberId) { | |
| maybeAutoCommitOffsetsSync(time.timer(rebalanceConfig.rebalanceTimeoutMs)); | ||
|
|
||
| // execute the user's callback before rebalance | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this comment be moved?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ack, makes sense. |
||
| ConsumerRebalanceListener listener = subscriptions.rebalanceListener(); | ||
|
|
||
| switch (protocol) { | ||
| case EAGER: | ||
| Set<TopicPartition> revokedPartitions = new HashSet<>(subscriptions.assignedPartitions()); | ||
| log.info("Revoking previously assigned partitions {}", revokedPartitions); | ||
| try { | ||
| listener.onPartitionsRevoked(revokedPartitions); | ||
| } catch (WakeupException | InterruptException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| log.error("User provided listener {} failed on partition revocation", listener.getClass().getName(), e); | ||
| } | ||
|
|
||
| // also clear the assigned partitions since all have been revoked | ||
| subscriptions.assignFromSubscribed(Collections.emptySet()); | ||
|
|
||
| // revoke all partitions | ||
| maybeRevokePartitions(tp -> true); | ||
| break; | ||
|
|
||
| case COOPERATIVE: | ||
| // only revoke those partitions that are not in the subscription any more. | ||
| maybeRevokePartitions(tp -> !subscriptions.subscription().contains(tp.topic())); | ||
| break; | ||
| } | ||
|
|
||
| isLeader = false; | ||
| subscriptions.resetGroupSubscription(); | ||
| } | ||
|
|
||
| @Override | ||
| public void resetGeneration(String errorMessage) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm.. This method is invoked from
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also be invoked when encountering errors from heartbeat / join-group etc, in which case we should really invoke I think I'll have to duplicate the logic a bit to distinguish these two cases. |
||
| // revoke all partitions | ||
| maybeLosePartitions(errorMessage, tp -> true); | ||
| super.resetGeneration(errorMessage); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean rejoinNeededOrPending() { | ||
| if (!subscriptions.partitionsAutoAssigned()) | ||
| return false; | ||
|
|
||
| // we need to rejoin if we performed the assignment and metadata has changed | ||
| if (assignmentSnapshot != null && !assignmentSnapshot.matches(metadataSnapshot)) | ||
| if (assignmentSnapshot != null && !assignmentSnapshot.matches(metadataSnapshot)) { | ||
| maybeLosePartitions("topic metadata has changed and therefore some topics may not exist any more", | ||
| tp -> !metadataSnapshot.partitionsPerTopic().containsKey(tp.topic())); | ||
| return true; | ||
| } | ||
|
|
||
| // we need to join if our subscription has changed since the last join | ||
| if (joinedSubscription != null && !joinedSubscription.equals(subscriptions.subscription())) | ||
| if (joinedSubscription != null && !joinedSubscription.equals(subscriptions.subscription())) { | ||
| return true; | ||
| } | ||
|
|
||
| return super.rejoinNeededOrPending(); | ||
| } | ||
|
|
@@ -1033,7 +1078,7 @@ public void handle(OffsetCommitResponse commitResponse, RequestFuture<Void> futu | |
| } else if (error == Errors.UNKNOWN_MEMBER_ID | ||
| || error == Errors.ILLEGAL_GENERATION) { | ||
| // need to reset generation and re-join group | ||
| resetGeneration(); | ||
| resetGeneration(this.getClass().getName() + " encountering " + error); | ||
| future.raise(new CommitFailedException()); | ||
| return; | ||
| } else { | ||
|
|
@@ -1173,6 +1218,10 @@ private MetadataSnapshot(SubscriptionState subscription, Cluster cluster, int ve | |
| boolean matches(MetadataSnapshot other) { | ||
| return version == other.version || partitionsPerTopic.equals(other.partitionsPerTopic); | ||
| } | ||
|
|
||
| Map<String, Integer> partitionsPerTopic() { | ||
| return partitionsPerTopic; | ||
| } | ||
| } | ||
|
|
||
| private static class OffsetCommitCompletion { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.