Search in sources :

Example 86 with Response

use of com.palantir.dialogue.Response in project dialogue by palantir.

the class QueuedChannelTest method testQueuedResponseAvoidsExecutingCancelled.

@Test
public void testQueuedResponseAvoidsExecutingCancelled() {
    Request queued = Request.builder().putHeaderParams("key", "val").build();
    when(delegate.maybeExecute(endpoint, queued, DO_NOT_SKIP_LIMITS)).thenReturn(Optional.empty());
    ListenableFuture<Response> result = queuedChannel.maybeExecute(endpoint, queued).get();
    verify(delegate, times(2)).maybeExecute(endpoint, queued, DO_NOT_SKIP_LIMITS);
    assertThat(result.cancel(true)).isTrue();
    when(delegate.maybeExecute(endpoint, request, DO_NOT_SKIP_LIMITS)).thenReturn(Optional.of(Futures.immediateFuture(Mockito.mock(Response.class))));
    queuedChannel.maybeExecute(endpoint, request);
    verify(delegate, times(1)).maybeExecute(endpoint, request, DO_NOT_SKIP_LIMITS);
    // Should not have been invoked any more.
    verify(delegate, times(2)).maybeExecute(endpoint, queued, DO_NOT_SKIP_LIMITS);
}
Also used : Response(com.palantir.dialogue.Response) Request(com.palantir.dialogue.Request) Test(org.junit.jupiter.api.Test)

Example 87 with Response

use of com.palantir.dialogue.Response in project dialogue by palantir.

the class QueuedChannelTest method testQueuedRequestExecutedOnNextSubmission_throws.

@Test
public void testQueuedRequestExecutedOnNextSubmission_throws() throws ExecutionException, InterruptedException {
    // First request is limited by the channel and queued
    Request queuedRequest = Mockito.mock(Request.class);
    when(delegate.maybeExecute(endpoint, queuedRequest, DO_NOT_SKIP_LIMITS)).thenReturn(Optional.empty());
    ListenableFuture<Response> queuedFuture = queuedChannel.maybeExecute(endpoint, queuedRequest).get();
    verify(delegate, times(2)).maybeExecute(endpoint, queuedRequest, DO_NOT_SKIP_LIMITS);
    assertThat(queuedFuture).isNotDone();
    // Second request succeeds and the queued request is attempted, but throws an exception
    futureResponse.set(mockResponse);
    when(delegate.maybeExecute(endpoint, request, DO_NOT_SKIP_LIMITS)).thenReturn(maybeResponse);
    when(delegate.maybeExecute(endpoint, queuedRequest, DO_NOT_SKIP_LIMITS)).thenThrow(new NullPointerException("expected"));
    ListenableFuture<Response> completed = queuedChannel.maybeExecute(endpoint, request).get();
    // Both results should be completed. The thrown exception should
    // be converted into a failed future by NeverThrowLimitedChannel
    assertThat(completed).isDone();
    assertThat(queuedFuture).isDone();
    assertThat(completed.get()).isEqualTo(mockResponse);
    assertThatThrownBy(queuedFuture::get).hasRootCauseMessage("expected");
    verify(delegate, times(1)).maybeExecute(endpoint, request, DO_NOT_SKIP_LIMITS);
    verify(delegate, times(3)).maybeExecute(endpoint, queuedRequest, DO_NOT_SKIP_LIMITS);
}
Also used : Response(com.palantir.dialogue.Response) Request(com.palantir.dialogue.Request) Test(org.junit.jupiter.api.Test)

Example 88 with Response

use of com.palantir.dialogue.Response in project dialogue by palantir.

the class PinUntilErrorNodeSelectionStrategyChannel method maybeExecute.

