public void enable() { synchronized (AbstractCoordinator.this) { log.debug("Enabling heartbeat thread"); this.enabled = true; heartbeat.resetTimeouts(); AbstractCoordinator.this.notify(); } }
/** * Check the status of the heartbeat thread (if it is active) and indicate the liveness * of the client. This must be called periodically after joining with {@link #ensureActiveGroup()} * to ensure that the member stays in the group. If an interval of time longer than the * provided rebalance timeout expires without calling this method, then the client will proactively * leave the group. * * @param now current time in milliseconds * @throws RuntimeException for unexpected errors raised from the heartbeat thread */ protected synchronized void pollHeartbeat(long now) { if (heartbeatThread != null) { if (heartbeatThread.hasFailed()) { // set the heartbeat thread to null and raise an exception. If the user catches it, // the next call to ensureActiveGroup() will spawn a new heartbeat thread. RuntimeException cause = heartbeatThread.failureCause(); heartbeatThread = null; throw cause; } // Awake the heartbeat thread if needed if (heartbeat.shouldHeartbeat(now)) { notify(); } heartbeat.poll(now); } }
protected synchronized long timeToNextHeartbeat(long now) { // if we have not joined the group, we don't need to send heartbeats if (state == MemberState.UNJOINED) return Long.MAX_VALUE; return heartbeat.timeToNextHeartbeat(now); }
@Override public void onFailure(RuntimeException e) { synchronized (AbstractCoordinator.this) { if (e instanceof RebalanceInProgressException) { // it is valid to continue heartbeating while the group is rebalancing. This // ensures that the coordinator keeps the member in the group for as long // as the duration of the rebalance timeout. If we stop sending heartbeats, // however, then the session timeout may expire before we can rejoin. heartbeat.receiveHeartbeat(); } else { heartbeat.failHeartbeat(); // wake up the thread if it's sleeping to reschedule the heartbeat AbstractCoordinator.this.notify(); } } } });
} else if (heartbeat.sessionTimeoutExpired(now)) { } else if (heartbeat.pollTimeoutExpired(now)) { "with max.poll.records."); maybeLeaveGroup(); } else if (!heartbeat.shouldHeartbeat(now)) { heartbeat.sentHeartbeat(now);
@Test public void testResetSession() { heartbeat.sentHeartbeat(time.milliseconds()); time.sleep(sessionTimeoutMs + 5); heartbeat.resetSessionTimeout(); assertFalse(heartbeat.sessionTimeoutExpired(time.milliseconds())); // Resetting the session timeout should not reset the poll timeout time.sleep(maxPollIntervalMs + 1); heartbeat.resetSessionTimeout(); assertTrue(heartbeat.pollTimeoutExpired(time.milliseconds())); }
@Test public void testResetTimeouts() { time.sleep(maxPollIntervalMs); assertTrue(heartbeat.sessionTimeoutExpired(time.milliseconds())); assertEquals(0, heartbeat.timeToNextHeartbeat(time.milliseconds())); assertTrue(heartbeat.pollTimeoutExpired(time.milliseconds())); heartbeat.resetTimeouts(); assertFalse(heartbeat.sessionTimeoutExpired(time.milliseconds())); assertEquals(heartbeatIntervalMs, heartbeat.timeToNextHeartbeat(time.milliseconds())); assertFalse(heartbeat.pollTimeoutExpired(time.milliseconds())); }
@Test public void testShouldHeartbeat() { heartbeat.sentHeartbeat(time.milliseconds()); time.sleep((long) ((float) heartbeatIntervalMs * 1.1)); assertTrue(heartbeat.shouldHeartbeat(time.milliseconds())); }
@Test public void testSessionTimeoutExpired() { heartbeat.sentHeartbeat(time.milliseconds()); time.sleep(sessionTimeoutMs + 5); assertTrue(heartbeat.sessionTimeoutExpired(time.milliseconds())); }
@Test public void testTimeToNextHeartbeat() { heartbeat.sentHeartbeat(time.milliseconds()); assertEquals(heartbeatIntervalMs, heartbeat.timeToNextHeartbeat(time.milliseconds())); time.sleep(heartbeatIntervalMs); assertEquals(0, heartbeat.timeToNextHeartbeat(time.milliseconds())); time.sleep(heartbeatIntervalMs); assertEquals(0, heartbeat.timeToNextHeartbeat(time.milliseconds())); }
private 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 }
@Test public void testUpdateLastHeartbeatPollWhenCoordinatorUnknown() throws Exception { // If we are part of an active group and we cannot find the coordinator, we should nevertheless // continue to update the last poll time so that we do not expire the consumer subscriptions.subscribe(singleton(topic1), rebalanceListener); client.prepareResponse(groupCoordinatorResponse(node, Errors.NONE)); coordinator.ensureCoordinatorReady(time.timer(Long.MAX_VALUE)); // Join the group, but signal a coordinator change after the first heartbeat client.prepareResponse(joinGroupFollowerResponse(1, "consumer", "leader", Errors.NONE)); client.prepareResponse(syncGroupResponse(singletonList(t1p), Errors.NONE)); client.prepareResponse(heartbeatResponse(Errors.NOT_COORDINATOR)); coordinator.poll(time.timer(Long.MAX_VALUE)); time.sleep(heartbeatIntervalMs); // Await the first heartbeat which forces us to find a new coordinator TestUtils.waitForCondition(() -> !client.hasPendingResponses(), "Failed to observe expected heartbeat from background thread"); assertTrue(coordinator.coordinatorUnknown()); assertFalse(coordinator.poll(time.timer(0))); assertEquals(time.milliseconds(), heartbeat.lastPollTime()); time.sleep(rebalanceTimeoutMs - 1); assertFalse(heartbeat.pollTimeoutExpired(time.milliseconds())); }
@Test public void testPollTimeout() { assertFalse(heartbeat.pollTimeoutExpired(time.milliseconds())); time.sleep(maxPollIntervalMs / 2); assertFalse(heartbeat.pollTimeoutExpired(time.milliseconds())); time.sleep(maxPollIntervalMs / 2 + 1); assertTrue(heartbeat.pollTimeoutExpired(time.milliseconds())); }
@Override public void onSuccess(ClientResponse resp, RequestFuture<Void> future) { log.debug("Received FindCoordinator response {}", resp); clearFindCoordinatorFuture(); FindCoordinatorResponse findCoordinatorResponse = (FindCoordinatorResponse) resp.responseBody(); Errors error = findCoordinatorResponse.error(); if (error == Errors.NONE) { synchronized (AbstractCoordinator.this) { // use MAX_VALUE - node.id as the coordinator id to allow separate connections // for the coordinator in the underlying network client layer int coordinatorConnectionId = Integer.MAX_VALUE - findCoordinatorResponse.node().id(); AbstractCoordinator.this.coordinator = new Node( coordinatorConnectionId, findCoordinatorResponse.node().host(), findCoordinatorResponse.node().port()); log.info("Discovered group coordinator {}", coordinator); client.tryConnect(coordinator); heartbeat.resetSessionTimeout(); } future.complete(null); } else if (error == Errors.GROUP_AUTHORIZATION_FAILED) { future.raise(new GroupAuthorizationException(groupId)); } else { log.debug("Group coordinator lookup failed: {}", error.message()); future.raise(error); } }
public double measure(MetricConfig config, long now) { return TimeUnit.SECONDS.convert(now - heartbeat.lastHeartbeatSend(), TimeUnit.MILLISECONDS); } };
@Test public void testShouldNotHeartbeat() { heartbeat.sentHeartbeat(time.milliseconds()); time.sleep(heartbeatIntervalMs / 2); assertFalse(heartbeat.shouldHeartbeat(time.milliseconds())); }
maxPollIntervalMs, sessionTimeoutMs, new Heartbeat(time, sessionTimeoutMs, heartbeatIntervalMs, maxPollIntervalMs, retryBackoffMs), assignors, this.metadata,
retryBackoffMs, requestTimeoutMs, heartbeatIntervalMs); Heartbeat heartbeat = new Heartbeat(time, sessionTimeoutMs, heartbeatIntervalMs, rebalanceTimeoutMs, retryBackoffMs); ConsumerCoordinator consumerCoordinator = new ConsumerCoordinator( loggerFactory,