use of org.apache.kafka.common.errors.MemberIdRequiredException in project kafka by apache.
the class AbstractCoordinator method joinGroupIfNeeded.
/**
* Joins the group without starting the heartbeat thread.
*
* If this function returns true, the state must always be in STABLE and heartbeat enabled.
* If this function returns false, the state can be in one of the following:
* * UNJOINED: got error response but times out before being able to re-join, heartbeat disabled
* * PREPARING_REBALANCE: not yet received join-group response before timeout, heartbeat disabled
* * COMPLETING_REBALANCE: not yet received sync-group response before timeout, heartbeat enabled
*
* Visible for testing.
*
* @param timer Timer bounding how long this method can block
* @throws KafkaException if the callback throws exception
* @return true iff the operation succeeded
*/
boolean joinGroupIfNeeded(final Timer timer) {
while (rejoinNeededOrPending()) {
if (!ensureCoordinatorReady(timer)) {
return false;
}
// still in progress.
if (needsJoinPrepare) {
// need to set the flag before calling onJoinPrepare since the user callback may throw
// exception, in which case upon retry we should not retry onJoinPrepare either.
needsJoinPrepare = false;
// return false when onJoinPrepare is waiting for committing offset
if (!onJoinPrepare(generation.generationId, generation.memberId)) {
needsJoinPrepare = true;
// should not initiateJoinGroup if needsJoinPrepare still is true
return false;
}
}
final RequestFuture<ByteBuffer> future = initiateJoinGroup();
client.poll(future, timer);
if (!future.isDone()) {
// we ran out of time
return false;
}
if (future.succeeded()) {
Generation generationSnapshot;
MemberState stateSnapshot;
// See {@link PlaintextConsumerTest#testMaxPollIntervalMsDelayInAssignment}
synchronized (AbstractCoordinator.this) {
generationSnapshot = this.generation;
stateSnapshot = this.state;
}
if (!hasGenerationReset(generationSnapshot) && stateSnapshot == MemberState.STABLE) {
// Duplicate the buffer in case `onJoinComplete` does not complete and needs to be retried.
ByteBuffer memberAssignment = future.value().duplicate();
onJoinComplete(generationSnapshot.generationId, generationSnapshot.memberId, generationSnapshot.protocolName, memberAssignment);
// Generally speaking we should always resetJoinGroupFuture once the future is done, but here
// we can only reset the join group future after the completion callback returns. This ensures
// that if the callback is woken up, we will retry it on the next joinGroupIfNeeded.
// And because of that we should explicitly trigger resetJoinGroupFuture in other conditions below.
resetJoinGroupFuture();
needsJoinPrepare = true;
} else {
final String reason = String.format("rebalance failed since the generation/state was " + "modified by heartbeat thread to %s/%s before the rebalance callback triggered", generationSnapshot, stateSnapshot);
resetStateAndRejoin(reason, true);
resetJoinGroupFuture();
}
} else {
final RuntimeException exception = future.exception();
resetJoinGroupFuture();
synchronized (AbstractCoordinator.this) {
rejoinReason = String.format("rebalance failed due to '%s' (%s)", exception.getMessage(), exception.getClass().getSimpleName());
rejoinNeeded = true;
}
if (exception instanceof UnknownMemberIdException || exception instanceof IllegalGenerationException || exception instanceof RebalanceInProgressException || exception instanceof MemberIdRequiredException)
continue;
else if (!future.isRetriable())
throw exception;
timer.sleep(rebalanceConfig.retryBackoffMs);
}
}
return true;
}
Aggregations