@Override
public Optional<ListenableFuture<Response>> maybeExecute(Endpoint endpoint, Request request, LimitEnforcement limitEnforcement) {
    int pin = currentPin.get();
    PinChannel channel = nodeList.get(pin);
    // n.b. StickyAttachments.maybeExecute uses the delegate PinChannel, which bypasses the FutureCallback
    // instrumentation below on subsequent "sticky" requests.
    Optional<ListenableFuture<Response>> maybeResponse = StickyAttachments.maybeAddStickyToken(channel, endpoint, request, limitEnforcement);
    if (!maybeResponse.isPresent()) {
        return Optional.empty();
    }
    DialogueFutures.addDirectCallback(maybeResponse.get(), new FutureCallback<>() {

        @Override
        public void onSuccess(Response response) {
            // even if a couple of them get rate limited in the middle.
            if (Responses.isServerErrorRange(response) || (Responses.isQosStatus(response) && !Responses.isTooManyRequests(response))) {
                OptionalInt next = incrementHostIfNecessary(pin);
                instrumentation.receivedErrorStatus(pin, channel, response, next);
            } else {
                instrumentation.successfulResponse(channel.stableIndex());
            }
        }

        @Override
        public void onFailure(Throwable throwable) {
            OptionalInt next = incrementHostIfNecessary(pin);
            instrumentation.receivedThrowable(pin, channel, throwable, next);
        }
    });
    return maybeResponse;
}
Also used : Response(com.palantir.dialogue.Response) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) OptionalInt(java.util.OptionalInt) Endpoint(com.palantir.dialogue.Endpoint)

Example 89 with Response

use of com.palantir.dialogue.Response in project dialogue by palantir.

the class QueuedChannel method scheduleNextTask.

/**
 * Get the next call and attempt to execute it. If it is runnable, wire up the underlying future to the one
 * previously returned to the caller. If it is not runnable, add it back into the queue. Returns true if more
 * tasks may be able to be scheduled, and false otherwise.
 */
private boolean scheduleNextTask() {
    DeferredCall queueHead = queuedCalls.poll();
    if (queueHead == null) {
        return false;
    }
    SettableFuture<Response> queuedResponse = queueHead.response();
    // request will be quickly cancelled in that case.
    if (queuedResponse.isDone()) {
        decrementQueueSize();
        queueHead.span().complete();
        queueHead.timer().stop();
        return true;
    }
    try (CloseableSpan ignored = queueHead.span().attach()) {
        Endpoint endpoint = queueHead.endpoint();
        Optional<ListenableFuture<Response>> maybeResponse = delegate.maybeExecute(endpoint, queueHead.request(), DO_NOT_SKIP_LIMITS);
        if (maybeResponse.isPresent()) {
            decrementQueueSize();
            ListenableFuture<Response> response = maybeResponse.get();
            queueHead.span().complete();
            queueHead.timer().stop();
            DialogueFutures.addDirectCallback(response, new ForwardAndSchedule(queuedResponse));
            DialogueFutures.addDirectListener(queuedResponse, () -> {
                if (queuedResponse.isCancelled()) {
                    // Currently cancel(false) will be converted to cancel(true)
                    if (!response.cancel(true) && log.isDebugEnabled()) {
                        log.debug("Failed to cancel delegate response, it should be reported by ForwardAndSchedule " + "logging", SafeArg.of("channel", channelName), SafeArg.of("service", endpoint.serviceName()), SafeArg.of("endpoint", endpoint.endpointName()));
                    }
                }
            });
            return true;
        } else {
            if (!queuedCalls.offerFirst(queueHead)) {
                // Should never happen, ConcurrentLinkedDeque has no maximum size
                log.error("Failed to add an attempted call back to the deque", SafeArg.of("channel", channelName), SafeArg.of("service", endpoint.serviceName()), SafeArg.of("endpoint", endpoint.endpointName()));
                decrementQueueSize();
                queueHead.timer().stop();
                if (!queuedResponse.setException(new SafeRuntimeException("Failed to req-queue request", SafeArg.of("channel", channelName), SafeArg.of("service", endpoint.serviceName()), SafeArg.of("endpoint", endpoint.endpointName())))) {
                    if (log.isDebugEnabled()) {
                        log.debug("Queued response has already been completed", SafeArg.of("channel", channelName), SafeArg.of("service", endpoint.serviceName()), SafeArg.of("endpoint", endpoint.endpointName()));
                    }
                }
            }
            return false;
        }
    }
}
Also used : Response(com.palantir.dialogue.Response) Endpoint(com.palantir.dialogue.Endpoint) SafeRuntimeException(com.palantir.logsafe.exceptions.SafeRuntimeException) CloseableSpan(com.palantir.tracing.CloseableSpan) ListenableFuture(com.google.common.util.concurrent.ListenableFuture)

