Set short timeouts for api test to prevent startup delays

Since endpointAvailable is called on construct, this can happen in premain when running with the agent. I included a simple retry just in case…

Not sure in what case the trace agent would respond so slowly…
This commit is contained in:
Tyler Benson 2018-02-20 17:43:26 +10:00
parent 3b0ba32ef2
commit c1224eaa9f
2 changed files with 68 additions and 9 deletions

View File

@ -32,7 +32,7 @@ public class DDApi {
private final String tracesEndpoint; private final String tracesEndpoint;
private final String servicesEndpoint; private final String servicesEndpoint;
private final List<ResponseListener> responseListeners = new ArrayList<ResponseListener>(); private final List<ResponseListener> responseListeners = new ArrayList<>();
private final RateLimiter loggingRateLimiter = private final RateLimiter loggingRateLimiter =
RateLimiter.create(1.0 / SECONDS_BETWEEN_ERROR_LOG); 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)) { if (!responseListeners.contains(listener)) {
responseListeners.add(listener); responseListeners.add(listener);
} }
@ -137,12 +137,12 @@ public class DDApi {
if (null != responseString if (null != responseString
&& !"".equals(responseString.trim()) && !"".equals(responseString.trim())
&& !"OK".equalsIgnoreCase(responseString.trim())) { && !"OK".equalsIgnoreCase(responseString.trim())) {
JsonNode response = objectMapper.readTree(responseString); final JsonNode response = objectMapper.readTree(responseString);
for (ResponseListener listener : responseListeners) { for (final ResponseListener listener : responseListeners) {
listener.onResponse(endpoint, response); listener.onResponse(endpoint, response);
} }
} }
} catch (IOException e) { } catch (final IOException e) {
log.debug("failed to parse DD agent response: " + responseString, e); log.debug("failed to parse DD agent response: " + responseString, e);
} }
return true; return true;
@ -164,13 +164,25 @@ public class DDApi {
} }
private boolean endpointAvailable(final String endpoint) { private boolean endpointAvailable(final String endpoint) {
return endpointAvailable(endpoint, true);
}
private boolean endpointAvailable(final String endpoint, final boolean retry) {
try { try {
final HttpURLConnection httpCon = getHttpURLConnection(endpoint); 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.flush();
out.close(); out.close();
return httpCon.getResponseCode() == 200; return httpCon.getResponseCode() == 200;
} catch (IOException e) { } catch (final IOException e) {
if (retry) {
return endpointAvailable(endpoint, false);
}
} }
return false; return false;
} }

View File

@ -8,9 +8,11 @@ import datadog.trace.common.Service
import datadog.trace.common.writer.DDApi import datadog.trace.common.writer.DDApi
import datadog.trace.common.writer.DDApi.ResponseListener import datadog.trace.common.writer.DDApi.ResponseListener
import org.msgpack.jackson.dataformat.MessagePackFactory import org.msgpack.jackson.dataformat.MessagePackFactory
import ratpack.exec.Blocking
import ratpack.http.Headers import ratpack.http.Headers
import ratpack.http.MediaType import ratpack.http.MediaType
import spock.lang.Specification import spock.lang.Specification
import spock.lang.Unroll
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
@ -253,12 +255,11 @@ class DDApiTest extends Specification {
// response not seen because of non-200 status // response not seen because of non-200 status
agentResponse.get() == 'not-set' agentResponse.get() == 'not-set'
cleanup: cleanup:
agent.close() agent.close()
} }
def "Api Downgrades to v3"() { def "Api Downgrades to v3 if v0.4 not available"() {
setup: setup:
def v3Agent = ratpack { def v3Agent = ratpack {
handlers { handlers {
@ -280,6 +281,52 @@ class DDApiTest extends Specification {
v3Agent.close() 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<TreeMap<String, Object>> convertList(byte[] bytes) { static List<TreeMap<String, Object>> convertList(byte[] bytes) {
return mapper.readValue(bytes, new TypeReference<List<TreeMap<String, Object>>>() {}) return mapper.readValue(bytes, new TypeReference<List<TreeMap<String, Object>>>() {})
} }