Skip to content

Commit

Permalink
Interpret timeout zero value as no limit (#7023)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg authored Jan 17, 2025
1 parent 4eeef33 commit 31869a3
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public GrpcExporterBuilder<T> setChannel(ManagedChannel channel) {
}

public GrpcExporterBuilder<T> setTimeout(long timeout, TimeUnit unit) {
timeoutNanos = unit.toNanos(timeout);
timeoutNanos = timeout == 0 ? Long.MAX_VALUE : unit.toNanos(timeout);
return this;
}

Expand All @@ -96,7 +96,7 @@ public GrpcExporterBuilder<T> setTimeout(Duration timeout) {
}

public GrpcExporterBuilder<T> setConnectTimeout(long timeout, TimeUnit unit) {
connectTimeoutNanos = unit.toNanos(timeout);
connectTimeoutNanos = timeout == 0 ? Long.MAX_VALUE : unit.toNanos(timeout);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ public HttpExporterBuilder(String exporterName, String type, String defaultEndpo
}

public HttpExporterBuilder<T> setTimeout(long timeout, TimeUnit unit) {
timeoutNanos = unit.toNanos(timeout);
timeoutNanos = timeout == 0 ? Long.MAX_VALUE : unit.toNanos(timeout);
return this;
}

public HttpExporterBuilder<T> setConnectTimeout(long timeout, TimeUnit unit) {
connectTimeoutNanos = unit.toNanos(timeout);
connectTimeoutNanos = timeout == 0 ? Long.MAX_VALUE : unit.toNanos(timeout);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -815,13 +815,39 @@ void overrideHost() {
@Test
@SuppressWarnings("PreferJavaTimeOverload")
void validConfig() {
assertThatCode(() -> exporterBuilder().setTimeout(0, TimeUnit.MILLISECONDS))
// We must build exporters to test timeout settings, which intersect with underlying client
// implementations and may convert between Duration, int, and long, which may be susceptible to
// overflow exceptions.
assertThatCode(() -> buildAndShutdown(exporterBuilder().setTimeout(0, TimeUnit.MILLISECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setTimeout(Duration.ofMillis(0)))
assertThatCode(() -> buildAndShutdown(exporterBuilder().setTimeout(Duration.ofMillis(0))))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setTimeout(10, TimeUnit.MILLISECONDS))
assertThatCode(
() ->
buildAndShutdown(
exporterBuilder().setTimeout(Long.MAX_VALUE, TimeUnit.NANOSECONDS)))
.doesNotThrowAnyException();
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setTimeout(Duration.ofNanos(Long.MAX_VALUE))))
.doesNotThrowAnyException();
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setTimeout(Long.MAX_VALUE, TimeUnit.SECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> buildAndShutdown(exporterBuilder().setTimeout(10, TimeUnit.MILLISECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> buildAndShutdown(exporterBuilder().setTimeout(Duration.ofMillis(10))))
.doesNotThrowAnyException();
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setConnectTimeout(0, TimeUnit.MILLISECONDS)))
.doesNotThrowAnyException();
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setConnectTimeout(Duration.ofMillis(0))))
.doesNotThrowAnyException();
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setConnectTimeout(10, TimeUnit.MILLISECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setTimeout(Duration.ofMillis(10)))
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setConnectTimeout(Duration.ofMillis(10))))
.doesNotThrowAnyException();

assertThatCode(() -> exporterBuilder().setEndpoint("http://localhost:4317"))
Expand All @@ -846,6 +872,11 @@ void validConfig() {
.doesNotThrowAnyException();
}

private void buildAndShutdown(TelemetryExporterBuilder<T> builder) {
TelemetryExporter<T> build = builder.build();
build.shutdown().join(10, TimeUnit.MILLISECONDS);
}