Example 90 with Response

use of com.palantir.dialogue.Response in project dialogue by palantir.

the class QueuedChannel method maybeExecute.

/**
 * Enqueues and tries to schedule as many queued tasks as possible.
 */
@VisibleForTesting
@SuppressWarnings("PreferJavaTimeOverload")
Optional<ListenableFuture<Response>> maybeExecute(Endpoint endpoint, Request request) {
    // Queuing adds contention between threads and should be avoided unless we need to shed load.
    if (queueSizeEstimate.get() <= 0) {
        Optional<ListenableFuture<Response>> maybeResult = delegate.maybeExecute(endpoint, request, DO_NOT_SKIP_LIMITS);
        if (maybeResult.isPresent()) {
            ListenableFuture<Response> result = maybeResult.get();
            DialogueFutures.addDirectListener(result, this::onCompletion);
            // While the queue was avoid, this is equivalent to spending zero time on the queue.
            if (shouldRecordQueueMetrics) {
                queuedTime.update(0, TimeUnit.NANOSECONDS);
            }
            return maybeResult;
        }
    }
    // maybeExecute may take sufficiently long that other requests could be queued.
    if (queueSizeEstimate.get() >= maxQueueSize) {
        return Optional.empty();
    }
    shouldRecordQueueMetrics = true;
    DeferredCall components = DeferredCall.builder().endpoint(endpoint).request(request).response(SettableFuture.create()).span(DetachedSpan.start("Dialogue-request-enqueued")).timer(queuedTime.time()).build();
    if (!queuedCalls.offer(components)) {
        // Should never happen, ConcurrentLinkedDeque has no maximum size
        return Optional.empty();
    }
    int newSize = incrementQueueSize();
    if (log.isDebugEnabled()) {
        log.debug("Request queued {} on channel {}", SafeArg.of("queueSize", newSize), SafeArg.of("channelName", channelName));
    }
    schedule();
    return Optional.of(components.response());
}
Also used : Response(com.palantir.dialogue.Response) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) Endpoint(com.palantir.dialogue.Endpoint) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Aggregations

Response (com.palantir.dialogue.Response)93 Test (org.junit.jupiter.api.Test)72 TestResponse (com.palantir.dialogue.TestResponse)56 EndpointChannel (com.palantir.dialogue.EndpointChannel)27 Channel (com.palantir.dialogue.Channel)24 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)22 Endpoint (com.palantir.dialogue.Endpoint)16 Request (com.palantir.dialogue.Request)15 ClientConfiguration (com.palantir.conjure.java.client.config.ClientConfiguration)11 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)10 TestEndpoint (com.palantir.dialogue.TestEndpoint)10 SafeRuntimeException (com.palantir.logsafe.exceptions.SafeRuntimeException)9 Duration (java.time.Duration)9 Meter (com.codahale.metrics.Meter)8 IOException (java.io.IOException)8 Optional (java.util.Optional)7 Assertions.assertThat (org.assertj.core.api.Assertions.assertThat)7 QosException (com.palantir.conjure.java.api.errors.QosException)5 Arrays (java.util.Arrays)5 Stream (java.util.stream.Stream)5