Skip to content
Merged
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 @@ -231,14 +231,27 @@ protected abstract void onJoinComplete(int generation,
protected void onLeavePrepare() {}

/**
* Visible for testing.
*
* Ensure that the coordinator is ready to receive requests.
*
* @param timer Timer bounding how long this method can block
* @return true If coordinator discovery and initial connection succeeded, false otherwise
*/
protected synchronized boolean ensureCoordinatorReady(final Timer timer) {
return ensureCoordinatorReady(timer, false);
}

/**
* Ensure that the coordinator is ready to receive requests. This will return
* immediately without blocking. It is intended to be called in an asynchronous
* context when wakeups are not expected.
*
* @return true If coordinator discovery and initial connection succeeded, false otherwise
*/
protected synchronized boolean ensureCoordinatorReadyAsync() {
return ensureCoordinatorReady(time.timer(0), true);
}

private synchronized boolean ensureCoordinatorReady(final Timer timer, boolean disableWakeup) {
if (!coordinatorUnknown())
return true;

Expand All @@ -249,7 +262,7 @@ protected synchronized boolean ensureCoordinatorReady(final Timer timer) {
throw fatalException;
}
final RequestFuture<Void> future = lookupCoordinator();
client.poll(future, timer);
client.poll(future, timer, disableWakeup);

if (!future.isDone()) {
// ran out of time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/
package org.apache.kafka.clients.consumer.internals;

import java.time.Duration;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.kafka.clients.GroupRebalanceConfig;
Expand Down Expand Up @@ -489,10 +488,14 @@ void maybeUpdateSubscriptionMetadata() {
}
}

private boolean coordinatorUnknownAndUnready(Timer timer) {
private boolean coordinatorUnknownAndUnreadySync(Timer timer) {
return coordinatorUnknown() && !ensureCoordinatorReady(timer);
}

private boolean coordinatorUnknownAndUnreadyAsync() {
return coordinatorUnknown() && !ensureCoordinatorReadyAsync();
}

/**
* Poll for coordinator events. This ensures that the coordinator is known and that the consumer
* has joined the group (if it is using group management). This also handles periodic offset commits
Expand All @@ -518,7 +521,7 @@ public boolean poll(Timer timer, boolean waitForJoinGroup) {
// Always update the heartbeat last poll time so that the heartbeat thread does not leave the
// group proactively due to application inactivity even if (say) the coordinator cannot be found.
pollHeartbeat(timer.currentTimeMs());
if (coordinatorUnknownAndUnready(timer)) {
if (coordinatorUnknownAndUnreadySync(timer)) {
return false;
}

Expand Down Expand Up @@ -1052,7 +1055,7 @@ public RequestFuture<Void> commitOffsetsAsync(final Map<TopicPartition, OffsetAn
if (offsets.isEmpty()) {
// No need to check coordinator if offsets is empty since commit of empty offsets is completed locally.
future = doCommitOffsetsAsync(offsets, callback);
} else if (!coordinatorUnknownAndUnready(time.timer(Duration.ZERO))) {
} else if (!coordinatorUnknownAndUnreadyAsync()) {
// we need to make sure coordinator is ready before committing, since
// this is for async committing we do not try to block, but just try once to
// clear the previous discover-coordinator future, resend, or get responses;
Expand Down Expand Up @@ -1141,7 +1144,7 @@ public boolean commitOffsetsSync(Map<TopicPartition, OffsetAndMetadata> offsets,
return true;

do {
if (coordinatorUnknownAndUnready(timer)) {
if (coordinatorUnknownAndUnreadySync(timer)) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,23 @@ public void poll(RequestFuture<?> future) {
* @throws InterruptException if the calling thread is interrupted
*/
public boolean poll(RequestFuture<?> future, Timer timer) {
return poll(future, timer, false);
}

/**
* Block until the provided request future request has finished or the timeout has expired.
*
* @param future The request future to wait for
* @param timer Timer bounding how long this method can block
* @param disableWakeup true if we should not check for wakeups, false otherwise
*
* @return true if the future is done, false otherwise
* @throws WakeupException if {@link #wakeup()} is called from another thread and `disableWakeup` is false
* @throws InterruptException if the calling thread is interrupted
*/
public boolean poll(RequestFuture<?> future, Timer timer, boolean disableWakeup) {
do {
poll(timer, future);
poll(timer, future, disableWakeup);
} while (!future.isDone() && timer.notExpired());
return future.isDone();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,21 @@ public void testCoordinatorDiscoveryBackoff() {
assertTrue(endTime - initialTime >= RETRY_BACKOFF_MS);
}

@Test
public void testWakeupFromEnsureCoordinatorReady() {
setupCoordinator();

consumerClient.wakeup();

// No wakeup should occur from the async variation.
coordinator.ensureCoordinatorReadyAsync();

// But should wakeup in sync variation even if timer is 0.
assertThrows(WakeupException.class, () -> {
coordinator.ensureCoordinatorReady(mockTime.timer(0));
});
}

@Test
public void testTimeoutAndRetryJoinGroupIfNeeded() throws Exception {
setupCoordinator();
Expand Down