Move all HTTP calls to a dedicated class and use Jetty HTTP client instead of raw HttpUrlConnection#346
Conversation
…stead of raw HttpUrlConnection
| <dependency> | ||
| <groupId>org.eclipse.jetty</groupId> | ||
| <artifactId>jetty-client</artifactId> | ||
| <version>9.4.51.v20230217</version> | ||
| </dependency> |
There was a problem hiding this comment.
Do we know if Jenkins uses jetty-client? If so, this might affect Jenkins core and we should use something under org.jenkins-ci.* groupId.
There was a problem hiding this comment.
Jenkins does not use Jetty client, and neither do any of the plugins that we're depending on, so we're good to go here.
| this.retryPolicyFactory = new HttpRetryPolicy.Factory(5, 100, 2.0); | ||
| } | ||
|
|
||
| public <T> T get(String url, Map<String, String> headers, Function<String, T> responseParser) throws ExecutionException, InterruptedException, TimeoutException { |
There was a problem hiding this comment.
Are we using get and post anywhere? Do we need them?
As far I could check, only asynchronous methods are used.
There was a problem hiding this comment.
we use the synchronous versions whenever we need to do something with the response: get is used to fetch the list of agent endpoints and post is used for connection validation (part of the validation is to check that the response is valid JSON)
|
|
||
| public static final class Factory { | ||
| private final int maxRetries; | ||
| private final long initialDelay; |
There was a problem hiding this comment.
Maybe just delay? It's confusing to find initial as it seems it's only going to be used once, but this is the actual delay for every backoff delay.
There was a problem hiding this comment.
The field in the policy class is mutable and is called delay, but the one in the factory class is really just the initial value
|
|
||
| public long backoff() { | ||
| long currentDelay = delay; | ||
| delay = (long) (delay * delayFactor * ThreadLocalRandom.current().nextDouble(0.85, 1.15)); |
There was a problem hiding this comment.
Could we encapsulate the jitter into something like nextJitter() or randomJitter() or something similar?
There was a problem hiding this comment.
done, moved to randomJitter()
Jenkins plugins are loaded using isolated classloaders, so including a third-party dependency in a plugin has no effect on Jenkins core. |
| try { | ||
| Throwable failure = result.getFailure(); | ||
| if (failure != null) { | ||
| if (retryPolicy.shouldRetry(null)) { |
There was a problem hiding this comment.
why don't we check if we should retry with the response of the result here?
There was a problem hiding this comment.
Having a failure usually means there is no response, i.e. we failed before the response could be received.
There was a problem hiding this comment.
Gotcha, thanks! Is there any downside in first getting the response, and then checking shouldRetry with it? That way if the response is null, the behavior hasn't changed, but if there is a response, we can use the additional logic in shouldRetry?
There was a problem hiding this comment.
Agree, that would allow to simplify the code a bit. Changed it accordingly
Requirements for Contributing to this repository
What does this PR do?
This PR does a few improvements to the way the plugin does HTTP calls, making them more efficient and limiting the number of concurrent requests.
The reason to do this is that there is a customer with a Jenkins instance that runs multiple large pipelines: when executed with the plugin enabled, the process runs out of available file descriptors because the requests are slow (since each one opens a new TCP socket) and because there is no limit on the number of simultaneous requests and no backpressure if the network layer cannot keep up.
Description of the Change
org.datadog.jenkins.plugins.datadog.clients.HttpClientwhich provides a layer of abstraction on top of HTTP-calling logicHttpUrlConnection, which allows to pool and reuse TCP sockets, limit the number of simultaneous requests and to make some of the request asynchronouslyAlternate Designs
Possible Drawbacks
Verification Process
Verified manually in Agentless/Evp-proxy/Agentful-APM modes.
Separate set of verifications is done with Jenkins using an HTTP proxy (to ensure the plugin respect Jenkins' proxy settings).
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.