/** * Send a request with the default timeout. See {@link #send(Node, AbstractRequest.Builder, int)}. */ public RequestFuture<ClientResponse> send(Node node, AbstractRequest.Builder<?> requestBuilder) { return send(node, requestBuilder, requestTimeoutMs); }
/** * Send Metadata Request to least loaded node in Kafka cluster asynchronously * @return A future that indicates result of sent metadata request */ private RequestFuture<ClientResponse> sendMetadataRequest(MetadataRequest.Builder request) { final Node node = client.leastLoadedNode(); if (node == null) return RequestFuture.noBrokersAvailable(); else return client.send(node, request); }
synchronized RequestFuture<Void> sendHeartbeatRequest() { log.debug("Sending Heartbeat request to coordinator {}", coordinator); HeartbeatRequest.Builder requestBuilder = new HeartbeatRequest.Builder(this.groupId, this.generation.generationId, this.generation.memberId); return client.send(coordinator, requestBuilder) .compose(new HeartbeatResponseHandler()); }
/** * Discover the current coordinator for the group. Sends a GroupMetadata request to * one of the brokers. The returned future should be polled to get the result of the request. * @return A request future which indicates the completion of the metadata request */ private RequestFuture<Void> sendFindCoordinatorRequest(Node node) { // initiate the group metadata request log.debug("Sending FindCoordinator request to broker {}", node); FindCoordinatorRequest.Builder requestBuilder = new FindCoordinatorRequest.Builder(FindCoordinatorRequest.CoordinatorType.GROUP, this.groupId); return client.send(node, requestBuilder) .compose(new FindCoordinatorResponseHandler()); }
/** * Send the ListOffsetRequest to a specific broker for the partitions and target timestamps. * * @param node The node to send the ListOffsetRequest to. * @param timestampsToSearch The mapping from partitions to the target timestamps. * @param requireTimestamp True if we require a timestamp in the response. * @return A response which can be polled to obtain the corresponding timestamps and offsets. */ private RequestFuture<ListOffsetResult> sendListOffsetRequest(final Node node, final Map<TopicPartition, ListOffsetRequest.PartitionData> timestampsToSearch, boolean requireTimestamp) { ListOffsetRequest.Builder builder = ListOffsetRequest.Builder .forConsumer(requireTimestamp, isolationLevel) .setTargetTimes(timestampsToSearch); log.debug("Sending ListOffsetRequest {} to broker {}", builder, node); return client.send(node, builder) .compose(new RequestFutureAdapter<ClientResponse, ListOffsetResult>() { @Override public void onSuccess(ClientResponse response, RequestFuture<ListOffsetResult> future) { ListOffsetResponse lor = (ListOffsetResponse) response.responseBody(); log.trace("Received ListOffsetResponse {} from broker {}", lor, node); handleListOffsetResponse(timestampsToSearch, lor, future); } }); }
/** * Fetch the committed offsets for a set of partitions. This is a non-blocking call. The * returned future can be polled to get the actual offsets returned from the broker. * * @param partitions The set of partitions to get offsets for. * @return A request future containing the committed offsets. */ private RequestFuture<Map<TopicPartition, OffsetAndMetadata>> sendOffsetFetchRequest(Set<TopicPartition> partitions) { Node coordinator = checkAndGetCoordinator(); if (coordinator == null) return RequestFuture.coordinatorNotAvailable(); log.debug("Fetching committed offsets for partitions: {}", partitions); // construct the request OffsetFetchRequest.Builder requestBuilder = new OffsetFetchRequest.Builder(this.groupId, new ArrayList<>(partitions)); // send the request with a callback return client.send(coordinator, requestBuilder) .compose(new OffsetFetchResponseHandler()); }
private RequestFuture<ByteBuffer> sendSyncGroupRequest(SyncGroupRequest.Builder requestBuilder) { if (coordinatorUnknown()) return RequestFuture.coordinatorNotAvailable(); return client.send(coordinator, requestBuilder) .compose(new SyncGroupResponseHandler()); }
/** * Join the group and return the assignment for the next generation. This function handles both * JoinGroup and SyncGroup, delegating to {@link #performAssignment(String, String, Map)} if * elected leader by the coordinator. * * NOTE: This is visible only for testing * * @return A request future which wraps the assignment returned from the group leader */ RequestFuture<ByteBuffer> sendJoinGroupRequest() { if (coordinatorUnknown()) return RequestFuture.coordinatorNotAvailable(); // send a join group request to the coordinator log.info("(Re-)joining group"); JoinGroupRequest.Builder requestBuilder = new JoinGroupRequest.Builder( groupId, this.sessionTimeoutMs, this.generation.memberId, protocolType(), metadata()).setRebalanceTimeout(this.rebalanceTimeoutMs); log.debug("Sending JoinGroup ({}) to coordinator {}", requestBuilder, this.coordinator); // Note that we override the request timeout using the rebalance timeout since that is the // maximum time that it may block on the coordinator. We add an extra 5 seconds for small delays. int joinGroupTimeoutMs = Math.max(rebalanceTimeoutMs, rebalanceTimeoutMs + 5000); return client.send(coordinator, requestBuilder, joinGroupTimeoutMs) .compose(new JoinGroupResponseHandler()); }
/** * Leave the current group and reset local generation/memberId. */ public synchronized void maybeLeaveGroup() { if (!coordinatorUnknown() && state != MemberState.UNJOINED && generation.isValid()) { // this is a minimal effort attempt to leave the group. we do not // attempt any resending if the request fails or times out. log.info("Sending LeaveGroup request to coordinator {}", coordinator); LeaveGroupRequest.Builder request = new LeaveGroupRequest.Builder(groupId, generation.memberId); client.send(coordinator, request) .compose(new LeaveGroupResponseHandler()); client.pollNoWakeup(); } resetGeneration(); }
return client.send(coordinator, builder) .compose(new OffsetCommitResponseHandler(offsets));
final RequestFuture<ClientResponse> future = consumerClient.send(node, heartbeat());
@Test public void testDisconnectWakesUpPoll() throws Exception { final RequestFuture<ClientResponse> future = consumerClient.send(node, heartbeat()); client.enableBlockingUntilWakeup(1); Thread t = new Thread() { @Override public void run() { consumerClient.poll(future); } }; t.start(); consumerClient.disconnectAsync(node); t.join(); assertTrue(future.failed()); assertTrue(future.exception() instanceof DisconnectException); }
@Test public void testCoordinatorUnknownInUnsentCallbacksAfterCoordinatorDead() throws Exception { // When the coordinator is marked dead, all unsent or in-flight requests are cancelled // with a disconnect error. This test case ensures that the corresponding callbacks see // the coordinator as unknown which prevents additional retries to the same coordinator. client.prepareResponse(groupCoordinatorResponse(node, Errors.NONE)); coordinator.ensureCoordinatorReady(time.timer(Long.MAX_VALUE)); final AtomicBoolean asyncCallbackInvoked = new AtomicBoolean(false); Map<TopicPartition, OffsetCommitRequest.PartitionData> offsets = singletonMap( new TopicPartition("foo", 0), new OffsetCommitRequest.PartitionData(13L, Optional.empty(), "")); consumerClient.send(coordinator.checkAndGetCoordinator(), new OffsetCommitRequest.Builder(groupId, offsets)) .compose(new RequestFutureAdapter<ClientResponse, Object>() { @Override public void onSuccess(ClientResponse value, RequestFuture<Object> future) {} @Override public void onFailure(RuntimeException e, RequestFuture<Object> future) { assertTrue("Unexpected exception type: " + e.getClass(), e instanceof DisconnectException); assertTrue(coordinator.coordinatorUnknown()); asyncCallbackInvoked.set(true); } }); coordinator.markCoordinatorUnknown(); consumerClient.pollNoWakeup(); assertTrue(asyncCallbackInvoked.get()); }
@Test public void multiSend() { client.prepareResponse(heartbeatResponse(Errors.NONE)); client.prepareResponse(heartbeatResponse(Errors.NONE)); RequestFuture<ClientResponse> future1 = consumerClient.send(node, heartbeat()); RequestFuture<ClientResponse> future2 = consumerClient.send(node, heartbeat()); assertEquals(2, consumerClient.pendingRequestCount()); assertEquals(2, consumerClient.pendingRequestCount(node)); consumerClient.awaitPendingRequests(node, time.timer(Long.MAX_VALUE)); assertTrue(future1.succeeded()); assertTrue(future2.succeeded()); }
@Test public void sendWithinBlackoutPeriodAfterAuthenticationFailure() throws InterruptedException { client.authenticationFailed(node, 300); client.prepareResponse(heartbeatResponse(Errors.NONE)); final RequestFuture<ClientResponse> future = consumerClient.send(node, heartbeat()); consumerClient.poll(future); assertTrue(future.failed()); assertTrue("Expected only an authentication error.", future.exception() instanceof AuthenticationException); time.sleep(30); // wait less than the blackout period assertTrue(client.connectionFailed(node)); final RequestFuture<ClientResponse> future2 = consumerClient.send(node, heartbeat()); consumerClient.poll(future2); assertTrue(future2.failed()); assertTrue("Expected only an authentication error.", future2.exception() instanceof AuthenticationException); }
@Test public void wakeup() { RequestFuture<ClientResponse> future = consumerClient.send(node, heartbeat()); consumerClient.wakeup(); try { consumerClient.poll(time.timer(0)); fail(); } catch (WakeupException e) { } client.respond(heartbeatResponse(Errors.NONE)); consumerClient.poll(future); assertTrue(future.isDone()); }
@Test public void testDisconnectWithUnsentRequests() { RequestFuture<ClientResponse> future = consumerClient.send(node, heartbeat()); assertTrue(consumerClient.hasPendingRequests(node)); assertFalse(client.hasInFlightRequests(node.idString())); consumerClient.disconnectAsync(node); consumerClient.pollNoWakeup(); assertTrue(future.failed()); assertTrue(future.exception() instanceof DisconnectException); }
@Test public void testDisconnectWithInFlightRequests() { RequestFuture<ClientResponse> future = consumerClient.send(node, heartbeat()); consumerClient.pollNoWakeup(); assertTrue(consumerClient.hasPendingRequests(node)); assertTrue(client.hasInFlightRequests(node.idString())); consumerClient.disconnectAsync(node); consumerClient.pollNoWakeup(); assertTrue(future.failed()); assertTrue(future.exception() instanceof DisconnectException); }
@Test public void testTimeoutUnsentRequest() { // Delay connection to the node so that the request remains unsent client.delayReady(node, 1000); RequestFuture<ClientResponse> future = consumerClient.send(node, heartbeat(), 500); consumerClient.pollNoWakeup(); // Ensure the request is pending, but hasn't been sent assertTrue(consumerClient.hasPendingRequests()); assertFalse(client.hasInFlightRequests()); time.sleep(501); consumerClient.pollNoWakeup(); assertFalse(consumerClient.hasPendingRequests()); assertTrue(future.failed()); assertTrue(future.exception() instanceof TimeoutException); }
@Test public void send() { client.prepareResponse(heartbeatResponse(Errors.NONE)); RequestFuture<ClientResponse> future = consumerClient.send(node, heartbeat()); assertEquals(1, consumerClient.pendingRequestCount()); assertEquals(1, consumerClient.pendingRequestCount(node)); assertFalse(future.isDone()); consumerClient.poll(future); assertTrue(future.isDone()); assertTrue(future.succeeded()); ClientResponse clientResponse = future.value(); HeartbeatResponse response = (HeartbeatResponse) clientResponse.responseBody(); assertEquals(Errors.NONE, response.error()); }