com.yahoo.vespa.hosted.provision.autoscale.MetricsV2MetricsFetcher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of node-repository Show documentation
Show all versions of node-repository Show documentation
Keeps track of node assignment in a multi-application setup.
The newest version!
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.autoscale;
import ai.vespa.util.http.hc5.VespaAsyncHttpClientBuilder;
import com.yahoo.component.annotation.Inject;
import com.yahoo.component.AbstractComponent;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.orchestrator.HostNameNotFoundException;
import com.yahoo.vespa.orchestrator.Orchestrator;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.core5.concurrent.FutureCallback;
import java.io.IOException;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Fetches node metrics over the metrics/v2 API
*
* @author bratseth
*/
public class MetricsV2MetricsFetcher extends AbstractComponent implements MetricsFetcher {
private static final Logger log = Logger.getLogger(MetricsV2MetricsFetcher.class.getName());
private static final String apiPath = "/metrics/v2/values";
private final NodeRepository nodeRepository;
private final Orchestrator orchestrator;
private final AsyncHttpClient httpClient;
@Inject
@SuppressWarnings("unused")
public MetricsV2MetricsFetcher(NodeRepository nodeRepository, Orchestrator orchestrator) {
this(nodeRepository, orchestrator, new AsyncApacheHttpClient());
}
public MetricsV2MetricsFetcher(NodeRepository nodeRepository, Orchestrator orchestrator, AsyncHttpClient httpClient) {
this.nodeRepository = nodeRepository;
this.orchestrator = orchestrator;
this.httpClient = httpClient;
}
@Override
public CompletableFuture fetchMetrics(ApplicationId application) {
NodeList applicationNodes = nodeRepository.nodes().list().owner(application).state(Node.State.active);
Optional metricsV2Container = applicationNodes.container()
.matching(this::expectedUp)
.stream()
.filter(node -> ! newNode(node)) // Skip newly added nodes, as they may not be reachable
.findFirst();
if (metricsV2Container.isEmpty()) {
return CompletableFuture.completedFuture(MetricsResponse.empty());
}
else {
// Collector 'autoscaling' defined in com.yahoo.vespa.model.admin.monitoring.MetricConsumer
String url = "http://" + metricsV2Container.get().hostname() + ":" + 4080 + apiPath + "?consumer=autoscaling";
return httpClient.get(url)
.thenApply(response -> new MetricsResponse(response, applicationNodes));
}
}
/**
* Returns true if this a new node (oldest node history event is less than 3 minutes old)
*/
private boolean newNode(Node node) {
return node.history().age(nodeRepository.clock().instant()).compareTo(Duration.ofMinutes(3)) <= 0;
}
@Override
public void deconstruct() {
httpClient.close();
}
private boolean expectedUp(Node node) {
try {
return ! orchestrator.getNodeStatus(new HostName(node.hostname())).isSuspended();
}
catch (HostNameNotFoundException e) {
return false;
}
}
/** A simple async HTTP client */
public interface AsyncHttpClient {
CompletableFuture get(String url);
void close();
}
/** Implements the AsyncHttpClient interface by delegating to an Apache HTTP client */
public static class AsyncApacheHttpClient implements AsyncHttpClient {
private final CloseableHttpAsyncClient httpClient = VespaAsyncHttpClientBuilder.create().build();
public AsyncApacheHttpClient() {
httpClient.start();
}
@Override
public CompletableFuture get(String url) {
CompletableFuture callback = new CompletableFuture<>();
httpClient.execute(new SimpleHttpRequest("GET", url), new CallbackAdaptor(callback));
return callback;
}
@Override
public void close() {
try {
httpClient.close();
}
catch (IOException e) {
log.log(Level.WARNING, "Exception deconstructing", e);
}
}
private static class CallbackAdaptor implements FutureCallback {
private final CompletableFuture callback;
public CallbackAdaptor(CompletableFuture callback) {
this.callback = callback;
}
@Override
public void completed(SimpleHttpResponse simpleHttpResponse) {
callback.complete(simpleHttpResponse.getBodyText());
}
@Override
public void failed(Exception e) {
callback.completeExceptionally(e);
}
@Override
public void cancelled() {
callback.cancel(true);
}
}
}
}