Skip to content

Commit 14c80ee

Browse files
committed
feat: Add readTimeout and conenctionTimeout as configurable parameters
Signed-off-by: huan.huynh <[email protected]>
1 parent be0b1cf commit 14c80ee

File tree

3 files changed

+120
-4
lines changed

3 files changed

+120
-4
lines changed

prometheus-metrics-exporter-pushgateway/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,11 @@
3939
<version>${project.version}</version>
4040
<scope>test</scope>
4141
</dependency>
42+
<dependency>
43+
<groupId>com.squareup.okhttp3</groupId>
44+
<artifactId>mockwebserver</artifactId>
45+
<version>5.3.0</version>
46+
<scope>test</scope>
47+
</dependency>
4248
</dependencies>
4349
</project>

prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.HashMap;
3333
import java.util.Map;
3434
import java.util.TreeMap;
35+
import java.util.Optional;
3536
import javax.annotation.Nullable;
3637

3738
/**
@@ -89,6 +90,8 @@ public class PushGateway {
8990
private final PrometheusRegistry registry;
9091
private final HttpConnectionFactory connectionFactory;
9192
private final EscapingScheme escapingScheme;
93+
private final Integer connectionTimeout;
94+
private final Integer readTimeout;
9295

9396
private PushGateway(
9497
PrometheusRegistry registry,
@@ -97,13 +100,17 @@ private PushGateway(
97100
HttpConnectionFactory connectionFactory,
98101
Map<String, String> requestHeaders,
99102
boolean prometheusTimestampsInMs,
100-
EscapingScheme escapingScheme) {
103+
EscapingScheme escapingScheme,
104+
@Nullable Integer connectionTimeout,
105+
@Nullable Integer readTimeout) {
101106
this.registry = registry;
102107
this.url = url;
103108
this.requestHeaders = Collections.unmodifiableMap(new HashMap<>(requestHeaders));
104109
this.connectionFactory = connectionFactory;
105110
this.prometheusTimestampsInMs = prometheusTimestampsInMs;
106111
this.escapingScheme = escapingScheme;
112+
this.connectionTimeout = Optional.ofNullable(connectionTimeout).orElse(10 * MILLISECONDS_PER_SECOND);
113+
this.readTimeout = Optional.ofNullable(readTimeout).orElse(10 * MILLISECONDS_PER_SECOND);
107114
writer = getWriter(format);
108115
if (!writer.isAvailable()) {
109116
throw new RuntimeException(writer.getClass() + " is not available");
@@ -206,8 +213,8 @@ private void doRequest(@Nullable PrometheusRegistry registry, String method) thr
206213
}
207214
connection.setRequestMethod(method);
208215

209-
connection.setConnectTimeout(10 * MILLISECONDS_PER_SECOND);
210-
connection.setReadTimeout(10 * MILLISECONDS_PER_SECOND);
216+
connection.setConnectTimeout(this.connectionTimeout);
217+
connection.setReadTimeout(this.readTimeout);
211218
connection.connect();
212219

213220
try {
@@ -277,6 +284,8 @@ public static class Builder {
277284
@Nullable private String address;
278285
@Nullable private Scheme scheme;
279286
@Nullable private String job;
287+
@Nullable private Integer connectionTimeout;
288+
@Nullable private Integer readTimeout;
280289
private boolean prometheusTimestampsInMs;
281290
private final Map<String, String> requestHeaders = new HashMap<>();
282291
private PrometheusRegistry registry = PrometheusRegistry.defaultRegistry;
@@ -395,6 +404,30 @@ public Builder prometheusTimestampsInMs(boolean prometheusTimestampsInMs) {
395404
return this;
396405
}
397406

407+
/**
408+
* Specify the connection timeout (in milliseconds) for HTTP connections to the PushGateway.
409+
* Default is {@code 10000} (10 seconds).
410+
*
411+
* @param connectionTimeout timeout value in milliseconds
412+
* @return this {@link Builder} instance
413+
*/
414+
public Builder connectionTimeout(Integer connectionTimeout) {
415+
this.connectionTimeout = connectionTimeout;
416+
return this;
417+
}
418+
419+
/**
420+
* Specify the read timeout (in milliseconds) for reading the response from the PushGateway.
421+
* Default is {@code 10000} (10 seconds).
422+
*
423+
* @param readTimeout timeout value in milliseconds
424+
* @return this {@link Builder} instance
425+
*/
426+
public Builder readTimeout(Integer readTimeout) {
427+
this.readTimeout = readTimeout;
428+
return this;
429+
}
430+
398431
private boolean getPrometheusTimestampsInMs() {
399432
// accept either to opt in to timestamps in milliseconds
400433
return config.getExporterProperties().getPrometheusTimestampsInMs()
@@ -496,7 +529,9 @@ public PushGateway build() {
496529
connectionFactory,
497530
requestHeaders,
498531
getPrometheusTimestampsInMs(),
499-
getEscapingScheme(properties));
532+
getEscapingScheme(properties),
533+
connectionTimeout,
534+
readTimeout);
500535
} catch (MalformedURLException e) {
501536
throw new PrometheusPropertiesException(
502537
address + ": Invalid address. Expecting <host>:<port>");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package io.prometheus.metrics.exporter.pushgateway;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import java.util.concurrent.TimeUnit;
6+
7+
import okhttp3.mockwebserver.MockResponse;
8+
import okhttp3.mockwebserver.MockWebServer;
9+
import org.junit.jupiter.api.AfterEach;
10+
import org.junit.jupiter.api.BeforeEach;
11+
import org.junit.jupiter.api.Test;
12+
13+
class PushGatewayTimeoutTest {
14+
15+
private MockWebServer mockWebServer;
16+
17+
@BeforeEach
18+
void setUp() throws Exception {
19+
mockWebServer = new MockWebServer();
20+
mockWebServer.start();
21+
}
22+
23+
@AfterEach
24+
void tearDown() throws Exception {
25+
mockWebServer.shutdown();
26+
}
27+
28+
@Test
29+
void connectionTimeoutIsEnforced() {
30+
// Simulate server that never accepts connection: by binding but delaying accept.
31+
// Since MockWebServer always accepts connection immediately, we instead use a short connect timeout and a response delay.
32+
int shortConnectTimeoutMillis = 10;
33+
int readTimeoutMillis = 1000;
34+
35+
PushGateway pushGateway = PushGateway.builder()
36+
.connectionTimeout(shortConnectTimeoutMillis)
37+
.readTimeout(readTimeoutMillis)
38+
.build();
39+
40+
// Enqueue a response that delays sending headers to simulate slow connection
41+
mockWebServer.enqueue(new MockResponse()
42+
.setBody("ok")
43+
.setBodyDelay(5, TimeUnit.SECONDS)); // very long delay
44+
45+
String url = mockWebServer.url("/").toString();
46+
47+
Exception thrown = assertThrows(Exception.class, pushGateway::pushAdd);
48+
49+
assertTrue(thrown.getMessage().contains("connect"), "Expected a connection‐timeout or connect error");
50+
}
51+
52+
@Test
53+
void readTimeoutIsEnforced() {
54+
int connectTimeoutMillis = 1000;
55+
int shortReadTimeoutMillis = 10;
56+
57+
PushGateway pushGateway = PushGateway.builder()
58+
.connectionTimeout(connectTimeoutMillis)
59+
.readTimeout(shortReadTimeoutMillis)
60+
.build();
61+
62+
// Enqueue a response that sends headers but delays body
63+
mockWebServer.enqueue(new MockResponse()
64+
.setHeadersDelay(0, TimeUnit.SECONDS)
65+
.setBodyDelay(5, TimeUnit.SECONDS)
66+
.setBody("ok"));
67+
68+
String url = mockWebServer.url("/").toString();
69+
70+
Exception thrown = assertThrows(Exception.class, pushGateway::pushAdd);
71+
72+
assertTrue(thrown.getMessage().contains("read") || thrown.getMessage().contains("timeout"),
73+
"Expected a read‐timeout error");
74+
}
75+
}

0 commit comments

Comments
 (0)