Remove APM Java Tracer dependency#234
Conversation
| } | ||
| } catch (final InterruptedException e) { | ||
| if (shutdown) { | ||
| return; |
There was a problem hiding this comment.
Why are you returning here? Wouldn't it be good for the caller to know that not all messages could be sent?
There was a problem hiding this comment.
I'd say this exception may happen when we try to shut down the executor, and we're trying to do a graceful shutdown (waiting 30 secs), so I think it should be fine here.
In any case, we could refactor the approach in the next PRs basing the logic on runnable tasks, or something similar.
|
|
||
|
|
||
| boolean isShutdown() { | ||
| return shutdown; |
There was a problem hiding this comment.
With the current code, isShutdown could return false while the sender is still actively sending messages. Not sure if that is a concern.
There was a problem hiding this comment.
I guess you mean the client is still actively sending messages. I think it should be fine for the moment because the shutdown only could happen when Jenkins is restarted, and all running pipelines must finish before the restart.
| public interface HttpClient { | ||
|
|
||
| void send(PayloadMessage obj); | ||
|
|
||
| void stop(); | ||
|
|
||
| void close(); | ||
| } |
There was a problem hiding this comment.
Is this plugin JRE8+ only? If so, you might want to consider using the async stuff that was added in Java8 (CompletableFuture & co). That would allow you to:
- use your executor's threads more efficiently through the use of non-blocking http calls
- not have to manage a manual queue of work (it would be managed by the executor)
- allow the callers to decide what to do if their request failed
There was a problem hiding this comment.
As we discussed offline, the best approach here had been to use the java.net.http.HttpClient to implement the HTTP calls asynchronously, but unfortunately, this class is only available since JDK11.
So this change would need an external library, which is something that could introduce issues in terms of dependency version clashes. To reduce the risk, we will use the approach based on java.net.HttpURLConnection for the moment.
| if(!spanBuffer.isEmpty()) { | ||
| final List<PayloadMessage> spanSendBuffer = new ArrayList<>(SIZE_SPANS_SEND_BUFFER); | ||
| for(int i = 0; i < spanBuffer.size(); i++) { | ||
| spanSendBuffer.add(spanBuffer.get(i)); | ||
|
|
||
| // Send every 100 spans or the last one. | ||
| if(spanSendBuffer.size() == SIZE_SPANS_SEND_BUFFER || i == (spanBuffer.size() - 1)) { | ||
| agentHttpClient.send(Collections.unmodifiableList(spanSendBuffer)); | ||
| spanSendBuffer.clear(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Avoid unbounded payloads
There was a problem hiding this comment.
Could this be an internal of the client?
There was a problem hiding this comment.
Yes, totally. 👌🏼
AdrianLC
left a comment
There was a problem hiding this comment.
I have some questions that I could not decide a line of code for:
-
What is the usage of DostatsgClient after this? Is it still called internally inside
DatadogAgentHttpClient? is it called separately for metrics? is it not called at all anymore? (This is separate from the tracer AFAIK) -
Could you share some info about test coverage for the client and flushing/buffering code? I've seen there are some tests but I'm not sure of what is really covered.
-
Could you comment a TL;DR for how the requests and flushing works? Is it a single separate thread doing blocking calls with timeout of a minute? What about retries, are we not retrying calls if there is a single 5XX error?
| tracePipelineLogic = new DatadogTracePipelineLogic(ddTracer); | ||
| this.stopAgentHttpClient(); | ||
| logger.info("Re/Initialize Datadog-Plugin Agent Http Client"); | ||
| final URL tracesURL = new URL("http://"+this.hostname+":"+this.traceCollectionPort+"/v0.3/traces"); |
There was a problem hiding this comment.
this seems important enough to have it's own function or a constant with a template string
| final Tracer ddTracer = tracerBuilder.build(); | ||
| traceBuildLogic = new DatadogTraceBuildLogic(ddTracer); | ||
| tracePipelineLogic = new DatadogTracePipelineLogic(ddTracer); | ||
| this.stopAgentHttpClient(); |
There was a problem hiding this comment.
Move this out of the exception block as you are catching all exceptions inside. As it is now you're calling it again in this block's catch so it looks like a bug in exception handling.
There was a problem hiding this comment.
Moved outside.
As it is now you're calling it again in this block's catch so it looks like a bug in exception handling.
Why a bug? I want to be sure that the agent client is stopped if an exception happened.
| private static final Logger logger = Logger.getLogger(DatadogTracePipelineLogic.class.getName()); | ||
|
|
||
| private final Tracer tracer; | ||
| private static final int SIZE_SPANS_SEND_BUFFER = 100; |
There was a problem hiding this comment.
How did you decide this number? It seems a bit too small.
There was a problem hiding this comment.
It was arbitrary. I don't think we're going to have those amounts of spans in a single pipeline, though.
I'm opened to suggestions, but probably a greater number will not affect so much.
| if(!spanBuffer.isEmpty()) { | ||
| final List<PayloadMessage> spanSendBuffer = new ArrayList<>(SIZE_SPANS_SEND_BUFFER); | ||
| for(int i = 0; i < spanBuffer.size(); i++) { | ||
| spanSendBuffer.add(spanBuffer.get(i)); | ||
|
|
||
| // Send every 100 spans or the last one. | ||
| if(spanSendBuffer.size() == SIZE_SPANS_SEND_BUFFER || i == (spanBuffer.size() - 1)) { | ||
| agentHttpClient.send(Collections.unmodifiableList(spanSendBuffer)); | ||
| spanSendBuffer.clear(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Could this be an internal of the client?
| public class IdGenerator { | ||
|
|
||
| public static long generate(){ | ||
| return ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); |
There was a problem hiding this comment.
is this copy paste from the tracer? will it be random enough?
There was a problem hiding this comment.
Yes, it's the same approach that is used in the Java Tracer: https://github.com/DataDog/dd-trace-java/blob/master/dd-trace-api/src/main/java/datadog/trace/api/IdGenerationStrategy.java#L10
| } | ||
|
|
||
| @Override | ||
| public void run() { |
There was a problem hiding this comment.
cold you add a comment here explaining how this works?
I'm guessing it polls the queue every second until it's empty or shutdown is triggered but I have no idea.
Why would this stop if the queue is empty? If we have a very long span that doesn't flush for a while will this be empty and trigger stop?
There was a problem hiding this comment.
It will stop only if shutdown=true and queue is empty. I refactored the conditions to be clearer.
| } | ||
| } | ||
|
|
||
| private void blockingSend(HttpMessage message) { |
There was a problem hiding this comment.
is this called only in the separate thread?
There was a problem hiding this comment.
yes, the HttpSender instance is executed in a separate threaad.
I have refactored the
Yes, I'll try to do this.
Yes,
|
Why? Was the DogStatsDClient separate from the APM requests before?
If we think the connectivity with the agent is going to work so well why would we need timeouts of 1min? This reads contradictory to me. I think it would be safer to have at least one retry and much lower timeouts (5 or 10 seconds). |
The class name was wrong in the past because we're using it as the class to create the clients for DogStatsD, Logs, and Traces clients for the Agent. So the correct name should be |
This method is used to create
Tracer is using 10 seconds of timeout and no retries, AFAIK. I'll follow the same strategy. |
Requirements for Contributing to this repository
What does this PR do?
This PR removes the dependency with the APM Java Tracer.
Additionally, it adds a mechanism to send the information asynchronously to the Datadog Agent via public HTTP endpoint using this spec https://docs.datadoghq.com/api/latest/tracing/
Description of the Change
Alternate Designs
Possible Drawbacks
Verification Process
Additional Notes
Release Notes
Review checklist (to be filled by reviewers)
changelog/label attached. If applicable it should have thebackward-incompatiblelabel attached.do-not-merge/label attached.kind/andseverity/labels attached at least.