Skip to content

Commit

Permalink
WIP add support for soft deadlines
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Laub committed Jan 30, 2025
1 parent 538707f commit 2a2d58f
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 6 deletions.
1 change: 1 addition & 0 deletions dialogue-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies {
implementation 'com.palantir.safe-logging:preconditions'
implementation 'com.palantir.safe-logging:safe-logging'
implementation 'com.palantir.tracing:tracing'
implementation 'com.palantir.deadlines:deadlines'
implementation 'io.dropwizard.metrics:metrics-core'
implementation 'com.palantir.safethreadlocalrandom:safe-thread-local-random'
implementation 'com.palantir.tritium:tritium-metrics'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* (c) Copyright 2025 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.dialogue.core;

import com.google.common.util.concurrent.ListenableFuture;
import com.palantir.deadlines.Deadlines;
import com.palantir.dialogue.Channel;
import com.palantir.dialogue.Endpoint;
import com.palantir.dialogue.Request;
import com.palantir.dialogue.Response;
import java.time.Duration;

final class DeadlineAdvertisementChannel implements Channel {

private final Channel delegate;
private final Duration readTimeout;

DeadlineAdvertisementChannel(Channel delegate, Duration readTimeout) {
this.delegate = delegate;
this.readTimeout = readTimeout;
}

@Override
public ListenableFuture<Response> execute(Endpoint endpoint, Request request) {
Request.Builder requestBuilder = Request.builder().from(request);
Deadlines.encodeToRequest(readTimeout, requestBuilder, RequestBuilderEncodingAdapter.INSTANCE);
return delegate.execute(endpoint, requestBuilder.build());
}

private enum RequestBuilderEncodingAdapter implements Deadlines.RequestEncodingAdapter<Request.Builder> {
INSTANCE;

@Override
public void setHeader(Request.Builder builder, String headerName, String headerValue) {
builder.putHeaderParams(headerName, headerValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ private static ImmutableList<LimitedChannel> createHostChannels(
channel = HostMetricsChannel.create(cf, channel, targetUri.uri());
channel =
new TraceEnrichingChannel(channel, DialogueTracing.tracingTags(cf, uriIndexForInstrumentation));
channel = new DeadlineAdvertisementChannel(
channel, cf.clientConf().readTimeout());

ChannelState channelState = state.get(targetUri);
Preconditions.checkNotNull(channelState, "no ChannelState exists for this TargetUri");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* (c) Copyright 2025 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.dialogue.core;

import static org.assertj.core.api.Assertions.assertThat;

import com.google.common.util.concurrent.Futures;
import com.palantir.deadlines.Deadlines;
import com.palantir.deadlines.Deadlines.RequestDecodingAdapter;
import com.palantir.deadlines.api.DeadlinesHttpHeaders;
import com.palantir.dialogue.Channel;
import com.palantir.dialogue.Request;
import com.palantir.dialogue.TestEndpoint;
import com.palantir.tracing.CloseableTracer;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;

class DeadlineAdvertisementChannelTest {

@Test
void adds_header_from_configured_read_timeout() {
try (CloseableTracer tracer = CloseableTracer.startSpan("test")) {
Duration readTimeout = Duration.ofSeconds(1);
List<Request> requests = new ArrayList<>();
Channel delegate = (_endpoint, request) -> {
requests.add(request);
return Futures.immediateCancelledFuture();
};
Channel channel = new DeadlineAdvertisementChannel(delegate, readTimeout);
assertThat(channel.execute(TestEndpoint.GET, Request.builder().build()))
.isCancelled();

assertThat(requests).singleElement().satisfies(request -> {
assertThat(request.headerParams().keySet())
.singleElement()
.isEqualTo(DeadlinesHttpHeaders.EXPECT_WITHIN);
assertThat(request.headerParams().get(DeadlinesHttpHeaders.EXPECT_WITHIN))
.singleElement()
.satisfies(value -> {
double nSeconds = Double.parseDouble(value);
Duration parsed = Duration.ofNanos((long) (nSeconds * 1e9d));
assertThat(parsed).isEqualTo(readTimeout);
});
});
}
}

@Test
void adds_header_from_remaining_deadline() {
try (CloseableTracer tracer = CloseableTracer.startSpan("test")) {
Duration readTimeout = Duration.ofSeconds(10);
List<Request> requests = new ArrayList<>();
Channel delegate = (_endpoint, request) -> {
requests.add(request);
return Futures.immediateCancelledFuture();
};

// set deadline state for this trace to somethign less than configured read timeout
Request inboundRequest = Request.builder()
.putHeaderParams(DeadlinesHttpHeaders.EXPECT_WITHIN, "1")
.build();
Deadlines.parseFromRequest(Optional.empty(), inboundRequest, Decoder.INSTANCE);

Channel channel = new DeadlineAdvertisementChannel(delegate, readTimeout);
assertThat(channel.execute(TestEndpoint.GET, Request.builder().build()))
.isCancelled();

assertThat(requests).singleElement().satisfies(request -> {
assertThat(request.headerParams().keySet())
.singleElement()
.isEqualTo(DeadlinesHttpHeaders.EXPECT_WITHIN);
assertThat(request.headerParams().get(DeadlinesHttpHeaders.EXPECT_WITHIN))
.singleElement()
.satisfies(value -> {
double nSeconds = Double.parseDouble(value);
Duration parsed = Duration.ofNanos((long) (nSeconds * 1e9d));
Duration maxAllowed = Duration.ofSeconds(1);
assertThat(parsed).isLessThanOrEqualTo(maxAllowed);
});
});
}
}

private enum Decoder implements RequestDecodingAdapter<Request> {
INSTANCE;

@Override
public Optional<String> getFirstHeader(Request request, String headerName) {
return request.headerParams().get(headerName).stream().findFirst();
}
}
}
14 changes: 8 additions & 6 deletions versions.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2 (1 constraints: 83
com.github.ben-manes.caffeine:caffeine:3.2.0 (3 constraints: 08289433)
com.google.auto:auto-common:1.2.1 (1 constraints: 17120ffb)
com.google.code.findbugs:jsr305:3.0.2 (14 constraints: 49e26143)
com.google.errorprone:error_prone_annotations:2.7.1 (18 constraints: 8420df82)
com.google.errorprone:error_prone_annotations:2.7.1 (19 constraints: d82ef1c9)
com.google.guava:failureaccess:1.0.2 (1 constraints: 150ae2b4)
com.google.guava:guava:33.4.0-jre (18 constraints: 603f58d1)
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava (1 constraints: bd17c918)
Expand All @@ -25,6 +25,8 @@ com.palantir.conjure.java.runtime:client-config:8.17.0 (1 constraints: 4205683b)
com.palantir.conjure.java.runtime:conjure-java-jackson-optimizations:8.17.0 (1 constraints: 861cc9a4)
com.palantir.conjure.java.runtime:conjure-java-jackson-serialization:8.17.0 (2 constraints: 6216970f)
com.palantir.conjure.java.runtime:keystores:8.17.0 (2 constraints: 6319f0dd)
com.palantir.deadlines:deadlines:0.1.0 (1 constraints: 0305ee35)
com.palantir.deadlines:deadlines-api:0.1.0 (1 constraints: 1b0eba44)
com.palantir.goethe:goethe:0.14.0 (1 constraints: 37052f3b)
com.palantir.javapoet:javapoet:0.6.0 (2 constraints: c81064d1)
com.palantir.nylon:nylon-threads:0.4.0 (1 constraints: 0c10fa91)
Expand All @@ -33,20 +35,20 @@ com.palantir.ri:resource-identifier:2.8.0 (2 constraints: fc1495b7)
com.palantir.safe-logging:logger:3.7.0 (13 constraints: 99d0b80e)
com.palantir.safe-logging:logger-slf4j:3.7.0 (1 constraints: 050e6842)
com.palantir.safe-logging:logger-spi:3.7.0 (2 constraints: 191ea27b)
com.palantir.safe-logging:preconditions:3.7.0 (19 constraints: 4e351810)
com.palantir.safe-logging:preconditions:3.7.0 (20 constraints: 71439e48)
com.palantir.safe-logging:safe-logging:3.7.0 (18 constraints: da26b0dc)
com.palantir.safethreadlocalrandom:safe-thread-local-random:0.3.0 (1 constraints: 0505f435)
com.palantir.tokens:auth-tokens:3.18.0 (3 constraints: 2628868e)
com.palantir.tracing:tracing:6.20.0 (2 constraints: 5416db0d)
com.palantir.tracing:tracing:6.20.0 (3 constraints: a52412fa)
com.palantir.tracing:tracing-api:6.20.0 (2 constraints: 0912eb17)
com.palantir.tritium:tritium-api:0.96.0 (2 constraints: 3f1f48be)
com.palantir.tritium:tritium-caffeine:0.96.0 (1 constraints: 4105553b)
com.palantir.tritium:tritium-core:0.96.0 (1 constraints: 47105ea2)
com.palantir.tritium:tritium-ids:0.96.0 (1 constraints: d20fb696)
com.palantir.tritium:tritium-metrics:0.96.0 (2 constraints: c11598e4)
com.palantir.tritium:tritium-registry:0.96.0 (5 constraints: ab561c1f)
com.palantir.tritium:tritium-metrics:0.96.0 (3 constraints: 1924b5b6)
com.palantir.tritium:tritium-registry:0.96.0 (6 constraints: 03658780)
com.squareup:javapoet:1.13.0 (1 constraints: f50b65f7)
io.dropwizard.metrics:metrics-core:4.2.30 (5 constraints: 5553f0f5)
io.dropwizard.metrics:metrics-core:4.2.30 (6 constraints: a76141a8)
javax.annotation:javax.annotation-api:1.3.2 (1 constraints: 0805fb35)
joda-time:joda-time:2.12.7 (1 constraints: 2f16b1f1)
org.apache.httpcomponents.client5:httpclient5:5.3.1 (1 constraints: 0b050e36)
Expand Down
1 change: 1 addition & 0 deletions versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ com.palantir.conjure.java.api:* = 2.58.0
com.palantir.conjure.java.runtime:* = 8.17.0
com.palantir.conjure.java:* = 8.41.0
com.palantir.conjure:conjure = 4.51.0
com.palantir.deadlines:* = 0.1.0
com.palantir.goethe:* = 0.14.0
com.palantir.javapoet:javapoet = 0.6.0
com.palantir.refreshable:* = 2.5.0
Expand Down

0 comments on commit 2a2d58f

Please sign in to comment.