@Test
@SuppressWarnings({"PreferJavaTimeOverload", "NullAway"})
void invalidConfig() {
Expand All @@ -858,6 +889,25 @@ void invalidConfig() {
assertThatThrownBy(() -> exporterBuilder().setTimeout(null))
.isInstanceOf(NullPointerException.class)
.hasMessage("timeout");
assertThatThrownBy(
() ->
buildAndShutdown(exporterBuilder().setTimeout(Duration.ofSeconds(Long.MAX_VALUE))))
.isInstanceOf(ArithmeticException.class);

assertThatThrownBy(() -> exporterBuilder().setConnectTimeout(-1, TimeUnit.MILLISECONDS))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("timeout must be non-negative");
assertThatThrownBy(() -> exporterBuilder().setConnectTimeout(1, null))
.isInstanceOf(NullPointerException.class)
.hasMessage("unit");
assertThatThrownBy(() -> exporterBuilder().setConnectTimeout(null))
.isInstanceOf(NullPointerException.class)
.hasMessage("timeout");
assertThatThrownBy(
() ->
buildAndShutdown(
exporterBuilder().setConnectTimeout(Duration.ofSeconds(Long.MAX_VALUE))))
.isInstanceOf(ArithmeticException.class);

assertThatThrownBy(() -> exporterBuilder().setEndpoint(null))
.isInstanceOf(NullPointerException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -698,22 +698,39 @@ void proxy() {
@Test
@SuppressWarnings("PreferJavaTimeOverload")
void validConfig() {
assertThatCode(() -> exporterBuilder().setTimeout(0, TimeUnit.MILLISECONDS))
// We must build exporters to test timeout settings, which intersect with underlying client
// implementations and may convert between Duration, int, and long, which may be susceptible to
// overflow exceptions.
assertThatCode(() -> buildAndShutdown(exporterBuilder().setTimeout(0, TimeUnit.MILLISECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setTimeout(Duration.ofMillis(0)))
assertThatCode(() -> buildAndShutdown(exporterBuilder().setTimeout(Duration.ofMillis(0))))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setTimeout(10, TimeUnit.MILLISECONDS))
assertThatCode(
() ->
buildAndShutdown(
exporterBuilder().setTimeout(Long.MAX_VALUE, TimeUnit.NANOSECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setTimeout(Duration.ofMillis(10)))
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setTimeout(Duration.ofNanos(Long.MAX_VALUE))))
.doesNotThrowAnyException();

assertThatCode(() -> exporterBuilder().setConnectTimeout(0, TimeUnit.MILLISECONDS))
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setTimeout(Long.MAX_VALUE, TimeUnit.SECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> buildAndShutdown(exporterBuilder().setTimeout(10, TimeUnit.MILLISECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> buildAndShutdown(exporterBuilder().setTimeout(Duration.ofMillis(10))))
.doesNotThrowAnyException();
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setConnectTimeout(0, TimeUnit.MILLISECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setConnectTimeout(Duration.ofMillis(0)))
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setConnectTimeout(Duration.ofMillis(0))))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setConnectTimeout(10, TimeUnit.MILLISECONDS))
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setConnectTimeout(10, TimeUnit.MILLISECONDS)))
.doesNotThrowAnyException();
assertThatCode(() -> exporterBuilder().setConnectTimeout(Duration.ofMillis(10)))
assertThatCode(
() -> buildAndShutdown(exporterBuilder().setConnectTimeout(Duration.ofMillis(10))))
.doesNotThrowAnyException();

assertThatCode(() -> exporterBuilder().setEndpoint("http://localhost:4318"))
Expand All @@ -738,6 +755,11 @@ void validConfig() {
.doesNotThrowAnyException();
}

private void buildAndShutdown(TelemetryExporterBuilder<T> builder) {
TelemetryExporter<T> build = builder.build();
build.shutdown().join(10, TimeUnit.MILLISECONDS);
}

@Test
@SuppressWarnings({"PreferJavaTimeOverload", "NullAway"})
void invalidConfig() {
Expand All @@ -750,6 +772,10 @@ void invalidConfig() {
assertThatThrownBy(() -> exporterBuilder().setTimeout(null))
.isInstanceOf(NullPointerException.class)
.hasMessage("timeout");
assertThatThrownBy(
() ->
buildAndShutdown(exporterBuilder().setTimeout(Duration.ofSeconds(Long.MAX_VALUE))))
.isInstanceOf(ArithmeticException.class);

assertThatThrownBy(() -> exporterBuilder().setConnectTimeout(-1, TimeUnit.MILLISECONDS))
.isInstanceOf(IllegalArgumentException.class)
Expand All @@ -760,6 +786,11 @@ void invalidConfig() {
assertThatThrownBy(() -> exporterBuilder().setConnectTimeout(null))
.isInstanceOf(NullPointerException.class)
.hasMessage("timeout");
assertThatThrownBy(
() ->
buildAndShutdown(
exporterBuilder().setConnectTimeout(Duration.ofSeconds(Long.MAX_VALUE))))
.isInstanceOf(ArithmeticException.class);

assertThatThrownBy(() -> exporterBuilder().setEndpoint(null))
.isInstanceOf(NullPointerException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,15 @@ public OkHttpGrpcSender(
@Nullable RetryPolicy retryPolicy,
@Nullable SSLContext sslContext,
@Nullable X509TrustManager trustManager) {
int callTimeoutMillis =
(int) Math.min(Duration.ofNanos(timeoutNanos).toMillis(), Integer.MAX_VALUE);
int connectTimeoutMillis =
(int) Math.min(Duration.ofNanos(connectTimeoutNanos).toMillis(), Integer.MAX_VALUE);
OkHttpClient.Builder clientBuilder =
new OkHttpClient.Builder()
.dispatcher(OkHttpUtil.newDispatcher())
.callTimeout(Duration.ofNanos(timeoutNanos))
.connectTimeout(Duration.ofNanos(connectTimeoutNanos));
.callTimeout(Duration.ofMillis(callTimeoutMillis))
.connectTimeout(Duration.ofMillis(connectTimeoutMillis));
if (retryPolicy != null) {
clientBuilder.addInterceptor(
new RetryInterceptor(retryPolicy, OkHttpGrpcSender::isRetryable));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,15 @@ public OkHttpHttpSender(
@Nullable RetryPolicy retryPolicy,
@Nullable SSLContext sslContext,
@Nullable X509TrustManager trustManager) {
int callTimeoutMillis =
(int) Math.min(Duration.ofNanos(timeoutNanos).toMillis(), Integer.MAX_VALUE);
int connectTimeoutMillis =
(int) Math.min(Duration.ofNanos(connectionTimeoutNanos).toMillis(), Integer.MAX_VALUE);
OkHttpClient.Builder builder =
new OkHttpClient.Builder()
.dispatcher(OkHttpUtil.newDispatcher())
.connectTimeout(Duration.ofNanos(connectionTimeoutNanos))
.callTimeout(Duration.ofNanos(timeoutNanos));
.connectTimeout(Duration.ofMillis(connectTimeoutMillis))
.callTimeout(Duration.ofMillis(callTimeoutMillis));

if (proxyOptions != null) {
builder.proxySelector(proxyOptions.getProxySelector());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public final class ZipkinSpanExporterBuilder {
// compression is enabled by default, because this is the default of OkHttpSender,
// which is created when no custom sender is set (see OkHttpSender.Builder)
private boolean compressionEnabled = true;
private long readTimeoutMillis = TimeUnit.SECONDS.toMillis(10);
private int readTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(10);
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;

/**
Expand Down Expand Up @@ -156,7 +156,8 @@ public ZipkinSpanExporterBuilder setCompression(String compressionMethod) {
public ZipkinSpanExporterBuilder setReadTimeout(long timeout, TimeUnit unit) {
requireNonNull(unit, "unit");
checkArgument(timeout >= 0, "timeout must be non-negative");
this.readTimeoutMillis = unit.toMillis(timeout);
long timeoutMillis = timeout == 0 ? Long.MAX_VALUE : unit.toMillis(timeout);
this.readTimeoutMillis = (int) Math.min(timeoutMillis, Integer.MAX_VALUE);
return this;
}

Expand Down Expand Up @@ -212,7 +213,7 @@ public ZipkinSpanExporter build() {
OkHttpSender.newBuilder()
.endpoint(endpoint)
.compressionEnabled(compressionEnabled)
.readTimeout((int) readTimeoutMillis)
.readTimeout(readTimeoutMillis)
.build();
}
OtelToZipkinSpanTransformer transformer =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,21 @@ void compressionEnabledAndDisabled() {
}
}

@Test
@SuppressWarnings("PreferJavaTimeOverload")
void readTimeout_Zero() {
ZipkinSpanExporter exporter =
ZipkinSpanExporter.builder().setReadTimeout(0, TimeUnit.SECONDS).build();

try {
assertThat(exporter)
.extracting("sender.delegate.client.readTimeoutMillis")
.isEqualTo(Integer.MAX_VALUE);
} finally {
exporter.shutdown();
}
}

@Test
void stringRepresentation() {
try (ZipkinSpanExporter exporter = ZipkinSpanExporter.builder().build()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ long getScheduleDelayNanos() {
public BatchLogRecordProcessorBuilder setExporterTimeout(long timeout, TimeUnit unit) {
requireNonNull(unit, "unit");
checkArgument(timeout >= 0, "timeout must be non-negative");
exporterTimeoutNanos = unit.toNanos(timeout);
exporterTimeoutNanos = timeout == 0 ? Long.MAX_VALUE : unit.toNanos(timeout);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ long getScheduleDelayNanos() {
public BatchSpanProcessorBuilder setExporterTimeout(long timeout, TimeUnit unit) {
requireNonNull(unit, "unit");
checkArgument(timeout >= 0, "timeout must be non-negative");
exporterTimeoutNanos = unit.toNanos(timeout);
exporterTimeoutNanos = timeout == 0 ? Long.MAX_VALUE : unit.toNanos(timeout);
return this;
}

Expand Down

0 comments on commit 31869a3

Please sign in to comment.