diff --git a/dd-trace-ot/src/main/java/datadog/trace/common/writer/DDApi.java b/dd-trace-ot/src/main/java/datadog/trace/common/writer/DDApi.java index 815cb92c9b..8759d4efdb 100644 --- a/dd-trace-ot/src/main/java/datadog/trace/common/writer/DDApi.java +++ b/dd-trace-ot/src/main/java/datadog/trace/common/writer/DDApi.java @@ -32,7 +32,7 @@ public class DDApi { private final String tracesEndpoint; private final String servicesEndpoint; - private final List responseListeners = new ArrayList(); + private final List responseListeners = new ArrayList<>(); private final RateLimiter loggingRateLimiter = RateLimiter.create(1.0 / SECONDS_BETWEEN_ERROR_LOG); @@ -51,7 +51,7 @@ public class DDApi { } } - public void addResponseListener(ResponseListener listener) { + public void addResponseListener(final ResponseListener listener) { if (!responseListeners.contains(listener)) { responseListeners.add(listener); } @@ -137,12 +137,12 @@ public class DDApi { if (null != responseString && !"".equals(responseString.trim()) && !"OK".equalsIgnoreCase(responseString.trim())) { - JsonNode response = objectMapper.readTree(responseString); - for (ResponseListener listener : responseListeners) { + final JsonNode response = objectMapper.readTree(responseString); + for (final ResponseListener listener : responseListeners) { listener.onResponse(endpoint, response); } } - } catch (IOException e) { + } catch (final IOException e) { log.debug("failed to parse DD agent response: " + responseString, e); } return true; @@ -164,13 +164,25 @@ public class DDApi { } private boolean endpointAvailable(final String endpoint) { + return endpointAvailable(endpoint, true); + } + + private boolean endpointAvailable(final String endpoint, final boolean retry) { try { final HttpURLConnection httpCon = getHttpURLConnection(endpoint); - OutputStreamWriter out = new OutputStreamWriter(httpCon.getOutputStream()); + + // This is potentially called in premain, so we want to fail fast. + httpCon.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(1)); + httpCon.setReadTimeout((int) TimeUnit.SECONDS.toMillis(1)); + + final OutputStreamWriter out = new OutputStreamWriter(httpCon.getOutputStream()); out.flush(); out.close(); return httpCon.getResponseCode() == 200; - } catch (IOException e) { + } catch (final IOException e) { + if (retry) { + return endpointAvailable(endpoint, false); + } } return false; } diff --git a/dd-trace-ot/src/test/groovy/datadog/trace/api/writer/DDApiTest.groovy b/dd-trace-ot/src/test/groovy/datadog/trace/api/writer/DDApiTest.groovy index 58018afde9..c9eac3fc23 100644 --- a/dd-trace-ot/src/test/groovy/datadog/trace/api/writer/DDApiTest.groovy +++ b/dd-trace-ot/src/test/groovy/datadog/trace/api/writer/DDApiTest.groovy @@ -8,9 +8,11 @@ import datadog.trace.common.Service import datadog.trace.common.writer.DDApi import datadog.trace.common.writer.DDApi.ResponseListener import org.msgpack.jackson.dataformat.MessagePackFactory +import ratpack.exec.Blocking import ratpack.http.Headers import ratpack.http.MediaType import spock.lang.Specification +import spock.lang.Unroll import java.util.concurrent.atomic.AtomicReference @@ -253,12 +255,11 @@ class DDApiTest extends Specification { // response not seen because of non-200 status agentResponse.get() == 'not-set' - cleanup: agent.close() } - def "Api Downgrades to v3"() { + def "Api Downgrades to v3 if v0.4 not available"() { setup: def v3Agent = ratpack { handlers { @@ -280,6 +281,52 @@ class DDApiTest extends Specification { v3Agent.close() } + @Unroll + def "Api Downgrades to v3 if timeout exceeded (#delayTrace, #delayServices, #badPort)"() { + // This test is unfortunately only exercising the read timeout, not the connect timeout. + setup: + def agent = ratpack { + handlers { + put("v0.3/traces") { + response.status(200).send() + } + put("v0.3/services") { + response.status(200).send() + } + put("v0.4/traces") { + Blocking.exec { + Thread.sleep(delayTrace) + response.status(200).send() + } + } + put("v0.4/services") { + Blocking.exec { + Thread.sleep(delayServices) + response.status(200).send() + } + } + } + } + def port = badPort ? 999 : agent.address.port + def client = new DDApi("localhost", port) + + expect: + client.tracesEndpoint == "http://localhost:${port}/$endpointVersion/traces" + client.servicesEndpoint == "http://localhost:${port}/$endpointVersion/services" + + cleanup: + agent.close() + + where: + endpointVersion | delayTrace | delayServices | badPort + "v0.4" | 0 | 0 | false + "v0.3" | 0 | 0 | true + "v0.4" | 500 | 0 | false + "v0.4" | 0 | 500 | false + "v0.3" | 3000 | 0 | false + "v0.3" | 0 | 3000 | false + } + static List> convertList(byte[] bytes) { return mapper.readValue(bytes, new TypeReference>>() {}) }